@makolabs/ripple 0.0.1-dev.6 → 0.0.1-dev.8

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.
Files changed (114) hide show
  1. package/README.md +393 -53
  2. package/dist/button/Button.svelte +5 -3
  3. package/dist/button/Button.svelte.d.ts +1 -1
  4. package/dist/button/button.d.ts +40 -63
  5. package/dist/button/button.js +15 -14
  6. package/dist/charts/Chart.svelte +533 -0
  7. package/dist/charts/Chart.svelte.d.ts +4 -0
  8. package/dist/drawer/Drawer.svelte +13 -2
  9. package/dist/drawer/Drawer.svelte.d.ts +1 -1
  10. package/dist/drawer/drawer.d.ts +0 -17
  11. package/dist/elements/alert/Alert.svelte +53 -0
  12. package/dist/elements/alert/Alert.svelte.d.ts +4 -0
  13. package/dist/elements/badge/Badge.svelte +13 -5
  14. package/dist/elements/badge/Badge.svelte.d.ts +1 -1
  15. package/dist/elements/badge/badge.d.ts +0 -12
  16. package/dist/elements/dropdown/Dropdown.svelte +32 -37
  17. package/dist/elements/dropdown/Dropdown.svelte.d.ts +1 -1
  18. package/dist/elements/dropdown/Select.svelte +143 -59
  19. package/dist/elements/dropdown/Select.svelte.d.ts +1 -1
  20. package/dist/elements/dropdown/dropdown.d.ts +34 -57
  21. package/dist/elements/dropdown/dropdown.js +10 -4
  22. package/dist/elements/dropdown/select.d.ts +34 -54
  23. package/dist/elements/dropdown/select.js +22 -14
  24. package/dist/elements/file-upload/FileUpload.svelte +213 -0
  25. package/dist/elements/file-upload/FileUpload.svelte.d.ts +4 -0
  26. package/dist/elements/progress/Progress.svelte +87 -0
  27. package/dist/elements/progress/Progress.svelte.d.ts +4 -0
  28. package/dist/elements/timeline/Timeline.svelte +92 -0
  29. package/dist/elements/timeline/Timeline.svelte.d.ts +7 -0
  30. package/dist/forms/Checkbox.svelte +54 -0
  31. package/dist/forms/Checkbox.svelte.d.ts +4 -0
  32. package/dist/forms/DateRange.svelte +493 -0
  33. package/dist/forms/DateRange.svelte.d.ts +4 -0
  34. package/dist/forms/Form.svelte +39 -0
  35. package/dist/forms/Form.svelte.d.ts +4 -0
  36. package/dist/forms/Input.svelte +86 -0
  37. package/dist/forms/Input.svelte.d.ts +4 -0
  38. package/dist/forms/NumberInput.svelte +159 -0
  39. package/dist/forms/NumberInput.svelte.d.ts +4 -0
  40. package/dist/forms/RadioInputs.svelte +64 -0
  41. package/dist/forms/RadioInputs.svelte.d.ts +4 -0
  42. package/dist/forms/RadioPill.svelte +66 -0
  43. package/dist/forms/RadioPill.svelte.d.ts +4 -0
  44. package/dist/forms/Slider.svelte +342 -0
  45. package/dist/forms/Slider.svelte.d.ts +4 -0
  46. package/dist/forms/Tags.svelte +181 -0
  47. package/dist/forms/Tags.svelte.d.ts +4 -0
  48. package/dist/forms/Toggle.svelte +132 -0
  49. package/dist/forms/Toggle.svelte.d.ts +4 -0
  50. package/dist/forms/slider.d.ts +143 -0
  51. package/dist/forms/slider.js +62 -0
  52. package/dist/header/Breadcrumbs.svelte +2 -1
  53. package/dist/header/Breadcrumbs.svelte.d.ts +1 -1
  54. package/dist/header/PageHeader.svelte +2 -2
  55. package/dist/header/PageHeader.svelte.d.ts +1 -1
  56. package/dist/header/breadcrumbs.d.ts +20 -14
  57. package/dist/header/breadcrumbs.js +6 -0
  58. package/dist/header/pageheaders.d.ts +1 -1
  59. package/dist/helper/date.d.ts +7 -0
  60. package/dist/helper/date.js +15 -0
  61. package/dist/index.d.ts +742 -9
  62. package/dist/index.js +59 -16
  63. package/dist/layout/card/Card.svelte +5 -8
  64. package/dist/layout/card/Card.svelte.d.ts +1 -1
  65. package/dist/layout/card/StatsCard.svelte +116 -87
  66. package/dist/layout/card/card.d.ts +22 -33
  67. package/dist/layout/card/card.js +9 -8
  68. package/dist/layout/card/stats-card.d.ts +23 -25
  69. package/dist/layout/card/stats-card.js +13 -13
  70. package/dist/layout/navbar/navbar.d.ts +0 -23
  71. package/dist/layout/sidebar/NavGroup.svelte +33 -41
  72. package/dist/layout/sidebar/NavGroup.svelte.d.ts +1 -1
  73. package/dist/layout/sidebar/NavItem.svelte +1 -1
  74. package/dist/layout/sidebar/NavItem.svelte.d.ts +1 -1
  75. package/dist/layout/sidebar/Sidebar.svelte +19 -25
  76. package/dist/layout/sidebar/Sidebar.svelte.d.ts +1 -1
  77. package/dist/layout/table/table.d.ts +1 -1
  78. package/dist/layout/tabs/tabs.d.ts +1 -1
  79. package/dist/modal/Modal.svelte +2 -1
  80. package/dist/modal/Modal.svelte.d.ts +1 -1
  81. package/dist/modal/modal.d.ts +0 -23
  82. package/dist/sonner/sonner.svelte +13 -0
  83. package/dist/sonner/sonner.svelte.d.ts +4 -0
  84. package/dist/types/variants.d.ts +1 -21
  85. package/dist/types/variants.js +1 -19
  86. package/dist/variants.d.ts +20 -0
  87. package/dist/variants.js +19 -0
  88. package/package.json +6 -2
  89. package/dist/button/index.d.ts +0 -1
  90. package/dist/button/index.js +0 -1
  91. package/dist/drawer/index.d.ts +0 -2
  92. package/dist/drawer/index.js +0 -1
  93. package/dist/elements/badge/index.d.ts +0 -2
  94. package/dist/elements/badge/index.js +0 -2
  95. package/dist/elements/dropdown/index.d.ts +0 -3
  96. package/dist/elements/dropdown/index.js +0 -2
  97. package/dist/header/index.d.ts +0 -4
  98. package/dist/header/index.js +0 -2
  99. package/dist/layout/card/index.d.ts +0 -4
  100. package/dist/layout/card/index.js +0 -2
  101. package/dist/layout/index.d.ts +0 -5
  102. package/dist/layout/index.js +0 -5
  103. package/dist/layout/navbar/index.d.ts +0 -2
  104. package/dist/layout/navbar/index.js +0 -2
  105. package/dist/layout/sidebar/index.d.ts +0 -2
  106. package/dist/layout/sidebar/index.js +0 -1
  107. package/dist/layout/sidebar/sidebar.d.ts +0 -46
  108. package/dist/layout/sidebar/sidebar.js +0 -1
  109. package/dist/layout/table/index.d.ts +0 -3
  110. package/dist/layout/table/index.js +0 -2
  111. package/dist/layout/tabs/index.d.ts +0 -3
  112. package/dist/layout/tabs/index.js +0 -3
  113. package/dist/modal/index.d.ts +0 -1
  114. package/dist/modal/index.js +0 -1
