DatePicker.svelte 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <script lang="ts">
  2. import type { AppointmentDateTimes } from '$lib/dbdata';
  3. import { isSameDate } from '$lib/funcs';
  4. import { t } from '$lib/translations';
  5. import moment, { type Moment } from 'moment';
  6. import { onMount } from 'svelte';
  7. let currentDate: Date = new Date();
  8. let days: Array<{ day: number; isActive: boolean; isAvailable: boolean }> = [];
  9. let currentMonth: string = '';
  10. export let selectedDate: Date;
  11. export let appointmentDateTimes: AppointmentDateTimes[];
  12. $: {
  13. if (appointmentDateTimes.length > 0) {
  14. renderCalendar();
  15. }
  16. }
  17. function isDayAvailable(year: number, month: number, day: number) {
  18. const dateStr = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
  19. const testDate: Date = moment(dateStr).toDate();
  20. return appointmentDateTimes.some((date) => isSameDate(testDate, date.date));
  21. }
  22. /**
  23. * Updates the calendar display by calculating the days of the current month.
  24. */
  25. const renderCalendar = (): void => {
  26. const mDate: Moment = moment(currentDate).locale("de");
  27. const year: number = currentDate.getFullYear();
  28. const month: number = mDate.month();
  29. let locale: string = "en-US";
  30. if (navigator.language.includes("de")) locale = "de";
  31. // make sure to properly translate this
  32. currentMonth = currentDate.toLocaleDateString(locale, { month: 'long', year: 'numeric' });
  33. const firstDayOfMonth: Date = new Date(year, month, 1);
  34. const lastDayOfMonth: Date = new Date(year, month + 1, 0);
  35. let firstDayIndex: number = firstDayOfMonth.getDay() - 1;
  36. if (firstDayIndex === -1) firstDayIndex = 6;
  37. const daysInMonth: number = lastDayOfMonth.getDate();
  38. // Reset days array
  39. days = [];
  40. // Add empty cells for days before the first of the month
  41. for (let i = 0; i < firstDayIndex; i++) {
  42. days.push({ day: 0, isActive: false, isAvailable: false });
  43. }
  44. // Add days of the month
  45. for (let day = 1; day <= daysInMonth; day++) {
  46. days.push({ day, isActive: false, isAvailable: isDayAvailable(year, month + 1, day) }); // Example: Make day 20 active
  47. }
  48. };
  49. /**
  50. * Changes the current month by a given offset and re-renders the calendar.
  51. * @param offset - The number of months to move forward or backward.
  52. */
  53. const changeMonth = (offset: number): void => {
  54. //currentDate.setMonth(currentDate.getMonth() + offset);
  55. if (offset === -1) {
  56. currentDate = moment(currentDate).subtract(1, 'month').toDate();
  57. } else {
  58. currentDate = moment(currentDate).add(1, 'month').toDate();
  59. }
  60. renderCalendar();
  61. };
  62. onMount(() => {
  63. currentDate = new Date();
  64. renderCalendar();
  65. });
  66. /**
  67. * Handles the selection of a day in the calendar.
  68. * @param selectedDay - The day number that was clicked.
  69. */
  70. const selectDay = (selectedDay: number): void => {
  71. days = days.map((d) => ({ ...d, isActive: d.day === selectedDay }));
  72. const selectedDateValue = new Date(
  73. currentDate.getFullYear(),
  74. currentDate.getMonth(),
  75. selectedDay
  76. );
  77. selectedDate = selectedDateValue;
  78. };
  79. </script>
  80. <div class="date-picker border center">
  81. <div class="date-picker-header">
  82. <button class="button square secondary medium" on:click={() => changeMonth(-1)}>&lt;</button>
  83. <h4 id="currentMonth" class="small wrap center-align">{currentMonth}</h4>
  84. <button class="button square secondary medium" on:click={() => changeMonth(1)}>&gt;</button>
  85. </div>
  86. <div class="date-picker-grid">
  87. <!-- Days of the week -->
  88. <div class="date-picker-day">{$t("common.monday-short")}</div>
  89. <div class="date-picker-day">{$t("common.tuesday-short")}</div>
  90. <div class="date-picker-day">{$t("common.wednesday-short")}</div>
  91. <div class="date-picker-day">{$t("common.thursday-short")}</div>
  92. <div class="date-picker-day">{$t("common.friday-short")}</div>
  93. <div class="date-picker-day">{$t("common.saturday-short")}</div>
  94. <div class="date-picker-day">{$t("common.sunday-short")}</div>
  95. <!-- Render days -->
  96. {#each days as { day, isActive, isAvailable }, index}
  97. {#if isAvailable}
  98. <!-- svelte-ignore a11y_click_events_have_key_events -->
  99. <!-- svelte-ignore a11y_no_static_element_interactions -->
  100. <div
  101. class="date-picker-cell {!isActive ? 'secondary' : ''} {isActive ? 'active' : ''} {day ===
  102. 0
  103. ? 'empty'
  104. : ''}"
  105. on:click={() => day && selectDay(day)}
  106. >
  107. {day || ''}
  108. </div>
  109. {:else}
  110. <!-- svelte-ignore a11y_click_events_have_key_events -->
  111. <!-- svelte-ignore a11y_no_static_element_interactions -->
  112. <div class="date-picker-cell">
  113. {day || ''}
  114. </div>
  115. {/if}
  116. {/each}
  117. </div>
  118. </div>
  119. <style>
  120. .date-picker {
  121. border: 1px solid var(--border-color);
  122. border-radius: var(--border-radius);
  123. padding: 1rem;
  124. max-width: 600px;
  125. background: var(--background);
  126. box-shadow: var(--shadow);
  127. }
  128. .date-picker-header {
  129. display: flex;
  130. justify-content: space-between;
  131. align-items: center;
  132. margin-bottom: 1rem;
  133. }
  134. .date-picker-grid {
  135. display: grid;
  136. grid-template-columns: repeat(7, 1fr);
  137. gap: 0.5rem;
  138. text-align: center;
  139. }
  140. .date-picker-day {
  141. font-weight: bold;
  142. color: var(--secondary);
  143. }
  144. .date-picker-cell {
  145. padding: 0.5rem;
  146. border-radius: var(--border-radius);
  147. cursor: pointer;
  148. user-select: none;
  149. }
  150. .date-picker-cell:hover {
  151. background-color: var(--hover-background);
  152. }
  153. .date-picker-cell.active {
  154. background-color: var(--primary);
  155. color: var(--background);
  156. }
  157. .empty {
  158. pointer-events: none;
  159. visibility: hidden;
  160. }
  161. </style>