flowbite-svelte 1.6.2 → 1.6.3

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.
@@ -10,6 +10,7 @@
10
10
  const dateFormatOptions = $derived(dateFormat ?? dateFormatDefault);
11
11
  // Internal state
12
12
  let isOpen: boolean = $state(inline);
13
+ let showMonthSelector: boolean = $state(false);
13
14
  let inputElement: HTMLInputElement | null = $state(null);
14
15
  let datepickerContainerElement: HTMLDivElement;
15
16
  let currentMonth: Date = $state(value || defaultDate || new Date());
@@ -62,6 +63,11 @@
62
63
  };
63
64
  let weekdays = getWeekdayNames();
64
65
 
66
+ const getMonthNames = (): string[] => {
67
+ return Array.from({ length: 12 }, (_, i) => new Date(2000, i, 1).toLocaleDateString(locale, { month: "short" }));
68
+ };
69
+ let monthNames = getMonthNames();
70
+
65
71
  const addMonth = (date: Date, increment: number): Date => new Date(date.getFullYear(), date.getMonth() + increment, 1);
66
72
  const addDay = (date: Date, increment: number): Date => new Date(date.getFullYear(), date.getMonth(), date.getDate() + increment);
67
73
 
@@ -69,6 +75,21 @@
69
75
  currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + increment, 1);
70
76
  }
71
77
 
78
+ function changeYear(increment: number) {
79
+ currentMonth = new Date(currentMonth.getFullYear() + increment, currentMonth.getMonth(), 1);
80
+ }
81
+
82
+ function selectMonth(monthIndex: number, event: MouseEvent) {
83
+ event.stopPropagation();
84
+ currentMonth = new Date(currentMonth.getFullYear(), monthIndex, 1);
85
+ showMonthSelector = false;
86
+ }
87
+
88
+ function toggleMonthSelector(event: MouseEvent) {
89
+ event.stopPropagation();
90
+ showMonthSelector = !showMonthSelector;
91
+ }
92
+
72
93
  function handleDaySelect(day: Date) {
73
94
  if (range) {
74
95
  if (!rangeFrom || (rangeFrom && rangeTo)) {
@@ -98,6 +119,7 @@
98
119
  function handleClickOutside(event: MouseEvent) {
99
120
  if (isOpen && datepickerContainerElement && !datepickerContainerElement.contains(event.target as Node)) {
100
121
  isOpen = false;
122
+ showMonthSelector = false;
101
123
  }
102
124
  }
103
125
 
@@ -133,6 +155,7 @@
133
155
  break;
134
156
  case "Escape":
135
157
  isOpen = false;
158
+ showMonthSelector = false;
136
159
  inputElement?.focus();
137
160
  break;
138
161
  default:
@@ -180,6 +203,14 @@
180
203
  </ToolbarButton>
181
204
  {/snippet}
182
205
 
206
+ {#snippet yearNavButton(forward: boolean)}
207
+ <ToolbarButton color="dark" onclick={() => changeYear(forward ? 1 : -1)} size="lg" aria-label={forward ? "Next year" : "Previous year"}>
208
+ <svg class="h-3 w-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
209
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={forward ? "M1 5h12m0 0L9 1m4 4L9 9" : "M13 5H1m0 0 4 4M1 5l4-4"}></path>
210
+ </svg>
211
+ </ToolbarButton>
212
+ {/snippet}
213
+
183
214
  <div bind:this={datepickerContainerElement} class={["relative", inline && "inline-block"]}>
184
215
  {#if !inline}
185
216
  <div class="relative">
@@ -197,25 +228,46 @@
197
228
  {#if title}
198
229
  <h2 class={titleVariant()}>{title}</h2>
199
230
  {/if}
200
- <div class={nav()}>
201
- {@render navButton(false)}
202
- <h3 class={polite()} aria-live="polite">
203
- {currentMonth.toLocaleString(locale, { month: "long", year: "numeric" })}
204
- </h3>
205
- {@render navButton(true)}
206
- </div>
207
- <div class={grid()} role="grid">
208
- {#each weekdays as day}
209
- <div class={columnHeader()} role="columnheader">{day}</div>
210
- {/each}
211
- {#each daysInMonth as day}
212
- {@const current = day.getMonth() !== currentMonth.getMonth()}
213
- <button type="button" color={isSelected(day) ? color : "alternative"} class={dayButton({ current, today: isToday(day), color: isInRange(day) ? color : undefined })} onclick={() => handleDaySelect(day)} onkeydown={handleCalendarKeydown} aria-label={day.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })} aria-selected={isSelected(day)} role="gridcell">
214
- {day.getDate()}
231
+
232
+ {#if showMonthSelector}
233
+ <!-- Month/Year Selector View -->
234
+ <div class={nav()}>
235
+ {@render yearNavButton(false)}
236
+ <h3 class={polite()} aria-live="polite">
237
+ {currentMonth.getFullYear()}
238
+ </h3>
239
+ {@render yearNavButton(true)}
240
+ </div>
241
+ <div class="grid grid-cols-4 gap-2 p-4">
242
+ {#each monthNames as month, index}
243
+ <button type="button" class="rounded-lg px-3 py-2 text-sm hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:hover:bg-gray-700 {currentMonth.getMonth() === index ? 'bg-blue-500 text-white' : 'text-gray-700 dark:text-gray-300'}" onclick={(event) => selectMonth(index, event)}>
244
+ {month}
245
+ </button>
246
+ {/each}
247
+ </div>
248
+ {:else}
249
+ <!-- Regular Calendar View -->
250
+ <div class={nav()}>
251
+ {@render navButton(false)}
252
+ <button type="button" class={cn(polite(), "cursor-pointer rounded px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-700")} aria-live="polite" onclick={(event) => toggleMonthSelector(event)}>
253
+ {currentMonth.toLocaleString(locale, { month: "long", year: "numeric" })}
215
254
  </button>
216
- {/each}
217
- </div>
218
- {#if showActionButtons}
255
+ {@render navButton(true)}
256
+ </div>
257
+ <div class={grid()} role="grid">
258
+ {#each weekdays as day}
259
+ <div class={columnHeader()} role="columnheader">{day}</div>
260
+ {/each}
261
+ {#each daysInMonth as day}
262
+ {@const current = day.getMonth() !== currentMonth.getMonth()}
263
+ <button type="button" color={isSelected(day) ? color : "alternative"} class={dayButton({ current, today: isToday(day), color: isInRange(day) ? color : undefined })} onclick={() => handleDaySelect(day)} onkeydown={handleCalendarKeydown} aria-label={day.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })} aria-selected={isSelected(day)} role="gridcell">
264
+ {day.getDate()}
265
+ </button>
266
+ {/each}
267
+ </div>
268
+ {/if}
269
+
270
+ {#if showActionButtons && !showMonthSelector}
219
271
  <div class={actionButtons()}>
220
272
  <Button onclick={() => handleDaySelect(new Date())} {color} size="sm">Today</Button>
221
273
  <Button onclick={handleClear} color="red" size="sm">Clear</Button>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbite-svelte",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Flowbite components for Svelte",
5
5
  "main": "dist/index.js",
6
6
  "author": {