@intinyagroup/calendar 0.1.1-alpha.14 → 0.1.1-alpha.16

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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, Clock, Plus, X } from 'lucide-svelte';
2
+ import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, CalendarDays, Clock, Plus, X } from 'lucide-svelte';
3
3
  import { Button } from '@intinyagroup/ui';
4
4
  import { cn } from '@intinyagroup/ui/utils';
5
5
  import {
@@ -72,6 +72,23 @@
72
72
  let dragCreateEnd = $state<{ date: Date; hour: number; minute: number } | null>(null);
73
73
  let isDragCreating = $state(false);
74
74
  let dragCreateColumn = $state<Date | null>(null);
75
+ // Mobile detection
76
+ let isMobile = $state(typeof window !== 'undefined' ? window.matchMedia('(max-width: 767px)').matches : false);
77
+ $effect(() => {
78
+ if (typeof window === 'undefined') return;
79
+ const mq = window.matchMedia('(max-width: 767px)');
80
+ isMobile = mq.matches;
81
+ const handler = (e: MediaQueryListEvent) => { isMobile = e.matches; };
82
+ mq.addEventListener('change', handler);
83
+ return () => mq.removeEventListener('change', handler);
84
+ });
85
+
86
+ // Mobile week: show only current day ± 1
87
+ const mobileWeekDays = $derived([
88
+ subDays(currentDate, 1),
89
+ currentDate,
90
+ addDays(currentDate, 1)
91
+ ]);
75
92
 
76
93
  function isMultiDayOrAllDay(ev: CalendarEvent): boolean {
77
94
  if (ev.allDay) return true;
@@ -390,8 +407,9 @@
390
407
  return format(currentDate, 'MMMM yyyy', { locale: activeDateFnsLocale });
391
408
  }
392
409
  if (view === 'week') {
393
- const start = weekDays[0];
394
- const end = weekDays[6];
410
+ const displayDays = isMobile ? mobileWeekDays : weekDays;
411
+ const start = displayDays[0];
412
+ const end = displayDays[displayDays.length - 1];
395
413
  const sStr = format(start, 'dd MMM', { locale: activeDateFnsLocale });
396
414
  const eStr = format(end, 'dd MMM yyyy', { locale: activeDateFnsLocale });
397
415
  return `${sStr} - ${eStr}`;
@@ -450,10 +468,10 @@
450
468
 
451
469
  <div class={cn('relative flex flex-col rounded-xl border border-[var(--ui-border)] bg-[var(--ui-card)] shadow-xs overflow-hidden', className)}>
452
470
  <!-- Toolbar Header -->
453
- <div class="flex flex-wrap items-center justify-between gap-3 border-b border-[var(--ui-border)] p-4 sm:px-6">
454
- <div class="flex items-center gap-2">
471
+ <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-[var(--ui-border)] p-4 sm:px-6">
472
+ <div class="flex flex-col sm:flex-row sm:items-center gap-2">
455
473
  <h2 class="text-base font-bold text-[var(--ui-foreground)] capitalize">{titleHeader}</h2>
456
- <div class="flex items-center gap-1 ml-2">
474
+ <div class="flex items-center gap-1">
457
475
  <Button variant="outline" size="sm" class="size-8 p-0" onclick={goPrev}>
458
476
  <ChevronLeft class="size-4" />
459
477
  </Button>
@@ -464,7 +482,7 @@
464
482
  <ChevronRight class="size-4" />
465
483
  </Button>
466
484
  </div>
467
- <span class="inline-flex items-center rounded-md bg-[var(--ui-secondary)]/50 px-2 py-0.5 text-[10px] font-medium text-[var(--ui-muted-foreground)]">
485
+ <span class="hidden sm:inline-flex items-center rounded-md bg-[var(--ui-secondary)]/50 px-2 py-0.5 text-[10px] font-medium text-[var(--ui-muted-foreground)]">
468
486
  {activeTimeZone}
469
487
  </span>
470
488
  </div>
@@ -476,37 +494,40 @@
476
494
  type="button"
477
495
  onclick={() => (view = 'month')}
478
496
  class={cn(
479
- 'rounded-md px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
497
+ 'rounded-md px-2 sm:px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
480
498
  view === 'month' ? 'bg-[var(--ui-card)] text-[var(--ui-foreground)] shadow-xs' : 'text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)]'
481
499
  )}
482
500
  >
483
- Month
501
+ <CalendarDays class="size-4 sm:hidden" />
502
+ <span class="hidden sm:inline">Month</span>
484
503
  </button>
485
504
  <button
486
505
  type="button"
487
506
  onclick={() => (view = 'week')}
488
507
  class={cn(
489
- 'rounded-md px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
508
+ 'rounded-md px-2 sm:px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
490
509
  view === 'week' ? 'bg-[var(--ui-card)] text-[var(--ui-foreground)] shadow-xs' : 'text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)]'
491
510
  )}
492
511
  >
493
- Week
512
+ <CalendarIcon class="size-4 sm:hidden" />
513
+ <span class="hidden sm:inline">Week</span>
494
514
  </button>
495
515
  <button
496
516
  type="button"
497
517
  onclick={() => (view = 'day')}
498
518
  class={cn(
499
- 'rounded-md px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
519
+ 'rounded-md px-2 sm:px-3 py-1 text-xs font-semibold transition-colors cursor-pointer',
500
520
  view === 'day' ? 'bg-[var(--ui-card)] text-[var(--ui-foreground)] shadow-xs' : 'text-[var(--ui-muted-foreground)] hover:text-[var(--ui-foreground)]'
501
521
  )}
502
522
  >
503
- Day
523
+ <Clock class="size-4 sm:hidden" />
524
+ <span class="hidden sm:inline">Day</span>
504
525
  </button>
505
526
  </div>
506
527
 
507
528
  {#if enableEventModal}
508
529
  <Button size="sm" class="gap-1.5 h-8 text-xs" onclick={() => openCreateModal(currentDate)}>
509
- <Plus class="size-3.5" /> Add Event
530
+ <Plus class="size-3.5" /> <span class="hidden sm:inline">Add Event</span>
510
531
  </Button>
511
532
  {/if}
512
533
  </div>
@@ -540,7 +561,7 @@
540
561
  handleEventDrop(date);
541
562
  }}
542
563
  class={cn(
543
- 'min-h-[110px] p-1.5 flex flex-col gap-1 transition-colors hover:bg-[var(--ui-secondary)]/25 cursor-pointer select-none',
564
+ 'min-h-[80px] md:min-h-[110px] p-1 md:p-1.5 flex flex-col gap-1 transition-colors hover:bg-[var(--ui-secondary)]/25 cursor-pointer select-none',
544
565
  !currentMonth && 'bg-[var(--ui-muted)]/15 opacity-50'
545
566
  )}
546
567
  >
@@ -562,7 +583,7 @@
562
583
 
563
584
  <!-- Event pills with spanning indicator -->
564
585
  <div class="flex flex-col gap-1 overflow-hidden">
565
- {#each dayEvents.slice(0, 3) as ev (ev.id)}
586
+ {#each dayEvents.slice(0, isMobile ? 2 : 3) as ev (ev.id)}
566
587
  {@const isSpanning = isMultiDayOrAllDay(ev)}
567
588
  {@const isStartDay = isSameDay(new Date(ev.start), date)}
568
589
  {@const isEndDay = isSameDay(new Date(ev.end), date)}
@@ -580,7 +601,7 @@
580
601
  onEventClick?.(ev);
581
602
  }}
582
603
  class={cn(
583
- 'truncate px-1.5 py-0.5 text-left text-[11px] font-medium transition-opacity hover:opacity-85 text-white shadow-2xs cursor-grab active:cursor-grabbing',
604
+ 'truncate px-1.5 py-0.5 text-left text-[10px] md:text-[11px] font-medium transition-opacity hover:opacity-85 text-white shadow-2xs cursor-grab active:cursor-grabbing',
584
605
  isSpanning ? 'rounded-none' : 'rounded',
585
606
  isSpanning && isStartDay && 'rounded-l-md',
586
607
  isSpanning && isEndDay && 'rounded-r-md',
@@ -596,9 +617,9 @@
596
617
  </button>
597
618
  {/each}
598
619
 
599
- {#if dayEvents.length > 3}
620
+ {#if dayEvents.length > (isMobile ? 2 : 3)}
600
621
  <span class="text-[10px] font-semibold text-[var(--ui-muted-foreground)] px-1">
601
- +{dayEvents.length - 3} more
622
+ +{dayEvents.length - (isMobile ? 2 : 3)} more
602
623
  </span>
603
624
  {/if}
604
625
  </div>
@@ -607,11 +628,11 @@
607
628
  </div>
608
629
  {:else if view === 'week'}
609
630
  <!-- WEEK VIEW -->
610
- <div class="grid grid-cols-8 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/20 text-center text-xs font-semibold text-[var(--ui-muted-foreground)] py-2">
631
+ <div class="grid grid-cols-4 md:grid-cols-8 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/20 text-center text-xs font-semibold text-[var(--ui-muted-foreground)] py-2">
611
632
  <div class="col-span-1">Time</div>
612
- {#each weekDays as d, idx}
633
+ {#each (isMobile ? mobileWeekDays : weekDays) as d}
613
634
  <div class="col-span-1 flex flex-col items-center">
614
- <span class="capitalize">{dayNames[idx]}</span>
635
+ <span class="capitalize">{format(d, 'EEE', { locale: activeDateFnsLocale })}</span>
615
636
  <span class={cn('size-6 flex items-center justify-center rounded-full text-xs mt-0.5', isToday(d) ? 'bg-[var(--ui-primary)] text-[var(--ui-primary-foreground)]' : '')}>
616
637
  {d.getDate()}
617
638
  </span>
@@ -620,13 +641,13 @@
620
641
  </div>
621
642
 
622
643
  <!-- All-day header slot (FullCalendar 1:1) -->
623
- {@const allDayEvents = getAllDayEventsForWeek(weekDays)}
644
+ {@const allDayEvents = getAllDayEventsForWeek(isMobile ? mobileWeekDays : weekDays)}
624
645
  {#if allDayEvents.length > 0}
625
- <div class="grid grid-cols-8 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/10 text-xs divide-x divide-[var(--ui-border)] py-1.5">
646
+ <div class="grid grid-cols-4 md:grid-cols-8 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/10 text-xs divide-x divide-[var(--ui-border)] py-1.5">
626
647
  <div class="col-span-1 text-right pr-2 text-[10px] uppercase tracking-wider font-semibold text-[var(--ui-muted-foreground)] flex items-center justify-end">
627
648
  all-day
628
649
  </div>
629
- <div class="col-span-7 px-2 flex flex-col gap-1">
650
+ <div class="col-span-3 md:col-span-7 px-2 flex flex-col gap-1">
630
651
  {#each allDayEvents as ev (ev.id)}
631
652
  <button
632
653
  type="button"
@@ -644,7 +665,7 @@
644
665
  </div>
645
666
  {/if}
646
667
 
647
- <div class="grid grid-cols-8 divide-x divide-[var(--ui-border)] max-h-[600px] overflow-y-auto">
668
+ <div class="grid grid-cols-4 md:grid-cols-8 divide-x divide-[var(--ui-border)] max-h-[600px] overflow-y-auto">
648
669
  <!-- Hours Column -->
649
670
  <div class="col-span-1 divide-y divide-[var(--ui-border)]/50 text-right pr-2 text-[11px] font-medium text-[var(--ui-muted-foreground)]">
650
671
  {#each hours as hour}
@@ -652,8 +673,8 @@
652
673
  {/each}
653
674
  </div>
654
675
 
655
- <!-- 7 Days Grid Columns -->
656
- {#each weekDays as d}
676
+ <!-- Day Grid Columns -->
677
+ {#each (isMobile ? mobileWeekDays : weekDays) as d}
657
678
  {@const dayEvents = getTimedEventsForDay(d)}
658
679
  {@const layoutEvents = computeOverlappingLayout(dayEvents)}
659
680
  {@const isCurrentDay = isToday(d)}
@@ -755,6 +776,12 @@
755
776
  </div>
756
777
  {/each}
757
778
  </div>
779
+ <!-- Mobile swipe hint -->
780
+ {#if isMobile}
781
+ <div class="px-4 py-2 text-center text-[11px] text-[var(--ui-muted-foreground)] border-t border-[var(--ui-border)]">
782
+ Swipe to see more days
783
+ </div>
784
+ {/if}
758
785
  {:else}
759
786
  <!-- DAY VIEW -->
760
787
  <div class="p-3 border-b border-[var(--ui-border)] bg-[var(--ui-secondary)]/20 flex items-center justify-between">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intinyagroup/calendar",
3
- "version": "0.1.1-alpha.14",
3
+ "version": "0.1.1-alpha.16",
4
4
  "description": "Calendar and DatePicker components for intinyagroup UI",
5
5
  "type": "module",
6
6
  "exports": {
@@ -20,7 +20,7 @@
20
20
  "test": "vitest run"
21
21
  },
22
22
  "dependencies": {
23
- "@intinyagroup/ui": "0.1.1-alpha.12",
23
+ "@intinyagroup/ui": "0.1.1-alpha.14",
24
24
  "date-fns": "^4.4.0",
25
25
  "lucide-svelte": "^1.0.1"
26
26
  },