@makolabs/ripple 3.0.0 → 3.0.2
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.
- package/dist/elements/accordion/Accordion.svelte +1 -1
- package/dist/elements/combobox/ComboBox.svelte +59 -29
- package/dist/elements/dropdown/Select.svelte +98 -62
- package/dist/elements/dropdown/select.d.ts +3 -108
- package/dist/elements/dropdown/select.js +37 -46
- package/dist/elements/popover/Popover.svelte +59 -36
- package/dist/filters/CompactFilters.svelte +1 -1
- package/dist/forms/Checkbox.svelte +24 -9
- package/dist/forms/DateRange.svelte +236 -204
- package/dist/forms/Input.svelte +18 -18
- package/dist/forms/MarketSelector.svelte +1 -1
- package/dist/forms/NumberInput.svelte +160 -55
- package/dist/forms/RadioGroup.svelte +6 -2
- package/dist/forms/SegmentedControl.svelte +1 -1
- package/dist/forms/Tags.svelte +32 -11
- package/dist/forms/Textarea.svelte +8 -13
- package/dist/forms/Toggle.svelte +22 -14
- package/dist/forms/calendar/Calendar.svelte +107 -8
- package/dist/forms/calendar/calendar-types.d.ts +8 -0
- package/dist/forms/date-picker/DatePicker.svelte +11 -14
- package/dist/forms/form-size.d.ts +37 -0
- package/dist/forms/form-size.js +67 -0
- package/dist/forms/form-types.d.ts +33 -0
- package/dist/forms/month-picker/MonthPicker.svelte +299 -0
- package/dist/forms/month-picker/MonthPicker.svelte.d.ts +4 -0
- package/dist/forms/month-picker/month-picker-types.d.ts +22 -0
- package/dist/forms/month-picker/month-picker-types.js +1 -0
- package/dist/forms/segmented-control.d.ts +2 -2
- package/dist/forms/segmented-control.js +18 -15
- package/dist/forms/slider.js +35 -28
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/layout/activity-list/ActivityList.svelte +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { cn } from '../helper/cls.js';
|
|
3
|
+
import { Size } from '../variants.js';
|
|
4
|
+
import { formSizeTokens } from './form-size.js';
|
|
3
5
|
import type { DateRangeProps } from '../index.js';
|
|
4
6
|
import Portal from '../utils/Portal.svelte';
|
|
5
7
|
import { fly } from 'svelte/transition';
|
|
6
8
|
import { quintOut } from 'svelte/easing';
|
|
9
|
+
import { onMount } from 'svelte';
|
|
7
10
|
|
|
8
11
|
let {
|
|
9
12
|
startDate = $bindable(),
|
|
@@ -17,11 +20,24 @@
|
|
|
17
20
|
endLabel = 'End date',
|
|
18
21
|
format = 'MM/dd/yyyy',
|
|
19
22
|
errors = [],
|
|
23
|
+
size = Size.MD,
|
|
20
24
|
id,
|
|
21
25
|
name,
|
|
22
26
|
onselect
|
|
23
27
|
}: DateRangeProps = $props();
|
|
24
28
|
|
|
29
|
+
const tokens = $derived(formSizeTokens[size]);
|
|
30
|
+
|
|
31
|
+
let isMobile = $state(
|
|
32
|
+
typeof window !== 'undefined' && window.matchMedia('(max-width: 639.98px)').matches
|
|
33
|
+
);
|
|
34
|
+
onMount(() => {
|
|
35
|
+
const mql = window.matchMedia('(max-width: 639.98px)');
|
|
36
|
+
const handler = (e: MediaQueryListEvent) => (isMobile = e.matches);
|
|
37
|
+
mql.addEventListener('change', handler);
|
|
38
|
+
return () => mql.removeEventListener('change', handler);
|
|
39
|
+
});
|
|
40
|
+
|
|
25
41
|
let isOpen = $state(false);
|
|
26
42
|
let hoveredDate = $state<Date | null>(null);
|
|
27
43
|
let datePickerRef = $state<HTMLDivElement | null>(null);
|
|
@@ -239,7 +255,12 @@
|
|
|
239
255
|
{id}
|
|
240
256
|
type="button"
|
|
241
257
|
class={cn(
|
|
242
|
-
'border-default-300 flex w-full items-center justify-between
|
|
258
|
+
'border-default-300 flex w-full items-center justify-between border bg-white',
|
|
259
|
+
tokens.height,
|
|
260
|
+
tokens.padX,
|
|
261
|
+
tokens.text,
|
|
262
|
+
tokens.radius,
|
|
263
|
+
tokens.shadow,
|
|
243
264
|
disabled
|
|
244
265
|
? 'bg-default-100 text-default-400 cursor-not-allowed opacity-50'
|
|
245
266
|
: errors?.length
|
|
@@ -259,7 +280,7 @@
|
|
|
259
280
|
? `${formatDate(startDate)} - Select end date`
|
|
260
281
|
: placeholder}
|
|
261
282
|
</span>
|
|
262
|
-
<svg class=
|
|
283
|
+
<svg class={cn('text-default-400', tokens.iconSize)} viewBox="0 0 20 20" fill="currentColor">
|
|
263
284
|
<path
|
|
264
285
|
fill-rule="evenodd"
|
|
265
286
|
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"
|
|
@@ -271,11 +292,16 @@
|
|
|
271
292
|
{#if startDate || endDate}
|
|
272
293
|
<button
|
|
273
294
|
type="button"
|
|
274
|
-
class=
|
|
295
|
+
class={cn(
|
|
296
|
+
'text-default-400 hover:text-default-500 absolute top-1/2 -translate-y-1/2',
|
|
297
|
+
// Sit just left of the calendar icon; use the token gap
|
|
298
|
+
// so the clear button stays visually tied to the icon.
|
|
299
|
+
'right-8'
|
|
300
|
+
)}
|
|
275
301
|
onclick={clearDates}
|
|
276
302
|
aria-label="Clear dates"
|
|
277
303
|
>
|
|
278
|
-
<svg class=
|
|
304
|
+
<svg class={cn(tokens.iconSize)} viewBox="0 0 20 20" fill="currentColor">
|
|
279
305
|
<path
|
|
280
306
|
fill-rule="evenodd"
|
|
281
307
|
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"
|
|
@@ -294,213 +320,219 @@
|
|
|
294
320
|
</div>
|
|
295
321
|
{/if}
|
|
296
322
|
|
|
297
|
-
{#if isOpen}
|
|
323
|
+
{#if isOpen && !isMobile}
|
|
298
324
|
<Portal target={datePickerRef}>
|
|
299
325
|
<div
|
|
300
326
|
bind:this={calendarRef}
|
|
301
327
|
class="ring-opacity-5 ring-default-300 absolute z-10 mt-1 w-full origin-top-left rounded-md bg-white p-4 shadow-lg ring-1 focus:outline-none"
|
|
302
328
|
transition:fly={{ y: -8, duration: 300, easing: quintOut }}
|
|
303
329
|
>
|
|
304
|
-
|
|
305
|
-
{#if viewMode === 'days'}
|
|
306
|
-
<button
|
|
307
|
-
type="button"
|
|
308
|
-
aria-label="Previous month"
|
|
309
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
310
|
-
onclick={prevMonth}
|
|
311
|
-
>
|
|
312
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
313
|
-
<path
|
|
314
|
-
fill-rule="evenodd"
|
|
315
|
-
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"
|
|
316
|
-
clip-rule="evenodd"
|
|
317
|
-
/>
|
|
318
|
-
</svg>
|
|
319
|
-
</button>
|
|
320
|
-
<button
|
|
321
|
-
type="button"
|
|
322
|
-
class="text-default-700 hover:bg-default-100 inline-flex items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
323
|
-
onclick={showMonths}
|
|
324
|
-
>
|
|
325
|
-
{getMonthName(viewDate.getMonth())}
|
|
326
|
-
{viewDate.getFullYear()}
|
|
327
|
-
</button>
|
|
328
|
-
<button
|
|
329
|
-
type="button"
|
|
330
|
-
aria-label="Next month"
|
|
331
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
332
|
-
onclick={nextMonth}
|
|
333
|
-
>
|
|
334
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
335
|
-
<path
|
|
336
|
-
fill-rule="evenodd"
|
|
337
|
-
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"
|
|
338
|
-
clip-rule="evenodd"
|
|
339
|
-
/>
|
|
340
|
-
</svg>
|
|
341
|
-
</button>
|
|
342
|
-
{:else if viewMode === 'months'}
|
|
343
|
-
<button
|
|
344
|
-
type="button"
|
|
345
|
-
aria-label="Previous year"
|
|
346
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
347
|
-
onclick={() =>
|
|
348
|
-
(viewDate = new Date(viewDate.getFullYear() - 1, viewDate.getMonth(), 1))}
|
|
349
|
-
>
|
|
350
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
351
|
-
<path
|
|
352
|
-
fill-rule="evenodd"
|
|
353
|
-
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"
|
|
354
|
-
clip-rule="evenodd"
|
|
355
|
-
/>
|
|
356
|
-
</svg>
|
|
357
|
-
</button>
|
|
358
|
-
<button
|
|
359
|
-
type="button"
|
|
360
|
-
aria-label="Current year"
|
|
361
|
-
class="text-default-700 inline-flex items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
362
|
-
onclick={showYears}
|
|
363
|
-
>
|
|
364
|
-
{viewDate.getFullYear()}
|
|
365
|
-
</button>
|
|
366
|
-
<button
|
|
367
|
-
type="button"
|
|
368
|
-
aria-label="Next year"
|
|
369
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
370
|
-
onclick={() =>
|
|
371
|
-
(viewDate = new Date(viewDate.getFullYear() + 1, viewDate.getMonth(), 1))}
|
|
372
|
-
>
|
|
373
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
374
|
-
<path
|
|
375
|
-
fill-rule="evenodd"
|
|
376
|
-
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"
|
|
377
|
-
clip-rule="evenodd"
|
|
378
|
-
/>
|
|
379
|
-
</svg>
|
|
380
|
-
</button>
|
|
381
|
-
{:else if viewMode === 'years'}
|
|
382
|
-
<button
|
|
383
|
-
type="button"
|
|
384
|
-
aria-label="Previous year"
|
|
385
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
386
|
-
onclick={() =>
|
|
387
|
-
(viewDate = new Date(viewDate.getFullYear() - 12, viewDate.getMonth(), 1))}
|
|
388
|
-
>
|
|
389
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
390
|
-
<path
|
|
391
|
-
fill-rule="evenodd"
|
|
392
|
-
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"
|
|
393
|
-
clip-rule="evenodd"
|
|
394
|
-
/>
|
|
395
|
-
</svg>
|
|
396
|
-
</button>
|
|
397
|
-
<button
|
|
398
|
-
type="button"
|
|
399
|
-
aria-label="Current year range"
|
|
400
|
-
class="text-default-700 inline-flex items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
401
|
-
>
|
|
402
|
-
{viewDate.getFullYear() - 6} - {viewDate.getFullYear() + 5}
|
|
403
|
-
</button>
|
|
404
|
-
<button
|
|
405
|
-
type="button"
|
|
406
|
-
aria-label="Next year"
|
|
407
|
-
class="text-default-500 hover:bg-default-100 inline-flex items-center rounded-md p-1 text-sm"
|
|
408
|
-
onclick={() =>
|
|
409
|
-
(viewDate = new Date(viewDate.getFullYear() + 12, viewDate.getMonth(), 1))}
|
|
410
|
-
>
|
|
411
|
-
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
412
|
-
<path
|
|
413
|
-
fill-rule="evenodd"
|
|
414
|
-
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"
|
|
415
|
-
clip-rule="evenodd"
|
|
416
|
-
/>
|
|
417
|
-
</svg>
|
|
418
|
-
</button>
|
|
419
|
-
{/if}
|
|
420
|
-
</div>
|
|
421
|
-
|
|
422
|
-
{#if viewMode === 'days'}
|
|
423
|
-
<div class="text-default-500 mb-1 grid grid-cols-7 gap-1 text-center text-xs font-medium">
|
|
424
|
-
<div>Su</div>
|
|
425
|
-
<div>Mo</div>
|
|
426
|
-
<div>Tu</div>
|
|
427
|
-
<div>We</div>
|
|
428
|
-
<div>Th</div>
|
|
429
|
-
<div>Fr</div>
|
|
430
|
-
<div>Sa</div>
|
|
431
|
-
</div>
|
|
432
|
-
<div class="grid grid-cols-7 gap-1">
|
|
433
|
-
{#each getDaysInMonth(viewDate) as { date, isCurrentMonth, isToday, isSelected, isInRange, isDisabled } (date.getTime())}
|
|
434
|
-
<button
|
|
435
|
-
type="button"
|
|
436
|
-
class={cn(
|
|
437
|
-
'flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium',
|
|
438
|
-
isDisabled ? 'text-default-300 cursor-not-allowed' : 'hover:bg-default-100',
|
|
439
|
-
isSelected ? 'bg-primary-500 hover:bg-primary-600 text-white' : '',
|
|
440
|
-
isInRange && !isSelected ? 'bg-primary-100 text-primary-800' : '',
|
|
441
|
-
!isCurrentMonth && !isSelected && !isInRange ? 'text-default-400' : '',
|
|
442
|
-
isToday && !isSelected ? 'border-primary-500 border' : ''
|
|
443
|
-
)}
|
|
444
|
-
onclick={() => handleDateClick(date)}
|
|
445
|
-
onmouseenter={() => handleDateHover(date)}
|
|
446
|
-
disabled={isDisabled}
|
|
447
|
-
>
|
|
448
|
-
{date.getDate()}
|
|
449
|
-
</button>
|
|
450
|
-
{/each}
|
|
451
|
-
</div>
|
|
452
|
-
{:else if viewMode === 'months'}
|
|
453
|
-
<div class="grid grid-cols-3 gap-2">
|
|
454
|
-
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
|
455
|
-
{#each Array(12).fill(0) as _, month (month)}
|
|
456
|
-
<button
|
|
457
|
-
type="button"
|
|
458
|
-
class={cn(
|
|
459
|
-
'flex items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
|
|
460
|
-
viewDate.getMonth() === month
|
|
461
|
-
? 'bg-primary-500 text-white'
|
|
462
|
-
: 'text-default-700 hover:bg-default-100'
|
|
463
|
-
)}
|
|
464
|
-
onclick={() => selectMonth(month)}
|
|
465
|
-
>
|
|
466
|
-
{getMonthName(month).substring(0, 3)}
|
|
467
|
-
</button>
|
|
468
|
-
{/each}
|
|
469
|
-
</div>
|
|
470
|
-
{:else if viewMode === 'years'}
|
|
471
|
-
<div class="grid grid-cols-3 gap-2">
|
|
472
|
-
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
|
473
|
-
{#each Array(12).fill(0) as _, i (i)}
|
|
474
|
-
{@const year = viewDate.getFullYear() - 6 + i}
|
|
475
|
-
<button
|
|
476
|
-
type="button"
|
|
477
|
-
class={cn(
|
|
478
|
-
'flex items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
|
|
479
|
-
viewDate.getFullYear() === year
|
|
480
|
-
? 'bg-primary-500 text-white'
|
|
481
|
-
: 'text-default-700 hover:bg-default-100'
|
|
482
|
-
)}
|
|
483
|
-
onclick={() => selectYear(year)}
|
|
484
|
-
>
|
|
485
|
-
{year}
|
|
486
|
-
</button>
|
|
487
|
-
{/each}
|
|
488
|
-
</div>
|
|
489
|
-
{/if}
|
|
490
|
-
|
|
491
|
-
{#if startDate || endDate}
|
|
492
|
-
<div
|
|
493
|
-
class="border-default-200 text-default-500 mt-4 flex flex-wrap justify-between gap-x-4 gap-y-1 border-t pt-3 text-xs"
|
|
494
|
-
>
|
|
495
|
-
<div>
|
|
496
|
-
{startDate ? `${startLabel}: ${formatDate(startDate)}` : ''}
|
|
497
|
-
</div>
|
|
498
|
-
<div>
|
|
499
|
-
{endDate ? `${endLabel}: ${formatDate(endDate)}` : ''}
|
|
500
|
-
</div>
|
|
501
|
-
</div>
|
|
502
|
-
{/if}
|
|
330
|
+
{@render calendarContent()}
|
|
503
331
|
</div>
|
|
504
332
|
</Portal>
|
|
505
333
|
{/if}
|
|
334
|
+
|
|
335
|
+
{#if isOpen && isMobile}
|
|
336
|
+
<button
|
|
337
|
+
type="button"
|
|
338
|
+
class="fixed inset-0 z-[9998] bg-black/40 backdrop-blur-sm"
|
|
339
|
+
aria-label="Close"
|
|
340
|
+
onclick={() => (isOpen = false)}
|
|
341
|
+
></button>
|
|
342
|
+
<div
|
|
343
|
+
class="fixed inset-x-0 bottom-0 z-[9999] flex max-h-[85vh] min-h-48 flex-col overflow-hidden rounded-t-2xl bg-white shadow-2xl"
|
|
344
|
+
transition:fly={{ y: 300, duration: 200, easing: quintOut }}
|
|
345
|
+
bind:this={calendarRef}
|
|
346
|
+
>
|
|
347
|
+
<div class="flex justify-center py-2">
|
|
348
|
+
<div class="bg-default-300 h-1 w-8 rounded-full"></div>
|
|
349
|
+
</div>
|
|
350
|
+
<div class="flex-1 cursor-pointer overflow-y-auto p-4">
|
|
351
|
+
{@render calendarContent()}
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
{/if}
|
|
506
355
|
</div>
|
|
356
|
+
|
|
357
|
+
{#snippet calendarContent()}
|
|
358
|
+
<div class="mb-2 flex items-center justify-between">
|
|
359
|
+
{#if viewMode === 'days'}
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
aria-label="Previous month"
|
|
363
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
364
|
+
onclick={prevMonth}
|
|
365
|
+
>
|
|
366
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
|
367
|
+
><path
|
|
368
|
+
fill-rule="evenodd"
|
|
369
|
+
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"
|
|
370
|
+
clip-rule="evenodd"
|
|
371
|
+
/></svg
|
|
372
|
+
>
|
|
373
|
+
</button>
|
|
374
|
+
<button
|
|
375
|
+
type="button"
|
|
376
|
+
class="text-default-700 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
377
|
+
onclick={showMonths}
|
|
378
|
+
>
|
|
379
|
+
{getMonthName(viewDate.getMonth())}
|
|
380
|
+
{viewDate.getFullYear()}
|
|
381
|
+
</button>
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
aria-label="Next month"
|
|
385
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
386
|
+
onclick={nextMonth}
|
|
387
|
+
>
|
|
388
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
|
389
|
+
><path
|
|
390
|
+
fill-rule="evenodd"
|
|
391
|
+
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"
|
|
392
|
+
clip-rule="evenodd"
|
|
393
|
+
/></svg
|
|
394
|
+
>
|
|
395
|
+
</button>
|
|
396
|
+
{:else if viewMode === 'months'}
|
|
397
|
+
<button
|
|
398
|
+
type="button"
|
|
399
|
+
aria-label="Previous year"
|
|
400
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
401
|
+
onclick={() => (viewDate = new Date(viewDate.getFullYear() - 1, 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="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"
|
|
407
|
+
clip-rule="evenodd"
|
|
408
|
+
/></svg
|
|
409
|
+
>
|
|
410
|
+
</button>
|
|
411
|
+
<button
|
|
412
|
+
type="button"
|
|
413
|
+
class="text-default-700 inline-flex cursor-pointer items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
414
|
+
onclick={showYears}>{viewDate.getFullYear()}</button
|
|
415
|
+
>
|
|
416
|
+
<button
|
|
417
|
+
type="button"
|
|
418
|
+
aria-label="Next year"
|
|
419
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
420
|
+
onclick={() => (viewDate = new Date(viewDate.getFullYear() + 1, viewDate.getMonth(), 1))}
|
|
421
|
+
>
|
|
422
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
|
423
|
+
><path
|
|
424
|
+
fill-rule="evenodd"
|
|
425
|
+
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"
|
|
426
|
+
clip-rule="evenodd"
|
|
427
|
+
/></svg
|
|
428
|
+
>
|
|
429
|
+
</button>
|
|
430
|
+
{:else if viewMode === 'years'}
|
|
431
|
+
<button
|
|
432
|
+
type="button"
|
|
433
|
+
aria-label="Previous years"
|
|
434
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
435
|
+
onclick={() => (viewDate = new Date(viewDate.getFullYear() - 12, viewDate.getMonth(), 1))}
|
|
436
|
+
>
|
|
437
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
|
438
|
+
><path
|
|
439
|
+
fill-rule="evenodd"
|
|
440
|
+
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"
|
|
441
|
+
clip-rule="evenodd"
|
|
442
|
+
/></svg
|
|
443
|
+
>
|
|
444
|
+
</button>
|
|
445
|
+
<button
|
|
446
|
+
type="button"
|
|
447
|
+
class="text-default-700 inline-flex items-center rounded-md px-2 py-1 text-sm font-medium"
|
|
448
|
+
>{viewDate.getFullYear() - 6} - {viewDate.getFullYear() + 5}</button
|
|
449
|
+
>
|
|
450
|
+
<button
|
|
451
|
+
type="button"
|
|
452
|
+
aria-label="Next years"
|
|
453
|
+
class="text-default-500 hover:bg-default-100 inline-flex cursor-pointer items-center rounded-md p-1 text-sm"
|
|
454
|
+
onclick={() => (viewDate = new Date(viewDate.getFullYear() + 12, viewDate.getMonth(), 1))}
|
|
455
|
+
>
|
|
456
|
+
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"
|
|
457
|
+
><path
|
|
458
|
+
fill-rule="evenodd"
|
|
459
|
+
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"
|
|
460
|
+
clip-rule="evenodd"
|
|
461
|
+
/></svg
|
|
462
|
+
>
|
|
463
|
+
</button>
|
|
464
|
+
{/if}
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
{#if viewMode === 'days'}
|
|
468
|
+
<div class="text-default-500 mb-1 grid grid-cols-7 gap-1 text-center text-xs font-medium">
|
|
469
|
+
<div>Su</div>
|
|
470
|
+
<div>Mo</div>
|
|
471
|
+
<div>Tu</div>
|
|
472
|
+
<div>We</div>
|
|
473
|
+
<div>Th</div>
|
|
474
|
+
<div>Fr</div>
|
|
475
|
+
<div>Sa</div>
|
|
476
|
+
</div>
|
|
477
|
+
<div class="grid grid-cols-7 gap-1">
|
|
478
|
+
{#each getDaysInMonth(viewDate) as { date, isCurrentMonth, isToday, isSelected, isInRange, isDisabled } (date.getTime())}
|
|
479
|
+
<button
|
|
480
|
+
type="button"
|
|
481
|
+
class={cn(
|
|
482
|
+
'flex h-8 w-8 cursor-pointer items-center justify-center rounded-full text-sm font-medium',
|
|
483
|
+
isDisabled ? 'text-default-300 cursor-not-allowed' : 'hover:bg-default-100',
|
|
484
|
+
isSelected ? 'bg-primary-500 hover:bg-primary-600 text-white' : '',
|
|
485
|
+
isInRange && !isSelected ? 'bg-primary-100 text-primary-800' : '',
|
|
486
|
+
!isCurrentMonth && !isSelected && !isInRange ? 'text-default-400' : '',
|
|
487
|
+
isToday && !isSelected ? 'border-primary-500 border' : ''
|
|
488
|
+
)}
|
|
489
|
+
onclick={() => handleDateClick(date)}
|
|
490
|
+
onmouseenter={() => handleDateHover(date)}
|
|
491
|
+
disabled={isDisabled}>{date.getDate()}</button
|
|
492
|
+
>
|
|
493
|
+
{/each}
|
|
494
|
+
</div>
|
|
495
|
+
{:else if viewMode === 'months'}
|
|
496
|
+
<div class="grid grid-cols-3 gap-2">
|
|
497
|
+
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
|
498
|
+
{#each Array(12).fill(0) as _, month (month)}
|
|
499
|
+
<button
|
|
500
|
+
type="button"
|
|
501
|
+
class={cn(
|
|
502
|
+
'flex cursor-pointer items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
|
|
503
|
+
viewDate.getMonth() === month
|
|
504
|
+
? 'bg-primary-500 text-white'
|
|
505
|
+
: 'text-default-700 hover:bg-default-100'
|
|
506
|
+
)}
|
|
507
|
+
onclick={() => selectMonth(month)}>{getMonthName(month).substring(0, 3)}</button
|
|
508
|
+
>
|
|
509
|
+
{/each}
|
|
510
|
+
</div>
|
|
511
|
+
{:else if viewMode === 'years'}
|
|
512
|
+
<div class="grid grid-cols-3 gap-2">
|
|
513
|
+
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
|
514
|
+
{#each Array(12).fill(0) as _, i (i)}
|
|
515
|
+
{@const year = viewDate.getFullYear() - 6 + i}
|
|
516
|
+
<button
|
|
517
|
+
type="button"
|
|
518
|
+
class={cn(
|
|
519
|
+
'flex cursor-pointer items-center justify-center rounded-md px-2 py-1 text-sm font-medium',
|
|
520
|
+
viewDate.getFullYear() === year
|
|
521
|
+
? 'bg-primary-500 text-white'
|
|
522
|
+
: 'text-default-700 hover:bg-default-100'
|
|
523
|
+
)}
|
|
524
|
+
onclick={() => selectYear(year)}>{year}</button
|
|
525
|
+
>
|
|
526
|
+
{/each}
|
|
527
|
+
</div>
|
|
528
|
+
{/if}
|
|
529
|
+
|
|
530
|
+
{#if startDate || endDate}
|
|
531
|
+
<div
|
|
532
|
+
class="border-default-200 text-default-500 mt-4 flex flex-wrap justify-between gap-x-4 gap-y-1 border-t pt-3 text-xs"
|
|
533
|
+
>
|
|
534
|
+
<div>{startDate ? `${startLabel}: ${formatDate(startDate)}` : ''}</div>
|
|
535
|
+
<div>{endDate ? `${endLabel}: ${formatDate(endDate)}` : ''}</div>
|
|
536
|
+
</div>
|
|
537
|
+
{/if}
|
|
538
|
+
{/snippet}
|
package/dist/forms/Input.svelte
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { cn } from '../helper/cls.js';
|
|
3
3
|
import { buildTestId } from '../helper/testid.js';
|
|
4
4
|
import { Size } from '../variants.js';
|
|
5
|
+
import { formSizeTokens } from './form-size.js';
|
|
5
6
|
import type { InputProps } from '../index.js';
|
|
6
7
|
|
|
7
8
|
let {
|
|
@@ -20,26 +21,25 @@
|
|
|
20
21
|
}: InputProps = $props();
|
|
21
22
|
|
|
22
23
|
const BASIC_TYPES = ['text', 'email', 'password', 'number', 'tel', 'url', 'date', 'textarea'];
|
|
24
|
+
const tokens = $derived(formSizeTokens[size]);
|
|
23
25
|
const inputClasses = $derived(
|
|
24
26
|
cn(
|
|
25
|
-
'transition-colors placeholder:text-default-400',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'border-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
!errors.length
|
|
42
|
-
},
|
|
27
|
+
'w-full bg-white transition-colors placeholder:text-default-400',
|
|
28
|
+
tokens.padX,
|
|
29
|
+
tokens.text,
|
|
30
|
+
// All basic types (including textarea) get the bordered look.
|
|
31
|
+
BASIC_TYPES.includes(type) && [
|
|
32
|
+
'border focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2',
|
|
33
|
+
tokens.radius,
|
|
34
|
+
tokens.shadow
|
|
35
|
+
],
|
|
36
|
+
// Single-line types get a fixed height; textarea is content-driven.
|
|
37
|
+
type !== 'textarea' && tokens.height,
|
|
38
|
+
type === 'textarea' && ['resize-y min-h-[100px]', tokens.padY],
|
|
39
|
+
errors.length
|
|
40
|
+
? 'border-danger-300 focus-within:border-danger-500 focus-within:ring-danger-500'
|
|
41
|
+
: 'border-default-300 focus-within:border-primary-500 focus-within:ring-primary-500',
|
|
42
|
+
disabled && 'opacity-50 cursor-not-allowed',
|
|
43
43
|
className
|
|
44
44
|
)
|
|
45
45
|
);
|