@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
|
@@ -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 '../../forms/form-size.js';
|
|
5
6
|
import Popover from '../popover/Popover.svelte';
|
|
6
7
|
import type { ComboBoxProps, ComboBoxItem } from './combobox-types.js';
|
|
7
8
|
|
|
@@ -46,9 +47,7 @@
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
const filteredItems = $derived(
|
|
49
|
-
onsearch
|
|
50
|
-
? items // when server-side, parent controls the filtered list
|
|
51
|
-
: items.filter((item) => (filter ?? defaultFilter)(item, query))
|
|
50
|
+
onsearch ? items : items.filter((item) => (filter ?? defaultFilter)(item, debouncedQuery))
|
|
52
51
|
);
|
|
53
52
|
|
|
54
53
|
// Reset highlight when list shrinks
|
|
@@ -57,19 +56,14 @@
|
|
|
57
56
|
if (highlightedIndex >= filteredItems.length) highlightedIndex = 0;
|
|
58
57
|
});
|
|
59
58
|
|
|
60
|
-
const
|
|
61
|
-
{
|
|
62
|
-
[Size.XS]: 'h-7 text-xs',
|
|
63
|
-
[Size.SM]: 'h-8 text-sm',
|
|
64
|
-
[Size.MD]: 'h-10 text-sm',
|
|
65
|
-
[Size.LG]: 'h-12 text-base',
|
|
66
|
-
[Size.XL]: 'h-14 text-lg',
|
|
67
|
-
[Size.XXL]: 'h-16 text-lg'
|
|
68
|
-
}[size]
|
|
69
|
-
);
|
|
59
|
+
const tokens = $derived(formSizeTokens[size]);
|
|
70
60
|
|
|
71
61
|
function openMenu() {
|
|
72
62
|
if (disabled) return;
|
|
63
|
+
if (skipNextFocusOpen) {
|
|
64
|
+
skipNextFocusOpen = false;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
73
67
|
open = true;
|
|
74
68
|
query = '';
|
|
75
69
|
}
|
|
@@ -86,21 +80,35 @@
|
|
|
86
80
|
closeMenu();
|
|
87
81
|
}
|
|
88
82
|
|
|
83
|
+
let skipNextFocusOpen = false;
|
|
84
|
+
|
|
89
85
|
function clear(e: MouseEvent) {
|
|
90
86
|
e.stopPropagation();
|
|
91
87
|
value = null;
|
|
92
88
|
onchange?.(null);
|
|
89
|
+
skipNextFocusOpen = true;
|
|
93
90
|
inputEl?.focus();
|
|
94
91
|
}
|
|
95
92
|
|
|
93
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
94
|
+
let debouncedQuery = $state('');
|
|
95
|
+
|
|
96
96
|
function handleInput(e: Event) {
|
|
97
97
|
const v = (e.currentTarget as HTMLInputElement).value;
|
|
98
98
|
query = v;
|
|
99
99
|
open = true;
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
clearTimeout(debounceTimer);
|
|
101
|
+
debounceTimer = setTimeout(() => {
|
|
102
|
+
debouncedQuery = v;
|
|
103
|
+
highlightedIndex = 0;
|
|
104
|
+
onsearch?.(v);
|
|
105
|
+
}, 1000);
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
$effect(() => {
|
|
109
|
+
return () => clearTimeout(debounceTimer);
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
function handleKey(e: KeyboardEvent) {
|
|
105
113
|
if (disabled) return;
|
|
106
114
|
if (e.key === 'ArrowDown') {
|
|
@@ -139,9 +147,14 @@
|
|
|
139
147
|
<Popover trigger="manual" bind:open panelClass="w-[var(--cbx-w,20rem)] p-0">
|
|
140
148
|
<div
|
|
141
149
|
class={cn(
|
|
142
|
-
'flex w-full items-center
|
|
150
|
+
'flex w-full items-center border bg-white transition-colors',
|
|
143
151
|
'focus-within:ring-2 focus-within:ring-offset-2 focus-within:outline-none',
|
|
144
|
-
|
|
152
|
+
tokens.height,
|
|
153
|
+
tokens.padX,
|
|
154
|
+
tokens.text,
|
|
155
|
+
tokens.gap,
|
|
156
|
+
tokens.radius,
|
|
157
|
+
tokens.shadow,
|
|
145
158
|
hasErrors
|
|
146
159
|
? 'border-danger-300 focus-within:border-danger-500 focus-within:ring-danger-500'
|
|
147
160
|
: 'border-default-300 focus-within:border-primary-500 focus-within:ring-primary-500',
|
|
@@ -172,7 +185,7 @@
|
|
|
172
185
|
type="button"
|
|
173
186
|
onclick={clear}
|
|
174
187
|
aria-label="Clear selection"
|
|
175
|
-
class="text-default-400 hover:text-default-700 flex size-5 items-center justify-center rounded"
|
|
188
|
+
class="text-default-400 hover:text-default-700 flex size-5 cursor-pointer items-center justify-center rounded"
|
|
176
189
|
>
|
|
177
190
|
<svg class="size-3" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
178
191
|
<path
|
|
@@ -184,18 +197,35 @@
|
|
|
184
197
|
</svg>
|
|
185
198
|
</button>
|
|
186
199
|
{/if}
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
onclick={(e) => {
|
|
203
|
+
e.stopPropagation();
|
|
204
|
+
if (!disabled) {
|
|
205
|
+
if (open) closeMenu();
|
|
206
|
+
else openMenu();
|
|
207
|
+
}
|
|
208
|
+
}}
|
|
209
|
+
class={cn(
|
|
210
|
+
'text-default-400 hover:text-default-700 flex cursor-pointer items-center justify-center rounded',
|
|
211
|
+
!disabled && 'hover:text-default-600'
|
|
212
|
+
)}
|
|
213
|
+
aria-label={open ? 'Close suggestions' : 'Open suggestions'}
|
|
214
|
+
{disabled}
|
|
192
215
|
>
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
216
|
+
<svg
|
|
217
|
+
class={cn('size-4 transition-transform', open && 'rotate-180')}
|
|
218
|
+
viewBox="0 0 20 20"
|
|
219
|
+
fill="currentColor"
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
>
|
|
222
|
+
<path
|
|
223
|
+
fill-rule="evenodd"
|
|
224
|
+
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06z"
|
|
225
|
+
clip-rule="evenodd"
|
|
226
|
+
/>
|
|
227
|
+
</svg>
|
|
228
|
+
</button>
|
|
199
229
|
</div>
|
|
200
230
|
|
|
201
231
|
{#snippet content()}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { tick } from 'svelte';
|
|
2
|
+
import { tick, onMount } from 'svelte';
|
|
3
3
|
import { cn } from '../../helper/cls.js';
|
|
4
4
|
import { buildTestId } from '../../helper/testid.js';
|
|
5
5
|
import { selectTV } from './select.js';
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
import { Size } from '../../variants.js';
|
|
11
11
|
import Portal from '../../utils/Portal.svelte';
|
|
12
12
|
|
|
13
|
+
let isMobile = $state(
|
|
14
|
+
typeof window !== 'undefined' && window.matchMedia('(max-width: 639.98px)').matches
|
|
15
|
+
);
|
|
16
|
+
onMount(() => {
|
|
17
|
+
const mql = window.matchMedia('(max-width: 639.98px)');
|
|
18
|
+
const handler = (e: MediaQueryListEvent) => (isMobile = e.matches);
|
|
19
|
+
mql.addEventListener('change', handler);
|
|
20
|
+
return () => mql.removeEventListener('change', handler);
|
|
21
|
+
});
|
|
22
|
+
|
|
13
23
|
let {
|
|
14
24
|
items = [],
|
|
15
25
|
value = $bindable(''),
|
|
@@ -402,7 +412,7 @@
|
|
|
402
412
|
{/each}
|
|
403
413
|
{/if}
|
|
404
414
|
|
|
405
|
-
{#if open}
|
|
415
|
+
{#if open && !isMobile}
|
|
406
416
|
<Portal target={labelRef}>
|
|
407
417
|
<div
|
|
408
418
|
class={containerClass_}
|
|
@@ -411,66 +421,92 @@
|
|
|
411
421
|
data-testid={buildTestId('select', 'list', testId)}
|
|
412
422
|
transition:fly={{ y: -8, duration: 300, easing: quintOut }}
|
|
413
423
|
>
|
|
414
|
-
{
|
|
415
|
-
<div class={searchInputClass_}>
|
|
416
|
-
<svg
|
|
417
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
418
|
-
width="12"
|
|
419
|
-
height="12"
|
|
420
|
-
viewBox="0 0 12 12"
|
|
421
|
-
class="size-4"
|
|
422
|
-
>
|
|
423
|
-
<path
|
|
424
|
-
fill="currentColor"
|
|
425
|
-
d="M5 1a4 4 0 1 0 2.452 7.16l2.694 2.693a.5.5 0 1 0 .707-.707L8.16 7.453A4 4 0 0 0 5 1M2 5a3 3 0 1 1 6 0a3 3 0 0 1-6 0"
|
|
426
|
-
/>
|
|
427
|
-
</svg>
|
|
428
|
-
<input
|
|
429
|
-
bind:this={searchInputRef}
|
|
430
|
-
bind:value={searchQuery}
|
|
431
|
-
type="text"
|
|
432
|
-
class="w-full ring-0 outline-0"
|
|
433
|
-
placeholder="Search..."
|
|
434
|
-
aria-label="Search select options"
|
|
435
|
-
oninput={() => (hasSearched = true)}
|
|
436
|
-
data-testid={buildTestId('select', 'search', testId)}
|
|
437
|
-
/>
|
|
438
|
-
</div>
|
|
439
|
-
{/if}
|
|
440
|
-
|
|
441
|
-
{#if asyncLoading}
|
|
442
|
-
<div class={emptyMessageClass} data-select-loading="">{loadingText}</div>
|
|
443
|
-
{:else if filteredItems.length === 0}
|
|
444
|
-
<div class={emptyMessageClass}>{emptyText}</div>
|
|
445
|
-
{:else if groupedItems}
|
|
446
|
-
<ul class={listClass_}>
|
|
447
|
-
{#each groupedItems as group, gIdx (group.label ?? `__null__${gIdx}`)}
|
|
448
|
-
{#if group.label !== null}
|
|
449
|
-
<li
|
|
450
|
-
class="text-default-500 bg-default-50 px-3 py-1.5 text-xs font-semibold tracking-wide uppercase"
|
|
451
|
-
role="presentation"
|
|
452
|
-
data-select-group={group.label}
|
|
453
|
-
>
|
|
454
|
-
{group.label}
|
|
455
|
-
</li>
|
|
456
|
-
{/if}
|
|
457
|
-
{#each group.items as groupItem (groupItem.value)}
|
|
458
|
-
{@const flatIdx = filteredItems.indexOf(groupItem)}
|
|
459
|
-
{@const selected = valueArray.includes(groupItem.value)}
|
|
460
|
-
{@const highlighted = flatIdx === highlightedIndex}
|
|
461
|
-
{@render itemButton(groupItem, flatIdx, selected, highlighted)}
|
|
462
|
-
{/each}
|
|
463
|
-
{/each}
|
|
464
|
-
</ul>
|
|
465
|
-
{:else}
|
|
466
|
-
<ul class={listClass_}>
|
|
467
|
-
{#each filteredItems as flatItem, index (flatItem.value)}
|
|
468
|
-
{@const selected = valueArray.includes(flatItem.value)}
|
|
469
|
-
{@const highlighted = index === highlightedIndex}
|
|
470
|
-
{@render itemButton(flatItem, index, selected, highlighted)}
|
|
471
|
-
{/each}
|
|
472
|
-
</ul>
|
|
473
|
-
{/if}
|
|
424
|
+
{@render selectListContent()}
|
|
474
425
|
</div>
|
|
475
426
|
</Portal>
|
|
476
427
|
{/if}
|
|
428
|
+
|
|
429
|
+
{#if open && isMobile}
|
|
430
|
+
<button
|
|
431
|
+
type="button"
|
|
432
|
+
class="fixed inset-0 z-[9998] bg-black/40 backdrop-blur-sm"
|
|
433
|
+
aria-label="Close"
|
|
434
|
+
onclick={() => (open = false)}
|
|
435
|
+
></button>
|
|
436
|
+
<div
|
|
437
|
+
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"
|
|
438
|
+
role="listbox"
|
|
439
|
+
aria-labelledby="{selectId}-label"
|
|
440
|
+
transition:fly={{ y: 300, duration: 200, easing: quintOut }}
|
|
441
|
+
>
|
|
442
|
+
<div class="flex justify-center py-2">
|
|
443
|
+
<div class="bg-default-300 h-1 w-8 rounded-full"></div>
|
|
444
|
+
</div>
|
|
445
|
+
<div class="flex-1 cursor-pointer overflow-y-auto">
|
|
446
|
+
{@render selectListContent()}
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
{/if}
|
|
450
|
+
|
|
451
|
+
{#snippet selectListContent()}
|
|
452
|
+
{#if isSearchable}
|
|
453
|
+
<div class={searchInputClass_}>
|
|
454
|
+
<svg
|
|
455
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
456
|
+
width="12"
|
|
457
|
+
height="12"
|
|
458
|
+
viewBox="0 0 12 12"
|
|
459
|
+
class="size-4"
|
|
460
|
+
>
|
|
461
|
+
<path
|
|
462
|
+
fill="currentColor"
|
|
463
|
+
d="M5 1a4 4 0 1 0 2.452 7.16l2.694 2.693a.5.5 0 1 0 .707-.707L8.16 7.453A4 4 0 0 0 5 1M2 5a3 3 0 1 1 6 0a3 3 0 0 1-6 0"
|
|
464
|
+
/>
|
|
465
|
+
</svg>
|
|
466
|
+
<input
|
|
467
|
+
bind:this={searchInputRef}
|
|
468
|
+
bind:value={searchQuery}
|
|
469
|
+
type="text"
|
|
470
|
+
class="w-full ring-0 outline-0"
|
|
471
|
+
placeholder="Search..."
|
|
472
|
+
aria-label="Search select options"
|
|
473
|
+
oninput={() => (hasSearched = true)}
|
|
474
|
+
data-testid={buildTestId('select', 'search', testId)}
|
|
475
|
+
/>
|
|
476
|
+
</div>
|
|
477
|
+
{/if}
|
|
478
|
+
|
|
479
|
+
{#if asyncLoading}
|
|
480
|
+
<div class={emptyMessageClass} data-select-loading="">{loadingText}</div>
|
|
481
|
+
{:else if filteredItems.length === 0}
|
|
482
|
+
<div class={emptyMessageClass}>{emptyText}</div>
|
|
483
|
+
{:else if groupedItems}
|
|
484
|
+
<ul class={listClass_}>
|
|
485
|
+
{#each groupedItems as group, gIdx (group.label ?? `__null__${gIdx}`)}
|
|
486
|
+
{#if group.label !== null}
|
|
487
|
+
<li
|
|
488
|
+
class="text-default-500 bg-default-50 px-3 py-1.5 text-xs font-semibold tracking-wide uppercase"
|
|
489
|
+
role="presentation"
|
|
490
|
+
data-select-group={group.label}
|
|
491
|
+
>
|
|
492
|
+
{group.label}
|
|
493
|
+
</li>
|
|
494
|
+
{/if}
|
|
495
|
+
{#each group.items as groupItem (groupItem.value)}
|
|
496
|
+
{@const flatIdx = filteredItems.indexOf(groupItem)}
|
|
497
|
+
{@const selected = valueArray.includes(groupItem.value)}
|
|
498
|
+
{@const highlighted = flatIdx === highlightedIndex}
|
|
499
|
+
{@render itemButton(groupItem, flatIdx, selected, highlighted)}
|
|
500
|
+
{/each}
|
|
501
|
+
{/each}
|
|
502
|
+
</ul>
|
|
503
|
+
{:else}
|
|
504
|
+
<ul class={listClass_}>
|
|
505
|
+
{#each filteredItems as flatItem, index (flatItem.value)}
|
|
506
|
+
{@const selected = valueArray.includes(flatItem.value)}
|
|
507
|
+
{@const highlighted = index === highlightedIndex}
|
|
508
|
+
{@render itemButton(flatItem, index, selected, highlighted)}
|
|
509
|
+
{/each}
|
|
510
|
+
</ul>
|
|
511
|
+
{/if}
|
|
512
|
+
{/snippet}
|
|
@@ -1,45 +1,10 @@
|
|
|
1
1
|
export declare const selectTV: import("tailwind-variants").TVReturnType<{
|
|
2
2
|
size: {
|
|
3
|
-
|
|
3
|
+
[k: string]: {
|
|
4
4
|
trigger: string;
|
|
5
5
|
triggerIcon: string;
|
|
6
|
-
container: string;
|
|
7
|
-
item: string;
|
|
8
|
-
base: string;
|
|
9
|
-
};
|
|
10
|
-
sm: {
|
|
11
|
-
trigger: string;
|
|
12
|
-
triggerIcon: string;
|
|
13
|
-
container: string;
|
|
14
6
|
item: string;
|
|
15
|
-
base: string;
|
|
16
|
-
};
|
|
17
|
-
md: {
|
|
18
|
-
trigger: string;
|
|
19
|
-
triggerIcon: string;
|
|
20
7
|
container: string;
|
|
21
|
-
item: string;
|
|
22
|
-
base: string;
|
|
23
|
-
};
|
|
24
|
-
lg: {
|
|
25
|
-
trigger: string;
|
|
26
|
-
triggerIcon: string;
|
|
27
|
-
container: string;
|
|
28
|
-
item: string;
|
|
29
|
-
base: string;
|
|
30
|
-
};
|
|
31
|
-
xl: {
|
|
32
|
-
trigger: string;
|
|
33
|
-
triggerIcon: string;
|
|
34
|
-
container: string;
|
|
35
|
-
item: string;
|
|
36
|
-
base: string;
|
|
37
|
-
};
|
|
38
|
-
"2xl": {
|
|
39
|
-
trigger: string;
|
|
40
|
-
triggerIcon: string;
|
|
41
|
-
container: string;
|
|
42
|
-
item: string;
|
|
43
8
|
base: string;
|
|
44
9
|
};
|
|
45
10
|
};
|
|
@@ -71,46 +36,11 @@ export declare const selectTV: import("tailwind-variants").TVReturnType<{
|
|
|
71
36
|
emptyMessage: string;
|
|
72
37
|
}, undefined, {
|
|
73
38
|
size: {
|
|
74
|
-
|
|
75
|
-
trigger: string;
|
|
76
|
-
triggerIcon: string;
|
|
77
|
-
container: string;
|
|
78
|
-
item: string;
|
|
79
|
-
base: string;
|
|
80
|
-
};
|
|
81
|
-
sm: {
|
|
82
|
-
trigger: string;
|
|
83
|
-
triggerIcon: string;
|
|
84
|
-
container: string;
|
|
85
|
-
item: string;
|
|
86
|
-
base: string;
|
|
87
|
-
};
|
|
88
|
-
md: {
|
|
89
|
-
trigger: string;
|
|
90
|
-
triggerIcon: string;
|
|
91
|
-
container: string;
|
|
92
|
-
item: string;
|
|
93
|
-
base: string;
|
|
94
|
-
};
|
|
95
|
-
lg: {
|
|
39
|
+
[k: string]: {
|
|
96
40
|
trigger: string;
|
|
97
41
|
triggerIcon: string;
|
|
98
|
-
container: string;
|
|
99
42
|
item: string;
|
|
100
|
-
base: string;
|
|
101
|
-
};
|
|
102
|
-
xl: {
|
|
103
|
-
trigger: string;
|
|
104
|
-
triggerIcon: string;
|
|
105
43
|
container: string;
|
|
106
|
-
item: string;
|
|
107
|
-
base: string;
|
|
108
|
-
};
|
|
109
|
-
"2xl": {
|
|
110
|
-
trigger: string;
|
|
111
|
-
triggerIcon: string;
|
|
112
|
-
container: string;
|
|
113
|
-
item: string;
|
|
114
44
|
base: string;
|
|
115
45
|
};
|
|
116
46
|
};
|
|
@@ -142,46 +72,11 @@ export declare const selectTV: import("tailwind-variants").TVReturnType<{
|
|
|
142
72
|
emptyMessage: string;
|
|
143
73
|
}, import("tailwind-variants").TVReturnType<{
|
|
144
74
|
size: {
|
|
145
|
-
|
|
75
|
+
[k: string]: {
|
|
146
76
|
trigger: string;
|
|
147
77
|
triggerIcon: string;
|
|
148
|
-
container: string;
|
|
149
78
|
item: string;
|
|
150
|
-
base: string;
|
|
151
|
-
};
|
|
152
|
-
sm: {
|
|
153
|
-
trigger: string;
|
|
154
|
-
triggerIcon: string;
|
|
155
79
|
container: string;
|
|
156
|
-
item: string;
|
|
157
|
-
base: string;
|
|
158
|
-
};
|
|
159
|
-
md: {
|
|
160
|
-
trigger: string;
|
|
161
|
-
triggerIcon: string;
|
|
162
|
-
container: string;
|
|
163
|
-
item: string;
|
|
164
|
-
base: string;
|
|
165
|
-
};
|
|
166
|
-
lg: {
|
|
167
|
-
trigger: string;
|
|
168
|
-
triggerIcon: string;
|
|
169
|
-
container: string;
|
|
170
|
-
item: string;
|
|
171
|
-
base: string;
|
|
172
|
-
};
|
|
173
|
-
xl: {
|
|
174
|
-
trigger: string;
|
|
175
|
-
triggerIcon: string;
|
|
176
|
-
container: string;
|
|
177
|
-
item: string;
|
|
178
|
-
base: string;
|
|
179
|
-
};
|
|
180
|
-
"2xl": {
|
|
181
|
-
trigger: string;
|
|
182
|
-
triggerIcon: string;
|
|
183
|
-
container: string;
|
|
184
|
-
item: string;
|
|
185
80
|
base: string;
|
|
186
81
|
};
|
|
187
82
|
};
|
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
import { tv } from 'tailwind-variants';
|
|
2
2
|
import { Size } from '../../variants.js';
|
|
3
|
+
import { formSizeTokens } from '../../forms/form-size.js';
|
|
4
|
+
// Build the per-size slot overrides from the canonical form-size tokens
|
|
5
|
+
// so Select coordinates visually with Input/Textarea/NumberInput at the
|
|
6
|
+
// same `size` prop. Container max-heights and base min-widths stay here
|
|
7
|
+
// because they're Select-specific (not tied to field sizing).
|
|
8
|
+
const containerHeights = {
|
|
9
|
+
[Size.XS]: 'max-h-32',
|
|
10
|
+
[Size.SM]: 'max-h-40',
|
|
11
|
+
[Size.MD]: 'max-h-56',
|
|
12
|
+
[Size.LG]: 'max-h-64',
|
|
13
|
+
[Size.XL]: 'max-h-72',
|
|
14
|
+
[Size.XXL]: 'max-h-80'
|
|
15
|
+
};
|
|
16
|
+
const minWidths = {
|
|
17
|
+
[Size.XS]: 'min-w-20',
|
|
18
|
+
[Size.SM]: 'min-w-28',
|
|
19
|
+
[Size.MD]: 'min-w-32',
|
|
20
|
+
[Size.LG]: 'min-w-40',
|
|
21
|
+
[Size.XL]: 'min-w-48',
|
|
22
|
+
[Size.XXL]: 'min-w-56'
|
|
23
|
+
};
|
|
24
|
+
const sizeVariants = Object.fromEntries(Object.keys(formSizeTokens).map((key) => {
|
|
25
|
+
const t = formSizeTokens[key];
|
|
26
|
+
return [
|
|
27
|
+
key,
|
|
28
|
+
{
|
|
29
|
+
trigger: `${t.height} ${t.padX} ${t.padY} ${t.text} ${t.gap} ${t.radius} ${t.shadow}`,
|
|
30
|
+
triggerIcon: t.iconSize,
|
|
31
|
+
item: `${t.padX} ${t.padY} ${t.text}`,
|
|
32
|
+
container: containerHeights[key],
|
|
33
|
+
base: minWidths[key]
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
}));
|
|
3
37
|
export const selectTV = tv({
|
|
4
38
|
slots: {
|
|
5
39
|
base: 'w-full',
|
|
6
40
|
trigger: `relative flex items-center justify-between w-full text-left bg-white border
|
|
7
|
-
border-default-300
|
|
41
|
+
border-default-300 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:border-primary-500 focus-within:ring-primary-500 cursor-pointer transition-colors hover:border-default-400`,
|
|
8
42
|
triggerIcon: 'transition-transform duration-200 text-default-500',
|
|
9
43
|
container: 'absolute z-50 w-full mt-1 bg-white overflow-clip border border-default-200 rounded-md shadow-sm origin-top-left top-full left-0 mt-2',
|
|
10
44
|
searchInput: 'flex items-center gap-x-3 w-full outline-none px-2 h-10 border-b border-b-default-200',
|
|
11
45
|
list: 'py-1 max-h-60 overflow-x-clip overflow-y-auto h-full',
|
|
12
|
-
item: `w-full
|
|
46
|
+
item: `w-full text-left
|
|
13
47
|
data-[highlighted=true]:bg-default-100 data-[highlighted=true]:text-default-700
|
|
14
48
|
data-[selected=true]:bg-info-100 data-[selected=true]:text-info-500 data-[selected=true]:font-medium
|
|
15
49
|
data-[selected=true]:data-[highlighted=true]:bg-info-200 data-[selected=true]:data-[highlighted=true]:text-info-500
|
|
@@ -17,50 +51,7 @@ export const selectTV = tv({
|
|
|
17
51
|
emptyMessage: 'px-3 py-2 text-sm text-default-500'
|
|
18
52
|
},
|
|
19
53
|
variants: {
|
|
20
|
-
size:
|
|
21
|
-
[Size.XS]: {
|
|
22
|
-
trigger: 'h-6 px-2 py-1 text-xs gap-1',
|
|
23
|
-
triggerIcon: 'h-3 w-3',
|
|
24
|
-
container: 'max-h-40',
|
|
25
|
-
item: 'px-2 py-1 text-xs',
|
|
26
|
-
base: 'min-w-24'
|
|
27
|
-
},
|
|
28
|
-
[Size.SM]: {
|
|
29
|
-
trigger: 'h-8 px-3 py-2 text-sm gap-1.5',
|
|
30
|
-
triggerIcon: 'h-3.5 w-3.5',
|
|
31
|
-
container: 'max-h-48',
|
|
32
|
-
item: 'px-2.5 py-1.5 text-xs',
|
|
33
|
-
base: 'min-w-32'
|
|
34
|
-
},
|
|
35
|
-
[Size.MD]: {
|
|
36
|
-
trigger: 'h-10 px-3 py-2 text-base gap-2',
|
|
37
|
-
triggerIcon: 'h-4 w-4',
|
|
38
|
-
container: 'max-h-60',
|
|
39
|
-
item: 'px-3 py-2 text-sm',
|
|
40
|
-
base: 'min-w-40'
|
|
41
|
-
},
|
|
42
|
-
[Size.LG]: {
|
|
43
|
-
trigger: 'h-12 px-3 py-2 text-lg gap-2.5',
|
|
44
|
-
triggerIcon: 'h-5 w-5',
|
|
45
|
-
container: 'max-h-72',
|
|
46
|
-
item: 'px-4 py-2.5 text-base',
|
|
47
|
-
base: 'min-w-48'
|
|
48
|
-
},
|
|
49
|
-
[Size.XL]: {
|
|
50
|
-
trigger: 'h-12 px-5 py-3 text-lg gap-3',
|
|
51
|
-
triggerIcon: 'h-6 w-6',
|
|
52
|
-
container: 'max-h-80',
|
|
53
|
-
item: 'px-5 py-3 text-lg',
|
|
54
|
-
base: 'min-w-56'
|
|
55
|
-
},
|
|
56
|
-
[Size.XXL]: {
|
|
57
|
-
trigger: 'h-14 px-6 py-3.5 text-xl gap-4',
|
|
58
|
-
triggerIcon: 'h-7 w-7',
|
|
59
|
-
container: 'max-h-96',
|
|
60
|
-
item: 'px-6 py-3.5 text-xl',
|
|
61
|
-
base: 'min-w-64'
|
|
62
|
-
}
|
|
63
|
-
},
|
|
54
|
+
size: sizeVariants,
|
|
64
55
|
disabled: {
|
|
65
56
|
true: {
|
|
66
57
|
trigger: 'opacity-50 cursor-not-allowed hover:border-default-300',
|