@intinyagroup/calendar 0.1.1-alpha.11 → 0.1.1-alpha.12
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/DatePicker.svelte
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as Popover from '@intinyagroup/ui';
|
|
3
3
|
import { Button, Input } from '@intinyagroup/ui';
|
|
4
|
-
import
|
|
4
|
+
import Calendar, { type CalendarDate } from './Calendar.svelte';
|
|
5
5
|
import { Calendar as CalendarIcon, X } from 'lucide-svelte';
|
|
6
|
-
import { cn } from '@intinyagroup/
|
|
6
|
+
import { cn } from '@intinyagroup/ui/utils';
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
9
|
value = $bindable(null),
|
|
@@ -186,14 +186,23 @@
|
|
|
186
186
|
</script>
|
|
187
187
|
|
|
188
188
|
<div class={cn('relative inline-block w-full max-w-sm', className)}>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
190
|
+
<div
|
|
191
|
+
role="button"
|
|
192
|
+
tabindex={disabled ? -1 : 0}
|
|
193
|
+
aria-disabled={disabled}
|
|
194
|
+
onclick={() => { if (!disabled) isOpen = !isOpen; }}
|
|
195
|
+
onkeydown={(e) => {
|
|
196
|
+
if (disabled) return;
|
|
197
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
isOpen = !isOpen;
|
|
200
|
+
}
|
|
201
|
+
}}
|
|
193
202
|
class={cn(
|
|
194
|
-
'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',
|
|
203
|
+
'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',
|
|
195
204
|
'hover:border-[var(--ui-border)] focus:outline-none focus:ring-2 focus:ring-[var(--ui-ring)]/20 focus:border-[var(--ui-primary)]',
|
|
196
|
-
disabled && 'opacity-50 cursor-not-allowed',
|
|
205
|
+
disabled && 'opacity-50 cursor-not-allowed pointer-events-none',
|
|
197
206
|
value.start ? 'text-[var(--ui-foreground)]' : 'text-[var(--ui-muted-foreground)]'
|
|
198
207
|
)}
|
|
199
208
|
>
|
|
@@ -205,14 +214,17 @@
|
|
|
205
214
|
{#if value.start && !disabled}
|
|
206
215
|
<button
|
|
207
216
|
type="button"
|
|
208
|
-
onclick={
|
|
217
|
+
onclick={(e) => {
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
clearRange();
|
|
220
|
+
}}
|
|
209
221
|
class="p-1 rounded-full hover:bg-[var(--ui-secondary)] text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)]"
|
|
210
222
|
aria-label="Clear date range"
|
|
211
223
|
>
|
|
212
224
|
<X class="size-3.5" />
|
|
213
225
|
</button>
|
|
214
226
|
{/if}
|
|
215
|
-
</
|
|
227
|
+
</div>
|
|
216
228
|
|
|
217
229
|
{#if isOpen}
|
|
218
230
|
<!-- Backdrop for click outside -->
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
onEventClick,
|
|
45
45
|
onDateClick,
|
|
46
46
|
onAddEvent,
|
|
47
|
+
onEventReschedule,
|
|
47
48
|
}: {
|
|
48
49
|
events?: CalendarEvent[];
|
|
49
50
|
view?: CalendarView;
|
|
@@ -56,8 +57,28 @@
|
|
|
56
57
|
onEventClick?: (event: CalendarEvent) => void;
|
|
57
58
|
onDateClick?: (date: Date) => void;
|
|
58
59
|
onAddEvent?: (newEvent: CalendarEvent) => void;
|
|
60
|
+
onEventReschedule?: (detail: { event: CalendarEvent; newStart: Date; newEnd: Date }) => void;
|
|
59
61
|
} = $props();
|
|
60
62
|
|
|
63
|
+
let draggedEventId = $state<string | null>(null);
|
|
64
|
+
|
|
65
|
+
function handleEventDrop(targetDate: Date, targetHour: number) {
|
|
66
|
+
if (!draggedEventId) return;
|
|
67
|
+
const ev = events.find((e) => e.id === draggedEventId);
|
|
68
|
+
if (!ev) return;
|
|
69
|
+
|
|
70
|
+
const origDurationMs = new Date(ev.end).getTime() - new Date(ev.start).getTime();
|
|
71
|
+
const newStart = new Date(targetDate);
|
|
72
|
+
newStart.setHours(targetHour, 0, 0, 0);
|
|
73
|
+
const newEnd = new Date(newStart.getTime() + origDurationMs);
|
|
74
|
+
|
|
75
|
+
events = events.map((e) =>
|
|
76
|
+
e.id === draggedEventId ? { ...e, start: newStart, end: newEnd } : e
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
onEventReschedule?.({ event: ev, newStart, newEnd });
|
|
80
|
+
draggedEventId = null;
|
|
81
|
+
}
|
|
61
82
|
const activeDateFnsLocale = $derived(locale.startsWith('id') ? localeId : localeEn);
|
|
62
83
|
const weekStartsOn = $derived(firstDayOfWeek as 0 | 1 | 2 | 3 | 4 | 5 | 6);
|
|
63
84
|
|
|
@@ -378,8 +399,17 @@
|
|
|
378
399
|
onclick={() => openCreateModal(d)}
|
|
379
400
|
class="col-span-1 divide-y divide-[var(--ui-border)]/50 relative hover:bg-[var(--ui-secondary)]/10 cursor-pointer"
|
|
380
401
|
>
|
|
381
|
-
{#each hours as
|
|
382
|
-
|
|
402
|
+
{#each hours as hour}
|
|
403
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
404
|
+
<div
|
|
405
|
+
class="h-14 transition-colors hover:bg-[var(--ui-primary)]/10"
|
|
406
|
+
ondragover={(e) => e.preventDefault()}
|
|
407
|
+
ondrop={(e) => {
|
|
408
|
+
e.preventDefault();
|
|
409
|
+
e.stopPropagation();
|
|
410
|
+
handleEventDrop(d, hour);
|
|
411
|
+
}}
|
|
412
|
+
></div>
|
|
383
413
|
{/each}
|
|
384
414
|
|
|
385
415
|
<!-- Google Calendar style Current Time Red Line -->
|
|
@@ -399,11 +429,16 @@
|
|
|
399
429
|
{@const topPos = (evTime.hours + evTime.minutes / 60) * 56}
|
|
400
430
|
<button
|
|
401
431
|
type="button"
|
|
432
|
+
draggable="true"
|
|
433
|
+
ondragstart={(e) => {
|
|
434
|
+
draggedEventId = ev.id;
|
|
435
|
+
e.dataTransfer?.setData('text/plain', ev.id);
|
|
436
|
+
}}
|
|
402
437
|
onclick={(e) => {
|
|
403
438
|
e.stopPropagation();
|
|
404
439
|
onEventClick?.(ev);
|
|
405
440
|
}}
|
|
406
|
-
class="absolute inset-x-1 rounded p-1 text-left text-[11px] text-white shadow-xs transition-opacity hover:opacity-90 overflow-hidden z-10"
|
|
441
|
+
class="absolute inset-x-1 rounded p-1 text-left text-[11px] text-white shadow-xs transition-opacity hover:opacity-90 overflow-hidden z-10 cursor-grab active:cursor-grabbing {draggedEventId === ev.id ? 'opacity-40 ring-2 ring-white' : ''}"
|
|
407
442
|
style="top: {topPos}px; height: 50px; background-color: {ev.color || 'var(--ui-primary)'};"
|
|
408
443
|
>
|
|
409
444
|
<div class="font-bold truncate">{ev.title}</div>
|
|
@@ -432,11 +467,6 @@
|
|
|
432
467
|
{/each}
|
|
433
468
|
</div>
|
|
434
469
|
|
|
435
|
-
{@const isCurrentDay = isToday(currentDate)}
|
|
436
|
-
{@const nowTime = getTimeInZone(new Date())}
|
|
437
|
-
{@const nowMinutes = nowTime.hours * 60 + nowTime.minutes}
|
|
438
|
-
{@const currentTimeTop = (nowMinutes / 60) * 64}
|
|
439
|
-
|
|
440
470
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
441
471
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
442
472
|
<div
|
|
@@ -448,7 +478,9 @@
|
|
|
448
478
|
{/each}
|
|
449
479
|
|
|
450
480
|
<!-- Google Calendar style Current Time Red Line -->
|
|
451
|
-
{#if
|
|
481
|
+
{#if isToday(currentDate)}
|
|
482
|
+
{@const nowTime = getTimeInZone(new Date())}
|
|
483
|
+
{@const currentTimeTop = ((nowTime.hours * 60 + nowTime.minutes) / 60) * 64}
|
|
452
484
|
<div
|
|
453
485
|
class="absolute inset-x-0 z-30 pointer-events-none flex items-center"
|
|
454
486
|
style="top: {currentTimeTop}px;"
|
|
@@ -500,28 +532,30 @@
|
|
|
500
532
|
|
|
501
533
|
<div class="space-y-3.5 text-xs">
|
|
502
534
|
<div>
|
|
503
|
-
<label class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Title</label>
|
|
535
|
+
<label for="event-cal-title" class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Title</label>
|
|
504
536
|
<input
|
|
537
|
+
id="event-cal-title"
|
|
505
538
|
type="text"
|
|
506
539
|
bind:value={newEventTitle}
|
|
507
540
|
placeholder="Event title (e.g. Sprint Review)..."
|
|
508
541
|
class="h-9 w-full rounded-lg border border-[var(--ui-input)] bg-[var(--ui-background)] px-3 text-xs outline-none focus:border-[var(--ui-primary)]"
|
|
509
|
-
autofocus
|
|
510
542
|
/>
|
|
511
543
|
</div>
|
|
512
544
|
|
|
513
545
|
<div class="grid grid-cols-2 gap-3">
|
|
514
546
|
<div>
|
|
515
|
-
<label class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Start Time</label>
|
|
547
|
+
<label for="event-cal-start-time" class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Start Time</label>
|
|
516
548
|
<input
|
|
549
|
+
id="event-cal-start-time"
|
|
517
550
|
type="time"
|
|
518
551
|
bind:value={newEventStartTime}
|
|
519
552
|
class="h-9 w-full rounded-lg border border-[var(--ui-input)] bg-[var(--ui-background)] px-2.5 text-xs outline-none focus:border-[var(--ui-primary)]"
|
|
520
553
|
/>
|
|
521
554
|
</div>
|
|
522
555
|
<div>
|
|
523
|
-
<label class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">End Time</label>
|
|
556
|
+
<label for="event-cal-end-time" class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">End Time</label>
|
|
524
557
|
<input
|
|
558
|
+
id="event-cal-end-time"
|
|
525
559
|
type="time"
|
|
526
560
|
bind:value={newEventEndTime}
|
|
527
561
|
class="h-9 w-full rounded-lg border border-[var(--ui-input)] bg-[var(--ui-background)] px-2.5 text-xs outline-none focus:border-[var(--ui-primary)]"
|
|
@@ -530,7 +564,7 @@
|
|
|
530
564
|
</div>
|
|
531
565
|
|
|
532
566
|
<div>
|
|
533
|
-
<
|
|
567
|
+
<span class="block mb-1.5 font-semibold text-[var(--ui-muted-foreground)]">Tag Color</span>
|
|
534
568
|
<div class="flex items-center gap-2">
|
|
535
569
|
{#each presetColors as col}
|
|
536
570
|
<button
|
|
@@ -548,11 +582,10 @@
|
|
|
548
582
|
</div>
|
|
549
583
|
|
|
550
584
|
<div>
|
|
551
|
-
<label class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Description (optional)</label>
|
|
585
|
+
<label for="event-cal-desc" class="block mb-1 font-semibold text-[var(--ui-muted-foreground)]">Description (optional)</label>
|
|
552
586
|
<textarea
|
|
587
|
+
id="event-cal-desc"
|
|
553
588
|
bind:value={newEventDescription}
|
|
554
|
-
rows={2}
|
|
555
|
-
placeholder="Notes, agenda, or link..."
|
|
556
589
|
class="w-full rounded-lg border border-[var(--ui-input)] bg-[var(--ui-background)] p-2.5 text-xs outline-none focus:border-[var(--ui-primary)]"
|
|
557
590
|
></textarea>
|
|
558
591
|
</div>
|
|
@@ -20,6 +20,11 @@ type $$ComponentProps = {
|
|
|
20
20
|
onEventClick?: (event: CalendarEvent) => void;
|
|
21
21
|
onDateClick?: (date: Date) => void;
|
|
22
22
|
onAddEvent?: (newEvent: CalendarEvent) => void;
|
|
23
|
+
onEventReschedule?: (detail: {
|
|
24
|
+
event: CalendarEvent;
|
|
25
|
+
newStart: Date;
|
|
26
|
+
newEnd: Date;
|
|
27
|
+
}) => void;
|
|
23
28
|
};
|
|
24
29
|
declare const EventCalendar: import("svelte").Component<$$ComponentProps, {}, "events" | "view" | "currentDate">;
|
|
25
30
|
type EventCalendar = ReturnType<typeof EventCalendar>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intinyagroup/calendar",
|
|
3
|
-
"version": "0.1.1-alpha.
|
|
3
|
+
"version": "0.1.1-alpha.12",
|
|
4
4
|
"description": "Calendar and DatePicker components for intinyagroup UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
"dev": "svelte-package -o dist -w"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@intinyagroup/ui": "0.1.1-alpha.
|
|
23
|
-
"date-fns": "^4.4.0"
|
|
22
|
+
"@intinyagroup/ui": "0.1.1-alpha.11",
|
|
23
|
+
"date-fns": "^4.4.0",
|
|
24
|
+
"lucide-svelte": "^1.0.1"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@sveltejs/package": "^2.5.8",
|