@aspect-ops/exon-ui 0.0.3 → 0.2.0

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 (79) hide show
  1. package/README.md +929 -54
  2. package/dist/components/Accordion/Accordion.svelte +79 -0
  3. package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
  4. package/dist/components/Accordion/AccordionItem.svelte +198 -0
  5. package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/Accordion/index.js +2 -0
  8. package/dist/components/Carousel/Carousel.svelte +454 -0
  9. package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
  10. package/dist/components/Carousel/CarouselSlide.svelte +22 -0
  11. package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
  12. package/dist/components/Carousel/index.d.ts +2 -0
  13. package/dist/components/Carousel/index.js +2 -0
  14. package/dist/components/Chip/Chip.svelte +461 -0
  15. package/dist/components/Chip/Chip.svelte.d.ts +17 -0
  16. package/dist/components/Chip/ChipGroup.svelte +76 -0
  17. package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
  18. package/dist/components/Chip/index.d.ts +2 -0
  19. package/dist/components/Chip/index.js +2 -0
  20. package/dist/components/DatePicker/DatePicker.svelte +746 -0
  21. package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
  22. package/dist/components/DatePicker/index.d.ts +1 -0
  23. package/dist/components/DatePicker/index.js +1 -0
  24. package/dist/components/FileUpload/FileUpload.svelte +484 -0
  25. package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
  26. package/dist/components/FileUpload/index.d.ts +1 -0
  27. package/dist/components/FileUpload/index.js +1 -0
  28. package/dist/components/Image/Image.svelte +223 -0
  29. package/dist/components/Image/Image.svelte.d.ts +19 -0
  30. package/dist/components/Image/index.d.ts +1 -0
  31. package/dist/components/Image/index.js +1 -0
  32. package/dist/components/NumberInput/NumberInput.svelte +293 -0
  33. package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
  34. package/dist/components/NumberInput/index.d.ts +1 -0
  35. package/dist/components/NumberInput/index.js +1 -0
  36. package/dist/components/OTPInput/OTPInput.svelte +312 -0
  37. package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
  38. package/dist/components/OTPInput/index.d.ts +1 -0
  39. package/dist/components/OTPInput/index.js +1 -0
  40. package/dist/components/Pagination/Pagination.svelte +243 -0
  41. package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
  42. package/dist/components/Pagination/index.d.ts +1 -0
  43. package/dist/components/Pagination/index.js +1 -0
  44. package/dist/components/Rating/Rating.svelte +316 -0
  45. package/dist/components/Rating/Rating.svelte.d.ts +16 -0
  46. package/dist/components/Rating/index.d.ts +1 -0
  47. package/dist/components/Rating/index.js +1 -0
  48. package/dist/components/SearchInput/SearchInput.svelte +480 -0
  49. package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
  50. package/dist/components/SearchInput/index.d.ts +1 -0
  51. package/dist/components/SearchInput/index.js +1 -0
  52. package/dist/components/Slider/Slider.svelte +324 -0
  53. package/dist/components/Slider/Slider.svelte.d.ts +14 -0
  54. package/dist/components/Slider/index.d.ts +1 -0
  55. package/dist/components/Slider/index.js +1 -0
  56. package/dist/components/Stepper/Stepper.svelte +100 -0
  57. package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
  58. package/dist/components/Stepper/StepperStep.svelte +391 -0
  59. package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
  60. package/dist/components/Stepper/index.d.ts +2 -0
  61. package/dist/components/Stepper/index.js +2 -0
  62. package/dist/components/TimePicker/TimePicker.svelte +803 -0
  63. package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
  64. package/dist/components/TimePicker/index.d.ts +1 -0
  65. package/dist/components/TimePicker/index.js +1 -0
  66. package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
  67. package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
  68. package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
  69. package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
  70. package/dist/components/ToggleGroup/index.d.ts +3 -0
  71. package/dist/components/ToggleGroup/index.js +2 -0
  72. package/dist/index.d.ts +13 -1
  73. package/dist/index.js +12 -0
  74. package/dist/types/data-display.d.ts +68 -0
  75. package/dist/types/index.d.ts +3 -2
  76. package/dist/types/input.d.ts +82 -0
  77. package/dist/types/input.js +2 -0
  78. package/dist/types/navigation.d.ts +15 -0
  79. package/package.json +1 -1