@@ -0,0 +1,493 @@
1
+ <script lang="ts">
2
+ import { cn } from '../helper/cls.js';
3
+ import type { DateRangeProps } from '../index.js';
4
+
5
+ let {
6
+ startDate = $bindable(),
7
+ endDate = $bindable(),
8
+ minDate = new Date(new Date().getFullYear() - 5, 0, 1),
9
+ maxDate = new Date(new Date().getFullYear() + 5, 11, 31),
10
+ disabled = false,
11
+ class: className = '',
12
+ placeholder = 'Select date range',
13
+ startLabel = 'Start date',
14
+ endLabel = 'End date',
15
+ format = 'MM/dd/yyyy',
16
+ id,
17
+ name,
18
+ onselect
19
+ }: DateRangeProps = $props();
20
+
21
+ let isOpen = $state(false);
22
+ let hoveredDate = $state<Date | null>(null);
23
+ let datePickerRef = $state<HTMLDivElement | null>(null);
24
+
25
+ let viewMode = $state<'days' | 'months' | 'years'>('days');
26
+ let viewDate = $state(new Date());
27
+
28
+ const handleOutsideClick = (event: MouseEvent) => {
29
+ if (isOpen && datePickerRef && !datePickerRef.contains(event.target as Node)) {
30
+ isOpen = false;
31
+ }
32
+ };
33
+
34
+ function formatDate(date: Date | null): string {
35
+ if (!date) return '';
36
+ const year = date.getFullYear();
37
+ const month = String(date.getMonth() + 1).padStart(2, '0');
38
+ const day = String(date.getDate()).padStart(2, '0');
39
+ return format.replace('yyyy', String(year)).replace('MM', month).replace('dd', day);
40
+ }
41
+
42
+ function parseDate(dateStr: string): Date | null {
43
+ if (!dateStr) return null;
44
+
45
+ const parts = dateStr.split('/');
46
+ if (parts.length !== 3) return null;
47
+
48
+ const month = parseInt(parts[0], 10) - 1;
49
+ const day = parseInt(parts[1], 10);
50
+ const year = parseInt(parts[2], 10);
51
+
52
+ if (isNaN(month) || isNaN(day) || isNaN(year)) return null;
53
+
54
+ return new Date(year, month, day);
55
+ }
56
+
57
+ function getDaysInMonth(month: Date): {
58
+ date: Date;
59
+ isCurrentMonth: boolean;
60
+ isToday: boolean;
61
+ isSelected: boolean;
62
+ isInRange: boolean;
63
+ isDisabled: boolean;
64
+ }[] {
65
+ const year = month.getFullYear();
66
+ const monthIndex = month.getMonth();
67
+ const daysInMonth = new Date(year, monthIndex + 1, 0).getDate();
68
+
69
+ const firstDay = new Date(year, monthIndex, 1);
70
+
71
+ const firstDayOfWeek = firstDay.getDay();
72
+
73
+ const daysFromPrevMonth = firstDayOfWeek;
74
+
75
+ const prevMonth = new Date(year, monthIndex, 0);
76
+ const prevMonthDays = prevMonth.getDate();
77
+
78
+ const daysInCalendar = 42; // 6 rows of 7 days
79
+ const daysFromNextMonth = daysInCalendar - daysInMonth - daysFromPrevMonth;
80
+
81
+ const today = new Date();
82
+ today.setHours(0, 0, 0, 0);
83
+
84
+ const days = [];
85
+
86
+ for (let i = daysFromPrevMonth - 1; i >= 0; i--) {
87
+ const date = new Date(year, monthIndex - 1, prevMonthDays - i);
88
+ days.push({
89
+ date,
90
+ isCurrentMonth: false,
91
+ isToday: date.getTime() === today.getTime(),
92
+ isSelected: isDateSelected(date),
93
+ isInRange: isDateInRange(date),
94
+ isDisabled: isDateDisabled(date)
95
+ });
96
+ }
97
+
98
+ for (let i = 1; i <= daysInMonth; i++) {
99
+ const date = new Date(year, monthIndex, i);
100
+ days.push({
101
+ date,
102
+ isCurrentMonth: true,
103
+ isToday: date.getTime() === today.getTime(),
104
+ isSelected: isDateSelected(date),
105
+ isInRange: isDateInRange(date),
106
+ isDisabled: isDateDisabled(date)
107
+ });
108
+ }
109
+
110
+ for (let i = 1; i <= daysFromNextMonth; i++) {
111
+ const date = new Date(year, monthIndex + 1, i);
112
+ days.push({
113
+ date,
114
+ isCurrentMonth: false,
115
+ isToday: date.getTime() === today.getTime(),
116
+ isSelected: isDateSelected(date),
117
+ isInRange: isDateInRange(date),
118
+ isDisabled: isDateDisabled(date)
119
+ });
120
+ }
121
+
122
+ return days;
123
+ }
124
+
125
+ function isDateSelected(date: Date): boolean {
126
+ return Boolean(
127
+ (startDate && isSameDate(date, startDate)) || (endDate && isSameDate(date, endDate))
128
+ );
129
+ }
130
+
131
+ function isDateInRange(date: Date): boolean {
132
+ if (!startDate || !endDate) {
133
+ if (startDate && hoveredDate) {
134
+ const hoverStart = startDate < hoveredDate ? startDate : hoveredDate;
135
+ const hoverEnd = startDate < hoveredDate ? hoveredDate : startDate;
136
+ return date >= hoverStart && date <= hoverEnd;
137
+ }
138
+ return false;
139
+ }
140
+
141
+ return date >= startDate && date <= endDate;
142
+ }
143
+
144
+ function isDateDisabled(date: Date): boolean {
145
+ return date < minDate || date > maxDate || disabled;
146
+ }
147
+
148
+ function isSameDate(date1: Date, date2: Date): boolean {
149
+ return (
150
+ date1.getDate() === date2.getDate() &&
151
+ date1.getMonth() === date2.getMonth() &&
152
+ date1.getFullYear() === date2.getFullYear()
153
+ );
154
+ }
155
+
156
+ function handleDateClick(date: Date): void {
157
+ if (isDateDisabled(date)) return;
158
+
159
+ if (!startDate || (startDate && endDate)) {
160
+ startDate = date;
161
+ endDate = undefined;
162
+ } else if (startDate && !endDate) {
163
+ if (date < startDate) {
164
+ endDate = startDate;
165
+ startDate = date;
166
+ } else {
167
+ endDate = date;
168
+ }
169
+ }
170
+
171
+ onselect?.({ startDate, endDate });
172
+
173
+ if (startDate && endDate) {
174
+ isOpen = false;
175
+ }
176
+ }
177
+
178
+ function handleDateHover(date: Date): void {
179
+ hoveredDate = date;
180
+ }
181
+
182
+ function nextMonth(): void {
183
+ viewDate = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1);
184
+ }
185
+
186
+ function prevMonth(): void {
187
+ viewDate = new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1);
188
+ }
189
+
190
+ function showMonths(): void {
191
+ viewMode = 'months';
192
+ }
193
+
194
+ function showYears(): void {
195
+ viewMode = 'years';
196
+ }
197
+
198
+ function selectMonth(month: number): void {
199
+ viewDate = new Date(viewDate.getFullYear(), month, 1);
200
+ viewMode = 'days';
201
+ }
202
+
203
+ function selectYear(year: number): void {
204
+ viewDate = new Date(year, viewDate.getMonth(), 1);
205
+ viewMode = 'months';
206
+ }
207
+
208
+ function getMonthName(monthIndex: number): string {
209
+ return new Date(2000, monthIndex, 1).toLocaleString('default', { month: 'long' });
210
+ }
211
+
212
+ function toggleDatepicker(): void {
213
+ if (disabled) return;
214
+ isOpen = !isOpen;
215
+ }
216
+
217
+ function clearDates(event: Event): void {
218
+ event.stopPropagation();
219
+ startDate = undefined;
220
+ endDate = undefined;
221
+ onselect?.({ startDate, endDate });
222
+ }
223
+
224
+ $effect(() => {
225
+ if (startDate && endDate) {
226
+ viewDate = new Date(startDate);
227
+ }
228
+ });
229
+
230
+ function getValue(): string {
231
+ return `${startDate ? formatDate(startDate) : ''}:${endDate ? formatDate(endDate) : ''}`;
232
+ }
233
+ </script>
234
+
235
+ <svelte:window onmousedown={handleOutsideClick} />
236
+
237
+ <input type="hidden" name={`${name}[start]`} value={startDate?.toISOString()} />
238
+ <input type="hidden" name={`${name}[end]`} value={endDate?.toISOString()} />
239
+ <input type="hidden" name={`${name}[format]`} value={format} />
240
+ <input type="hidden" name={`${name}`} value={getValue()} />
241
+
242
+ <div class={cn('relative block w-full', className)} bind:this={datePickerRef}>
243
+ <div class="relative">
244
+ <button
245
+ {id}
246
+ type="button"
247
+ class={cn(
248
+ 'flex w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm',
249
+ disabled
250
+ ? 'cursor-not-allowed bg-gray-100 text-gray-400'
251
+ : 'focus:border-primary-500 focus:ring-primary-500 hover:border-gray-400 focus:ring-2'
252
+ )}
253
+ onclick={toggleDatepicker}
254
+ aria-haspopup="true"
255
+ aria-expanded={isOpen}
256
+ {disabled}
257
+ >
258
+ <span class={startDate && endDate ? 'text-gray-900' : 'text-gray-500'}>
259
+ {startDate && endDate
260
+ ? `${formatDate(startDate)} - ${formatDate(endDate)}`
261
+ : startDate
262
+ ? `${formatDate(startDate)} - Select end date`
263
+ : placeholder}
264
+ </span>
265
+ <svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
266
+ <path
267
+ fill-rule="evenodd"
268
+ d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
269
+ clip-rule="evenodd"
270
+ />
271
+ </svg>
272
+ </button>
273
+
274
+ {#if startDate || endDate}
275
+ <button
276
+ type="button"
277
+ class="absolute top-1/2 right-10 -translate-y-1/2 text-gray-400 hover:text-gray-500"
278
+ onclick={clearDates}
279
+ aria-label="Clear dates"
280
+ >
281
+ <svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
282
+ <path
283
+ fill-rule="evenodd"
284
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
285
+ clip-rule="evenodd"
286
+ />
287
+ </svg>
288
+ </button>
289
+ {/if}
290
+ </div>
291
+
292
+ {#if isOpen}
293
+ <div
294
+ class="ring-opacity-5 absolute z-10 mt-1 w-full origin-top-left rounded-md bg-white p-4 shadow-lg ring-1 ring-black focus:outline-none"
295
+ >
296
+ <div class="mb-2 flex items-center justify-between">
297
+ {#if viewMode === 'days'}
298
+ <button
299
+ type="button"
300
+ aria-label="Previous month"
301
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
302
+ onclick={prevMonth}
303
+ >
304
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
305
+ <path
306
+ fill-rule="evenodd"
307
+ d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
308
+ clip-rule="evenodd"
309
+ />
310
+ </svg>
311
+ </button>
312
+ <button
313
+ type="button"
314
+ class="inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-700 hover:bg-gray-100"
315
+ onclick={showMonths}
316
+ >
317
+ {getMonthName(viewDate.getMonth())}
318
+ {viewDate.getFullYear()}
319
+ </button>
320
+ <button
321
+ type="button"
322
+ aria-label="Next month"
323
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
324
+ onclick={nextMonth}
325
+ >
326
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
327
+ <path
328
+ fill-rule="evenodd"
329
+ d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
330
+ clip-rule="evenodd"
331
+ />
332
+ </svg>
333
+ </button>
334
+ {:else if viewMode === 'months'}
335
+ <button
336
+ type="button"
337
+ aria-label="Previous year"
338
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
339
+ onclick={() =>
340
+ (viewDate = new Date(viewDate.getFullYear() - 1, viewDate.getMonth(), 1))}
341
+ >
342
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
343
+ <path
344
+ fill-rule="evenodd"
345
+ d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
346
+ clip-rule="evenodd"
347
+ />
348
+ </svg>
349
+ </button>
350
+ <button
351
+ type="button"
352
+ aria-label="Current year"
353
+ class="inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-700"
354
+ onclick={showYears}
355
+ >
356
+ {viewDate.getFullYear()}
357
+ </button>
358
+ <button
359
+ type="button"
360
+ aria-label="Next year"
361
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
362
+ onclick={() =>
363
+ (viewDate = new Date(viewDate.getFullYear() + 1, viewDate.getMonth(), 1))}
364
+ >
365
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
366
+ <path
367
+ fill-rule="evenodd"
368
+ d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
369
+ clip-rule="evenodd"
370
+ />
371
+ </svg>
372
+ </button>
373
+ {:else if viewMode === 'years'}
374
+ <button
375
+ type="button"
376
+ aria-label="Previous year"
377
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
378
+ onclick={() =>
379
+ (viewDate = new Date(viewDate.getFullYear() - 12, viewDate.getMonth(), 1))}
380
+ >
381
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
382
+ <path
383
+ fill-rule="evenodd"
384
+ d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
385
+ clip-rule="evenodd"
386
+ />
387
+ </svg>
388
+ </button>
389
+ <button
390
+ type="button"
391
+ aria-label="Current year range"
392
+ class="inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-700"
393
+ >
394
+ {viewDate.getFullYear() - 6} - {viewDate.getFullYear() + 5}
395
+ </button>
396
+ <button
397
+ type="button"
398
+ aria-label="Next year"
399
+ class="inline-flex items-center rounded-md p-1 text-sm text-gray-500 hover:bg-gray-100"
400
+ onclick={() =>
401
+ (viewDate = new Date(viewDate.getFullYear() + 12, viewDate.getMonth(), 1))}
402
+ >
403
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
404
+ <path
405
+ fill-rule="evenodd"
406
+ d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
407
+ clip-rule="evenodd"
408
+ />
409
+ </svg>
410
+ </button>
411
+ {/if}
412
+ </div>
413
+
414
+ {#if viewMode === 'days'}
415
+ <div class="mb-1 grid grid-cols-7 gap-1 text-center text-xs font-medium text-gray-500">
416
+ <div>Su</div>
417
+ <div>Mo</div>
418
+ <div>Tu</div>
419
+ <div>We</div>
420
+ <div>Th</div>
421
+ <div>Fr</div>
422
+ <div>Sa</div>
423
+ </div>
424
+ <div class="grid grid-cols-7 gap-1">
425
+ {#each getDaysInMonth(viewDate) as { date, isCurrentMonth, isToday, isSelected, isInRange, isDisabled }}
426
+ <button
427
+ type="button"
428
+ class={cn(
429
+ 'flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium',
430
+ isDisabled ? 'cursor-not-allowed text-gray-300' : 'hover:bg-gray-100',
431
+ isSelected ? 'bg-primary-500 hover:bg-primary-600 text-white' : '',
432
+ isInRange && !isSelected ? 'bg-primary-100 text-primary-800' : '',
433
+ !isCurrentMonth && !isSelected && !isInRange ? 'text-gray-400' : '',
434
+ isToday && !isSelected ? 'border-primary-500 border' : ''
435
+ )}
436
+ onclick={() => handleDateClick(date)}
437
+ onmouseenter={() => handleDateHover(date)}
438
+ disabled={isDisabled}
439
+ >
440
+ {date.getDate()}
441
+ </button>
442
+ {/each}
443
+ </div>
444
+ {:else if viewMode === 'months'}
445
+ <div class="grid grid-cols-3 gap-2">
446
+ {#each Array(12).fill(0) as _, month}
447
+ <button
448
+ type="button"
449
+ class={cn(
450
+ 'flex items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
451
+ viewDate.getMonth() === month
452
+ ? 'bg-primary-500 text-white'
453
+ : 'text-gray-700 hover:bg-gray-100'
454
+ )}
455
+ onclick={() => selectMonth(month)}
456
+ >
457
+ {getMonthName(month).substring(0, 3)}
458
+ </button>
459
+ {/each}
460
+ </div>
461
+ {:else if viewMode === 'years'}
462
+ <div class="grid grid-cols-3 gap-2">
463
+ {#each Array(12).fill(0) as _, i}
464
+ {@const year = viewDate.getFullYear() - 6 + i}
465
+ <button
466
+ type="button"
467
+ class={cn(
468
+ 'flex items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
469
+ viewDate.getFullYear() === year
470
+ ? 'bg-primary-500 text-white'
471
+ : 'text-gray-700 hover:bg-gray-100'
472
+ )}
473
+ onclick={() => selectYear(year)}
474
+ >
475
+ {year}
476
+ </button>
477
+ {/each}
478
+ </div>
479
+ {/if}
480
+
481
+ {#if startDate || endDate}
482
+ <div class="mt-4 flex justify-between border-t border-gray-200 pt-3 text-xs text-gray-500">
483
+ <div>
484
+ {startDate ? `${startLabel}: ${formatDate(startDate)}` : ''}
485
+ </div>
486
+ <div>
487
+ {endDate ? `${endLabel}: ${formatDate(endDate)}` : ''}
488
+ </div>
489
+ </div>
490
+ {/if}
491
+ </div>
492
+ {/if}
493
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { DateRangeProps } from '../index.js';
2
+ declare const DateRange: import("svelte").Component<DateRangeProps, {}, "startDate" | "endDate">;
3
+ type DateRange = ReturnType<typeof DateRange>;
4
+ export default DateRange;
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import { setContext } from 'svelte';
3
+ import { cn } from '../helper/cls.js';
4
+ import type { FormProps } from '../index.js';
5
+
6
+ let {
7
+ form,
8
+ class: className = '',
9
+ method = 'POST',
10
+ action,
11
+ enctype,
12
+ autocomplete,
13
+ novalidate = false,
14
+ children,
15
+ ...restProps
16
+ }: FormProps<any> = $props();
17
+
18
+ const { enhance, delayed } = form;
19
+ setContext('form', form);
20
+
21
+ const formClasses = $derived(cn('space-y-4', className));
22
+ </script>
23
+
24
+ <form
25
+ {method}
26
+ {action}
27
+ {enctype}
28
+ {autocomplete}
29
+ {novalidate}
30
+ class={formClasses}
31
+ use:enhance
32
+ {...restProps}
33
+ >
34
+ {@render children?.()}
35
+ </form>
36
+
37
+ {#if $delayed}
38
+ <div class="fixed inset-0 bg-black/20 backdrop-blur-sm"></div>
39
+ {/if}
@@ -0,0 +1,4 @@
1
+ import type { FormProps } from '../index.js';
2
+ declare const Form: import("svelte").Component<FormProps<any>, {}, "">;
3
+ type Form = ReturnType<typeof Form>;
4
+ export default Form;
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import { cn } from '../helper/cls.js';
3
+ import { Size } from '../variants.js';
4
+ import type { InputProps } from '../index.js';
5
+
6
+ let {
7
+ name,
8
+ id = name,
9
+ type = 'text',
10
+ label,
11
+ placeholder,
12
+ disabled = false,
13
+ class: className = '',
14
+ size = Size.BASE,
15
+ value = $bindable(),
16
+ errors = [],
17
+ ...restProps
18
+ }: InputProps = $props();
19
+
20
+ const BASIC_TYPES = ['text', 'email', 'password', 'number', 'tel', 'url', 'date', 'textarea'];
21
+ const inputClasses = $derived(
22
+ cn(
23
+ 'transition-colors',
24
+ {
25
+ 'border rounded-lg shadow-sm w-full bg-white px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2':
26
+ BASIC_TYPES.includes(type),
27
+ 'w-full bg-white px-3 py-2 text-sm resize-y min-h-[100px]': type === 'textarea',
28
+ 'border-red-300 focus:border-red-500 focus:ring-red-500': errors.length,
29
+ 'opacity-50 cursor-not-allowed': disabled,
30
+ ...(BASIC_TYPES.includes(type)
31
+ ? {
32
+ 'h-8 text-sm': size === Size.SM,
33
+ 'h-10 text-base': size === Size.BASE,
34
+ 'h-12 text-lg': size === Size.LG
35
+ }
36
+ : {}),
37
+ 'border-gray-300 focus:border-primary-500 focus:ring-primary-500': !errors.length
38
+ },
39
+ className
40
+ )
41
+ );
42
+
43
+ const inputCommonProps = $derived({
44
+ id,
45
+ name,
46
+ type,
47
+ placeholder,
48
+ disabled,
49
+ class: inputClasses,
50
+ 'aria-invalid': !!errors.length,
51
+ 'aria-describedby': errors.length ? `${name}-errors` : undefined,
52
+ ...restProps
53
+ });
54
+ </script>
55
+
56
+ <div class="w-full">
57
+ {@render Label('mb-2')}
58
+ {#if type === 'textarea'}
59
+ <textarea {...inputCommonProps} bind:value></textarea>
60
+ {:else}
61
+ <input {...inputCommonProps} bind:value />
62
+ {/if}
63
+ {#if errors.length}
64
+ {#each errors as error (error)}
65
+ <p id="{name}-errors" class="mt-1 text-sm text-red-600" role="alert">
66
+ {error}
67
+ </p>
68
+ {/each}
69
+ {/if}
70
+ </div>
71
+
72
+ {#snippet Label(extraClass = '')}
73
+ {#if label}
74
+ <label
75
+ for={id}
76
+ class={cn(
77
+ 'block w-full text-sm font-medium',
78
+ {
79
+ 'text-gray-700': !errors.length,
80
+ 'text-red-600': errors.length
81
+ },
82
+ extraClass
83
+ )}>{label}</label
84
+ >
85
+ {/if}
86
+ {/snippet}
@@ -0,0 +1,4 @@
1
+ import type { InputProps } from '../index.js';
2
+ declare const Input: import("svelte").Component<InputProps, {}, "value">;
3
+ type Input = ReturnType<typeof Input>;
4
+ export default Input;