@@ -0,0 +1,746 @@
1
+ <script lang="ts">
2
+ import type { InputSize } from '../../types/index.js';
3
+ import { onMount } from 'svelte';
4
+
5
+ interface Props {
6
+ value?: Date | null;
7
+ placeholder?: string;
8
+ min?: Date | null;
9
+ max?: Date | null;
10
+ disabled?: boolean;
11
+ required?: boolean;
12
+ error?: boolean;
13
+ size?: InputSize;
14
+ format?: string;
15
+ locale?: string;
16
+ class?: string;
17
+ onchange?: (date: Date | null) => void;
18
+ onclear?: () => void;
19
+ }
20
+
21
+ let {
22
+ value = $bindable(null),
23
+ placeholder = 'Select date',
24
+ min = null,
25
+ max = null,
26
+ disabled = false,
27
+ required = false,
28
+ error = false,
29
+ size = 'md',
30
+ format = 'yyyy-MM-dd',
31
+ locale = 'en-US',
32
+ class: className = '',
33
+ onchange,
34
+ onclear
35
+ }: Props = $props();
36
+
37
+ let isOpen = $state(false);
38
+ let inputElement: HTMLInputElement | undefined;
39
+ let calendarElement = $state<HTMLDivElement | undefined>(undefined);
40
+ let currentMonth = $state(
41
+ value ? new Date(value.getFullYear(), value.getMonth(), 1) : new Date()
42
+ );
43
+ let focusedDate = $state<Date | null>(null);
44
+
45
+ const formattedValue = $derived(value ? formatDate(value, format, locale) : '');
46
+
47
+ function formatDate(date: Date, fmt: string, loc: string): string {
48
+ const year = date.getFullYear();
49
+ const month = String(date.getMonth() + 1).padStart(2, '0');
50
+ const day = String(date.getDate()).padStart(2, '0');
51
+
52
+ // Simple format replacements
53
+ return fmt
54
+ .replace('yyyy', String(year))
55
+ .replace('yy', String(year).slice(-2))
56
+ .replace('MM', month)
57
+ .replace('M', String(date.getMonth() + 1))
58
+ .replace('dd', day)
59
+ .replace('d', String(date.getDate()));
60
+ }
61
+
62
+ function getDaysInMonth(date: Date): Date[] {
63
+ const year = date.getFullYear();
64
+ const month = date.getMonth();
65
+ const firstDay = new Date(year, month, 1);
66
+ const lastDay = new Date(year, month + 1, 0);
67
+
68
+ const days: Date[] = [];
69
+
70
+ // Add padding days from previous month
71
+ const firstDayOfWeek = firstDay.getDay();
72
+ for (let i = firstDayOfWeek - 1; i >= 0; i--) {
73
+ const prevDate = new Date(year, month, -i);
74
+ days.push(prevDate);
75
+ }
76
+
77
+ // Add current month days
78
+ for (let day = 1; day <= lastDay.getDate(); day++) {
79
+ days.push(new Date(year, month, day));
80
+ }
81
+
82
+ // Add padding days from next month
83
+ const remainingDays = 42 - days.length; // 6 weeks * 7 days
84
+ for (let i = 1; i <= remainingDays; i++) {
85
+ days.push(new Date(year, month + 1, i));
86
+ }
87
+
88
+ return days;
89
+ }
90
+
91
+ const calendarDays = $derived(getDaysInMonth(currentMonth));
92
+
93
+ function isDateDisabled(date: Date): boolean {
94
+ if (min && date < min) return true;
95
+ if (max && date > max) return true;
96
+ return false;
97
+ }
98
+
99
+ function isToday(date: Date): boolean {
100
+ const today = new Date();
101
+ return (
102
+ date.getDate() === today.getDate() &&
103
+ date.getMonth() === today.getMonth() &&
104
+ date.getFullYear() === today.getFullYear()
105
+ );
106
+ }
107
+
108
+ function isSelected(date: Date): boolean {
109
+ if (!value) return false;
110
+ return (
111
+ date.getDate() === value.getDate() &&
112
+ date.getMonth() === value.getMonth() &&
113
+ date.getFullYear() === value.getFullYear()
114
+ );
115
+ }
116
+
117
+ function isCurrentMonth(date: Date): boolean {
118
+ return date.getMonth() === currentMonth.getMonth();
119
+ }
120
+
121
+ function selectDate(date: Date) {
122
+ if (isDateDisabled(date) || disabled) return;
123
+ value = date;
124
+ onchange?.(date);
125
+ isOpen = false;
126
+ inputElement?.focus();
127
+ }
128
+
129
+ function clearDate() {
130
+ value = null;
131
+ onclear?.();
132
+ onchange?.(null);
133
+ inputElement?.focus();
134
+ }
135
+
136
+ function openCalendar() {
137
+ if (disabled) return;
138
+ isOpen = true;
139
+ // Set current month to selected date or today
140
+ currentMonth = value ? new Date(value.getFullYear(), value.getMonth(), 1) : new Date();
141
+ focusedDate = value || new Date();
142
+ }
143
+
144
+ function closeCalendar() {
145
+ isOpen = false;
146
+ focusedDate = null;
147
+ }
148
+
149
+ function previousMonth() {
150
+ currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
151
+ focusedDate = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
152
+ }
153
+
154
+ function nextMonth() {
155
+ currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
156
+ focusedDate = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1);
157
+ }
158
+
159
+ function handleKeyDown(e: KeyboardEvent) {
160
+ if (!isOpen || !focusedDate) return;
161
+
162
+ const currentIndex = calendarDays.findIndex(
163
+ (d) =>
164
+ d.getDate() === focusedDate!.getDate() &&
165
+ d.getMonth() === focusedDate!.getMonth() &&
166
+ d.getFullYear() === focusedDate!.getFullYear()
167
+ );
168
+
169
+ if (currentIndex === -1) return;
170
+
171
+ let newIndex = currentIndex;
172
+
173
+ switch (e.key) {
174
+ case 'ArrowLeft':
175
+ e.preventDefault();
176
+ newIndex = Math.max(0, currentIndex - 1);
177
+ break;
178
+ case 'ArrowRight':
179
+ e.preventDefault();
180
+ newIndex = Math.min(calendarDays.length - 1, currentIndex + 1);
181
+ break;
182
+ case 'ArrowUp':
183
+ e.preventDefault();
184
+ newIndex = Math.max(0, currentIndex - 7);
185
+ break;
186
+ case 'ArrowDown':
187
+ e.preventDefault();
188
+ newIndex = Math.min(calendarDays.length - 1, currentIndex + 7);
189
+ break;
190
+ case 'Enter':
191
+ e.preventDefault();
192
+ selectDate(focusedDate);
193
+ return;
194
+ case 'Escape':
195
+ e.preventDefault();
196
+ closeCalendar();
197
+ return;
198
+ case 'Tab':
199
+ // Allow normal tab navigation
200
+ return;
201
+ }
202
+
203
+ if (newIndex !== currentIndex) {
204
+ focusedDate = calendarDays[newIndex];
205
+ // Change month if necessary
206
+ if (focusedDate.getMonth() !== currentMonth.getMonth()) {
207
+ currentMonth = new Date(focusedDate.getFullYear(), focusedDate.getMonth(), 1);
208
+ }
209
+ }
210
+ }
211
+
212
+ function handleClickOutside(e: MouseEvent) {
213
+ if (!isOpen) return;
214
+ const target = e.target as Node;
215
+ if (
216
+ calendarElement &&
217
+ !calendarElement.contains(target) &&
218
+ inputElement &&
219
+ !inputElement.contains(target)
220
+ ) {
221
+ closeCalendar();
222
+ }
223
+ }
224
+
225
+ onMount(() => {
226
+ document.addEventListener('click', handleClickOutside);
227
+ return () => {
228
+ document.removeEventListener('click', handleClickOutside);
229
+ };
230
+ });
231
+
232
+ const monthYearLabel = $derived(
233
+ new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(currentMonth)
234
+ );
235
+ </script>
236
+
237
+ <div class="datepicker datepicker--{size} {className}">
238
+ <div class="datepicker__input-wrapper">
239
+ <input
240
+ bind:this={inputElement}
241
+ type="text"
242
+ role="combobox"
243
+ class="datepicker__input"
244
+ class:datepicker__input--error={error}
245
+ class:datepicker__input--disabled={disabled}
246
+ value={formattedValue}
247
+ {placeholder}
248
+ {disabled}
249
+ {required}
250
+ readonly
251
+ aria-invalid={error}
252
+ aria-required={required}
253
+ aria-haspopup="dialog"
254
+ aria-expanded={isOpen}
255
+ aria-controls="datepicker-calendar"
256
+ onclick={openCalendar}
257
+ onkeydown={(e) => {
258
+ if (e.key === 'Enter' || e.key === ' ') {
259
+ e.preventDefault();
260
+ openCalendar();
261
+ }
262
+ }}
263
+ />
264
+
265
+ <div class="datepicker__icons">
266
+ {#if value && !disabled}
267
+ <button
268
+ type="button"
269
+ class="datepicker__clear"
270
+ aria-label="Clear date"
271
+ onclick={(e) => {
272
+ e.stopPropagation();
273
+ clearDate();
274
+ }}
275
+ >
276
+ <svg
277
+ width="16"
278
+ height="16"
279
+ viewBox="0 0 16 16"
280
+ fill="none"
281
+ xmlns="http://www.w3.org/2000/svg"
282
+ >
283
+ <path
284
+ d="M12 4L4 12M4 4L12 12"
285
+ stroke="currentColor"
286
+ stroke-width="1.5"
287
+ stroke-linecap="round"
288
+ stroke-linejoin="round"
289
+ />
290
+ </svg>
291
+ </button>
292
+ {/if}
293
+
294
+ <button
295
+ type="button"
296
+ class="datepicker__calendar-icon"
297
+ aria-label="Open calendar"
298
+ {disabled}
299
+ onclick={openCalendar}
300
+ >
301
+ <svg
302
+ width="16"
303
+ height="16"
304
+ viewBox="0 0 16 16"
305
+ fill="none"
306
+ xmlns="http://www.w3.org/2000/svg"
307
+ >
308
+ <path
309
+ d="M12.6667 2.66667H3.33333C2.59695 2.66667 2 3.26362 2 4V13.3333C2 14.0697 2.59695 14.6667 3.33333 14.6667H12.6667C13.403 14.6667 14 14.0697 14 13.3333V4C14 3.26362 13.403 2.66667 12.6667 2.66667Z"
310
+ stroke="currentColor"
311
+ stroke-width="1.5"
312
+ stroke-linecap="round"
313
+ stroke-linejoin="round"
314
+ />
315
+ <path
316
+ d="M10.6667 1.33333V4"
317
+ stroke="currentColor"
318
+ stroke-width="1.5"
319
+ stroke-linecap="round"
320
+ stroke-linejoin="round"
321
+ />
322
+ <path
323
+ d="M5.33333 1.33333V4"
324
+ stroke="currentColor"
325
+ stroke-width="1.5"
326
+ stroke-linecap="round"
327
+ stroke-linejoin="round"
328
+ />
329
+ <path
330
+ d="M2 6.66667H14"
331
+ stroke="currentColor"
332
+ stroke-width="1.5"
333
+ stroke-linecap="round"
334
+ stroke-linejoin="round"
335
+ />
336
+ </svg>
337
+ </button>
338
+ </div>
339
+ </div>
340
+
341
+ {#if isOpen}
342
+ <div
343
+ bind:this={calendarElement}
344
+ id="datepicker-calendar"
345
+ class="datepicker__calendar"
346
+ role="dialog"
347
+ aria-label="Choose a date"
348
+ aria-modal="false"
349
+ tabindex="-1"
350
+ onkeydown={handleKeyDown}
351
+ >
352
+ <div class="datepicker__header">
353
+ <button
354
+ type="button"
355
+ class="datepicker__nav-button"
356
+ aria-label="Previous month"
357
+ onclick={previousMonth}
358
+ tabindex={0}
359
+ >
360
+ <svg
361
+ width="16"
362
+ height="16"
363
+ viewBox="0 0 16 16"
364
+ fill="none"
365
+ xmlns="http://www.w3.org/2000/svg"
366
+ >
367
+ <path
368
+ d="M10 12L6 8L10 4"
369
+ stroke="currentColor"
370
+ stroke-width="1.5"
371
+ stroke-linecap="round"
372
+ stroke-linejoin="round"
373
+ />
374
+ </svg>
375
+ </button>
376
+
377
+ <span class="datepicker__month-year" aria-live="polite">
378
+ {monthYearLabel}
379
+ </span>
380
+
381
+ <button
382
+ type="button"
383
+ class="datepicker__nav-button"
384
+ aria-label="Next month"
385
+ onclick={nextMonth}
386
+ tabindex={0}
387
+ >
388
+ <svg
389
+ width="16"
390
+ height="16"
391
+ viewBox="0 0 16 16"
392
+ fill="none"
393
+ xmlns="http://www.w3.org/2000/svg"
394
+ >
395
+ <path
396
+ d="M6 4L10 8L6 12"
397
+ stroke="currentColor"
398
+ stroke-width="1.5"
399
+ stroke-linecap="round"
400
+ stroke-linejoin="round"
401
+ />
402
+ </svg>
403
+ </button>
404
+ </div>
405
+
406
+ <div class="datepicker__weekdays">
407
+ {#each ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as day}
408
+ <span class="datepicker__weekday" aria-label={day}>{day}</span>
409
+ {/each}
410
+ </div>
411
+
412
+ <div class="datepicker__days">
413
+ {#each calendarDays as day}
414
+ <button
415
+ type="button"
416
+ class="datepicker__day"
417
+ class:datepicker__day--today={isToday(day)}
418
+ class:datepicker__day--selected={isSelected(day)}
419
+ class:datepicker__day--disabled={isDateDisabled(day)}
420
+ class:datepicker__day--outside-month={!isCurrentMonth(day)}
421
+ class:datepicker__day--focused={focusedDate &&
422
+ day.getDate() === focusedDate.getDate() &&
423
+ day.getMonth() === focusedDate.getMonth() &&
424
+ day.getFullYear() === focusedDate.getFullYear()}
425
+ disabled={isDateDisabled(day)}
426
+ aria-label={new Intl.DateTimeFormat(locale, {
427
+ weekday: 'long',
428
+ year: 'numeric',
429
+ month: 'long',
430
+ day: 'numeric'
431
+ }).format(day)}
432
+ aria-current={isSelected(day) ? 'date' : undefined}
433
+ aria-disabled={isDateDisabled(day)}
434
+ tabindex={focusedDate &&
435
+ day.getDate() === focusedDate.getDate() &&
436
+ day.getMonth() === focusedDate.getMonth() &&
437
+ day.getFullYear() === focusedDate.getFullYear()
438
+ ? 0
439
+ : -1}
440
+ onclick={() => selectDate(day)}
441
+ onfocus={() => {
442
+ focusedDate = day;
443
+ }}
444
+ >
445
+ {day.getDate()}
446
+ </button>
447
+ {/each}
448
+ </div>
449
+ </div>
450
+ {/if}
451
+ </div>
452
+
453
+ <style>
454
+ .datepicker {
455
+ position: relative;
456
+ width: 100%;
457
+ }
458
+
459
+ .datepicker__input-wrapper {
460
+ position: relative;
461
+ display: flex;
462
+ align-items: center;
463
+ width: 100%;
464
+ }
465
+
466
+ .datepicker__input {
467
+ width: 100%;
468
+ min-height: var(--touch-target-min, 44px);
469
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
470
+ padding-right: 4rem;
471
+ border: 1px solid var(--color-border, #e5e7eb);
472
+ border-radius: var(--radius-md, 0.375rem);
473
+ background: var(--color-bg, #ffffff);
474
+ color: var(--color-text, #1f2937);
475
+ font-family: inherit;
476
+ font-size: var(--text-sm, 0.875rem);
477
+ line-height: 1.5;
478
+ cursor: pointer;
479
+ transition:
480
+ border-color var(--transition-fast, 150ms ease),
481
+ box-shadow var(--transition-fast, 150ms ease);
482
+ -webkit-tap-highlight-color: transparent;
483
+ }
484
+
485
+ .datepicker__input::placeholder {
486
+ color: var(--color-text-muted, #9ca3af);
487
+ }
488
+
489
+ .datepicker__input:focus {
490
+ outline: none;
491
+ border-color: var(--color-primary, #3b82f6);
492
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
493
+ }
494
+
495
+ .datepicker__input--disabled {
496
+ background: var(--color-bg-muted, #f3f4f6);
497
+ cursor: not-allowed;
498
+ opacity: 0.5;
499
+ }
500
+
501
+ .datepicker__input--error {
502
+ border-color: var(--color-error, #ef4444);
503
+ }
504
+
505
+ .datepicker__input--error:focus {
506
+ border-color: var(--color-error, #ef4444);
507
+ box-shadow: 0 0 0 3px var(--color-error-alpha, rgba(239, 68, 68, 0.1));
508
+ }
509
+
510
+ /* Sizes */
511
+ .datepicker--sm .datepicker__input {
512
+ min-height: var(--touch-target-min, 44px);
513
+ padding: var(--space-xs, 0.25rem) var(--space-sm, 0.5rem);
514
+ padding-right: 3.5rem;
515
+ font-size: var(--text-xs, 0.75rem);
516
+ }
517
+
518
+ .datepicker--md .datepicker__input {
519
+ min-height: var(--touch-target-min, 44px);
520
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
521
+ padding-right: 4rem;
522
+ font-size: var(--text-sm, 0.875rem);
523
+ }
524
+
525
+ .datepicker--lg .datepicker__input {
526
+ min-height: 3.25rem;
527
+ padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
528
+ padding-right: 4.5rem;
529
+ font-size: var(--text-base, 1rem);
530
+ }
531
+
532
+ /* Icons */
533
+ .datepicker__icons {
534
+ position: absolute;
535
+ right: var(--space-md, 1rem);
536
+ display: flex;
537
+ align-items: center;
538
+ gap: var(--space-xs, 0.25rem);
539
+ }
540
+
541
+ .datepicker__clear,
542
+ .datepicker__calendar-icon {
543
+ display: flex;
544
+ align-items: center;
545
+ justify-content: center;
546
+ min-width: var(--touch-target-min, 44px);
547
+ min-height: var(--touch-target-min, 44px);
548
+ padding: var(--space-xs, 0.25rem);
549
+ border: none;
550
+ background: transparent;
551
+ color: var(--color-text-muted, #6b7280);
552
+ cursor: pointer;
553
+ border-radius: var(--radius-sm, 0.25rem);
554
+ transition: background var(--transition-fast, 150ms ease);
555
+ -webkit-tap-highlight-color: transparent;
556
+ }
557
+
558
+ .datepicker__clear:hover,
559
+ .datepicker__calendar-icon:hover {
560
+ background: var(--color-bg-muted, #f3f4f6);
561
+ color: var(--color-text, #1f2937);
562
+ }
563
+
564
+ .datepicker__calendar-icon:disabled {
565
+ cursor: not-allowed;
566
+ opacity: 0.5;
567
+ }
568
+
569
+ /* Calendar */
570
+ .datepicker__calendar {
571
+ position: absolute;
572
+ top: calc(100% + 0.5rem);
573
+ left: 0;
574
+ z-index: 50;
575
+ min-width: 18rem;
576
+ padding: var(--space-md, 1rem);
577
+ border: 1px solid var(--color-border, #e5e7eb);
578
+ border-radius: var(--radius-md, 0.375rem);
579
+ background: var(--color-bg, #ffffff);
580
+ box-shadow: var(--shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));
581
+ animation: calendar-in 150ms ease-out;
582
+ font-family: var(--font-family, system-ui, -apple-system, sans-serif);
583
+ }
584
+
585
+ @keyframes calendar-in {
586
+ from {
587
+ opacity: 0;
588
+ transform: translateY(-4px);
589
+ }
590
+ to {
591
+ opacity: 1;
592
+ transform: translateY(0);
593
+ }
594
+ }
595
+
596
+ .datepicker__header {
597
+ display: flex;
598
+ align-items: center;
599
+ justify-content: space-between;
600
+ margin-bottom: var(--space-md, 1rem);
601
+ }
602
+
603
+ .datepicker__nav-button {
604
+ display: flex;
605
+ align-items: center;
606
+ justify-content: center;
607
+ min-width: var(--touch-target-min, 44px);
608
+ min-height: var(--touch-target-min, 44px);
609
+ padding: var(--space-xs, 0.25rem);
610
+ border: none;
611
+ background: transparent;
612
+ color: var(--color-text, #1f2937);
613
+ cursor: pointer;
614
+ border-radius: var(--radius-sm, 0.25rem);
615
+ transition: background var(--transition-fast, 150ms ease);
616
+ -webkit-tap-highlight-color: transparent;
617
+ }
618
+
619
+ .datepicker__nav-button:hover {
620
+ background: var(--color-bg-muted, #f3f4f6);
621
+ }
622
+
623
+ .datepicker__nav-button:focus {
624
+ outline: none;
625
+ box-shadow: 0 0 0 2px var(--color-primary-alpha, rgba(59, 130, 246, 0.2));
626
+ }
627
+
628
+ .datepicker__month-year {
629
+ font-size: var(--text-sm, 0.875rem);
630
+ font-weight: 600;
631
+ color: var(--color-text, #1f2937);
632
+ }
633
+
634
+ .datepicker__weekdays {
635
+ display: grid;
636
+ grid-template-columns: repeat(7, 1fr);
637
+ gap: var(--space-xs, 0.25rem);
638
+ margin-bottom: var(--space-sm, 0.5rem);
639
+ }
640
+
641
+ .datepicker__weekday {
642
+ display: flex;
643
+ align-items: center;
644
+ justify-content: center;
645
+ padding: var(--space-xs, 0.25rem);
646
+ font-size: var(--text-xs, 0.75rem);
647
+ font-weight: 600;
648
+ color: var(--color-text-muted, #6b7280);
649
+ text-align: center;
650
+ }
651
+
652
+ .datepicker__days {
653
+ display: grid;
654
+ grid-template-columns: repeat(7, 1fr);
655
+ gap: var(--space-xs, 0.25rem);
656
+ }
657
+
658
+ .datepicker__day {
659
+ display: flex;
660
+ align-items: center;
661
+ justify-content: center;
662
+ min-width: var(--touch-target-min, 44px);
663
+ min-height: var(--touch-target-min, 44px);
664
+ padding: var(--space-xs, 0.25rem);
665
+ border: none;
666
+ background: transparent;
667
+ color: var(--color-text, #1f2937);
668
+ font-size: var(--text-sm, 0.875rem);
669
+ cursor: pointer;
670
+ border-radius: var(--radius-sm, 0.25rem);
671
+ transition:
672
+ background var(--transition-fast, 150ms ease),
673
+ color var(--transition-fast, 150ms ease);
674
+ -webkit-tap-highlight-color: transparent;
675
+ }
676
+
677
+ .datepicker__day:hover:not(:disabled) {
678
+ background: var(--color-bg-muted, #f3f4f6);
679
+ }
680
+
681
+ .datepicker__day:focus {
682
+ outline: none;
683
+ }
684
+
685
+ .datepicker__day--focused {
686
+ box-shadow: 0 0 0 2px var(--color-primary-alpha, rgba(59, 130, 246, 0.2));
687
+ }
688
+
689
+ .datepicker__day--today {
690
+ font-weight: 600;
691
+ color: var(--color-primary, #3b82f6);
692
+ }
693
+
694
+ .datepicker__day--selected {
695
+ background: var(--color-primary, #3b82f6);
696
+ color: var(--color-text-inverse, #ffffff);
697
+ font-weight: 600;
698
+ }
699
+
700
+ .datepicker__day--selected:hover {
701
+ background: var(--color-primary, #3b82f6);
702
+ color: var(--color-text-inverse, #ffffff);
703
+ }
704
+
705
+ .datepicker__day--disabled {
706
+ color: var(--color-text-muted, #9ca3af);
707
+ cursor: not-allowed;
708
+ opacity: 0.5;
709
+ }
710
+
711
+ .datepicker__day--disabled:hover {
712
+ background: transparent;
713
+ }
714
+
715
+ .datepicker__day--outside-month {
716
+ color: var(--color-text-muted, #9ca3af);
717
+ opacity: 0.5;
718
+ }
719
+
720
+ /* Mobile responsive */
721
+ @media (max-width: 640px) {
722
+ .datepicker__calendar {
723
+ position: fixed;
724
+ top: auto;
725
+ bottom: 0;
726
+ left: 0;
727
+ right: 0;
728
+ min-width: 100%;
729
+ max-width: 100%;
730
+ border-radius: 1rem 1rem 0 0;
731
+ animation: calendar-slide-up 250ms ease-out;
732
+ padding-bottom: calc(var(--space-md, 1rem) + env(safe-area-inset-bottom, 0));
733
+ }
734
+
735
+ @keyframes calendar-slide-up {
736
+ from {
737
+ opacity: 0;
738
+ transform: translateY(100%);
739
+ }
740
+ to {
741
+ opacity: 1;
742
+ transform: translateY(0);
743
+ }
744
+ }
745
+ }
746
+ </style>