@getmicdrop/svelte-components 5.10.1 → 5.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
- package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
- package/dist/calendar/FAQs/FAQs.svelte +75 -75
- package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
- package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
- package/dist/calendar/PublicCard/PublicCard.svelte +146 -146
- package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
- package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
- package/dist/components/Heading.svelte +60 -60
- package/dist/components/Layout/AppShell.svelte +104 -104
- package/dist/components/Layout/ContentSection.svelte +80 -80
- package/dist/components/Layout/Grid.svelte +4 -4
- package/dist/components/Layout/Heading.svelte +81 -81
- package/dist/components/Layout/PageContainer.svelte +69 -69
- package/dist/components/Layout/Responsive.svelte +75 -75
- package/dist/components/Layout/Section.svelte +80 -80
- package/dist/components/Layout/ShowOnDesktop.svelte +37 -37
- package/dist/components/Layout/ShowOnMobile.svelte +37 -37
- package/dist/components/Layout/Sidebar.svelte +108 -108
- package/dist/components/Layout/Stack.svelte +6 -6
- package/dist/components/Layout/Text.svelte +87 -87
- package/dist/components/Layout/TwoColumn.svelte +108 -108
- package/dist/components/Text.svelte +53 -53
- package/dist/constants/validation.js +91 -91
- package/dist/constants/validation.spec.js +64 -64
- package/dist/index.js +51 -51
- package/dist/patterns/data/DataGrid.svelte +45 -45
- package/dist/patterns/data/DataList.svelte +24 -24
- package/dist/patterns/data/DataTable.svelte +36 -36
- package/dist/patterns/forms/FormActions.spec.js +95 -95
- package/dist/patterns/forms/FormActions.stories.svelte +97 -97
- package/dist/patterns/forms/FormActions.svelte +46 -46
- package/dist/patterns/forms/FormGrid.svelte +33 -33
- package/dist/patterns/forms/FormSection.svelte +32 -32
- package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
- package/dist/patterns/forms/FormValidationSummary.svelte +74 -74
- package/dist/patterns/layout/Sidebar.svelte +39 -39
- package/dist/patterns/layout/index.js +29 -29
- package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
- package/dist/patterns/navigation/BottomNav.svelte +74 -74
- package/dist/patterns/navigation/Header.stories.svelte +77 -77
- package/dist/patterns/navigation/Header.svelte +255 -255
- package/dist/patterns/page/PageHeader.svelte +18 -18
- package/dist/patterns/page/PageLayout.svelte +40 -40
- package/dist/patterns/page/PageLoader.spec.js +57 -57
- package/dist/patterns/page/PageLoader.stories.svelte +137 -137
- package/dist/patterns/page/PageLoader.svelte +24 -24
- package/dist/patterns/page/SectionHeader.svelte +29 -29
- package/dist/presets/badges.js +112 -112
- package/dist/presets/buttons.js +76 -76
- package/dist/presets/index.js +9 -9
- package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
- package/dist/primitives/Accordion/Accordion.svelte +42 -42
- package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
- package/dist/primitives/Alert/Alert.spec.js +173 -173
- package/dist/primitives/Alert/Alert.stories.svelte +88 -88
- package/dist/primitives/Alert/Alert.svelte +27 -27
- package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
- package/dist/primitives/Avatar/Avatar.svelte +66 -66
- package/dist/primitives/Badges/Badge.spec.js +144 -144
- package/dist/primitives/Badges/Badge.stories.svelte +86 -86
- package/dist/primitives/Badges/Badge.svelte +99 -99
- package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -136
- package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
- package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
- package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -122
- package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +98 -98
- package/dist/primitives/Button/Button.spec.js +223 -223
- package/dist/primitives/Button/Button.stories.svelte +76 -76
- package/dist/primitives/Button/Button.svelte +283 -283
- package/dist/primitives/Button/ButtonGroup.svelte +50 -50
- package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -146
- package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
- package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
- package/dist/primitives/Card.spec.js +49 -49
- package/dist/primitives/Card.stories.svelte +22 -22
- package/dist/primitives/Card.svelte +28 -28
- package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
- package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
- package/dist/primitives/DarkModeToggle.spec.js +390 -390
- package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
- package/dist/primitives/DarkModeToggle.svelte +136 -136
- package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
- package/dist/primitives/Drawer/Drawer.svelte +120 -120
- package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
- package/dist/primitives/Dropdown/Dropdown.svelte +170 -170
- package/dist/primitives/Dropdown/DropdownDivider.svelte +9 -9
- package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
- package/dist/primitives/Helper/Helper.svelte +33 -33
- package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
- package/dist/primitives/Icons/ArrowRight.svelte +8 -8
- package/dist/primitives/Icons/Availability.svelte +14 -14
- package/dist/primitives/Icons/Back.svelte +14 -14
- package/dist/primitives/Icons/CheckCircle.svelte +6 -6
- package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
- package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
- package/dist/primitives/Icons/ChevronRight.svelte +4 -4
- package/dist/primitives/Icons/Copy.svelte +15 -15
- package/dist/primitives/Icons/Cross.svelte +5 -5
- package/dist/primitives/Icons/DownArrow.svelte +8 -8
- package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
- package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
- package/dist/primitives/Icons/Home.svelte +15 -15
- package/dist/primitives/Icons/Icon.spec.js +169 -169
- package/dist/primitives/Icons/Icon.stories.svelte +100 -100
- package/dist/primitives/Icons/Icon.svelte +52 -52
- package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
- package/dist/primitives/Icons/Info.svelte +7 -7
- package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
- package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
- package/dist/primitives/Icons/Message.svelte +15 -15
- package/dist/primitives/Icons/MoonIcon.svelte +5 -5
- package/dist/primitives/Icons/More.svelte +21 -21
- package/dist/primitives/Icons/MoreHori.spec.js +61 -61
- package/dist/primitives/Icons/MoreHori.svelte +22 -22
- package/dist/primitives/Icons/Notification.svelte +14 -14
- package/dist/primitives/Icons/Payment.svelte +14 -14
- package/dist/primitives/Icons/Profile.svelte +21 -21
- package/dist/primitives/Icons/Reload.svelte +29 -29
- package/dist/primitives/Icons/Shows.svelte +21 -21
- package/dist/primitives/Icons/Signout.svelte +21 -21
- package/dist/primitives/Icons/SunIcon.svelte +8 -8
- package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
- package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
- package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
- package/dist/primitives/Icons/WarningIcon.svelte +5 -5
- package/dist/primitives/Input/Input.spec.js +573 -573
- package/dist/primitives/Input/Input.stories.svelte +139 -139
- package/dist/primitives/Input/Input.svelte +423 -423
- package/dist/primitives/Input/Select.spec.js +218 -218
- package/dist/primitives/Input/Select.stories.svelte +112 -112
- package/dist/primitives/Input/Select.svelte +252 -252
- package/dist/primitives/Input/Textarea.stories.svelte +137 -137
- package/dist/primitives/Input/Textarea.svelte +105 -105
- package/dist/primitives/Label/Label.svelte +37 -37
- package/dist/primitives/Modal/Modal.spec.js +99 -99
- package/dist/primitives/Modal/Modal.stories.svelte +86 -86
- package/dist/primitives/Modal/Modal.svelte +157 -157
- package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
- package/dist/primitives/Pagination/Pagination.svelte +261 -261
- package/dist/primitives/Radio/Radio.stories.svelte +80 -80
- package/dist/primitives/Radio/Radio.svelte +67 -67
- package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
- package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
- package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
- package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
- package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
- package/dist/primitives/Spinner/Spinner.spec.js +71 -71
- package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
- package/dist/primitives/Spinner/Spinner.svelte +20 -20
- package/dist/primitives/Tabs/TabItem.svelte +49 -49
- package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
- package/dist/primitives/Tabs/Tabs.svelte +137 -137
- package/dist/primitives/Toggle.spec.js +146 -146
- package/dist/primitives/Toggle.stories.svelte +92 -92
- package/dist/primitives/Toggle.svelte +131 -131
- package/dist/primitives/Tooltip/Tooltip.svelte +83 -83
- package/dist/primitives/Typography/Typography.svelte +53 -53
- package/dist/primitives/ValidationError.spec.js +103 -103
- package/dist/primitives/ValidationError.stories.svelte +69 -69
- package/dist/primitives/ValidationError.svelte +29 -29
- package/dist/primitives/index.js +92 -92
- package/dist/recipes/CropImage/CropImage.spec.js +208 -208
- package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
- package/dist/recipes/CropImage/CropImage.svelte +219 -219
- package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
- package/dist/recipes/ImageUploader/ImageUploader.svelte +970 -970
- package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
- package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
- package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
- package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
- package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -133
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
- package/dist/recipes/fields/CheckboxField.svelte +85 -85
- package/dist/recipes/fields/FormField.svelte +58 -58
- package/dist/recipes/fields/RadioGroup.svelte +95 -95
- package/dist/recipes/fields/SelectField.svelte +80 -80
- package/dist/recipes/fields/TextareaField.svelte +97 -97
- package/dist/recipes/fields/ToggleField.svelte +60 -60
- package/dist/recipes/fields/index.js +7 -7
- package/dist/recipes/inputs/MultiSelect.spec.js +260 -260
- package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
- package/dist/recipes/inputs/MultiSelect.svelte +283 -283
- package/dist/recipes/inputs/OTPInput.spec.js +251 -251
- package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
- package/dist/recipes/inputs/OTPInput.svelte +117 -117
- package/dist/recipes/inputs/PasswordInput.svelte +22 -22
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +131 -117
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte.d.ts.map +1 -1
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +344 -344
- package/dist/recipes/inputs/Search.svelte +102 -102
- package/dist/recipes/inputs/SelectDropdown.svelte +171 -171
- package/dist/recipes/modals/AlertModal.svelte +130 -130
- package/dist/recipes/modals/ConfirmationModal.spec.js +206 -206
- package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
- package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
- package/dist/recipes/modals/InputModal.svelte +182 -182
- package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
- package/dist/recipes/modals/ModalStateManager.svelte +77 -77
- package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
- package/dist/recipes/modals/StatusModal.svelte +206 -206
- package/dist/services/EventService.js +75 -75
- package/dist/services/EventService.spec.js +217 -217
- package/dist/services/ShowService.spec.js +345 -345
- package/dist/stores/toaster.js +13 -13
- package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
- package/dist/stories/ButtonAuditReview.svelte +427 -427
- package/dist/stories/PatternsGallery.stories.svelte +19 -19
- package/dist/stories/PatternsGallery.svelte +206 -206
- package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
- package/dist/stories/PrimitivesGallery.svelte +752 -752
- package/dist/stories/RecipesGallery.stories.svelte +19 -19
- package/dist/stories/RecipesGallery.svelte +471 -471
- package/dist/stories/button-audit-manifest.json +11186 -11186
- package/dist/tailwind/preset.cjs +82 -82
- package/dist/telemetry.js +405 -405
- package/dist/telemetry.spec.js +1169 -1169
- package/dist/tokens/tokens.css +87 -87
- package/dist/tokens/typography-base.css +163 -163
- package/dist/tokens/utilities.css +353 -353
- package/dist/utils/apiConfig.spec.js +219 -219
- package/dist/utils/transitions.js +4 -4
- package/dist/utils/utils.js +693 -693
- package/package.json +297 -297
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import { SearchOutline } from "../../primitives/Icons";
|
|
4
|
-
import { typography } from '../../tokens/typography';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
value?: unknown;
|
|
8
|
-
placeholder?: string;
|
|
9
|
-
size?: 'sm' | 'md' | 'lg';
|
|
10
|
-
disabled?: boolean;
|
|
11
|
-
id?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
class?: string;
|
|
14
|
-
oninput?: (event: Event) => void;
|
|
15
|
-
onchange?: (event: Event) => void;
|
|
16
|
-
onkeydown?: (event: KeyboardEvent) => void;
|
|
17
|
-
onkeyup?: (event: KeyboardEvent) => void;
|
|
18
|
-
onfocus?: (event: FocusEvent) => void;
|
|
19
|
-
onblur?: (event: FocusEvent) => void;
|
|
20
|
-
children?: Snippet;
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// API compatible with flowbite-svelte Search
|
|
25
|
-
let {
|
|
26
|
-
value = $bindable(""),
|
|
27
|
-
placeholder = "Search",
|
|
28
|
-
size = "lg",
|
|
29
|
-
disabled = false,
|
|
30
|
-
id = "",
|
|
31
|
-
name = "",
|
|
32
|
-
class: className = "",
|
|
33
|
-
oninput,
|
|
34
|
-
onchange,
|
|
35
|
-
onkeydown,
|
|
36
|
-
onkeyup,
|
|
37
|
-
onfocus,
|
|
38
|
-
onblur,
|
|
39
|
-
children,
|
|
40
|
-
...restProps
|
|
41
|
-
}: Props = $props();
|
|
42
|
-
|
|
43
|
-
const sizes = {
|
|
44
|
-
sm: { icon: "w-3.5 h-3.5", input: `h-8 ${typography.xs} pl-8`, wrapper: "h-8" },
|
|
45
|
-
md: { icon: "w-4 h-4", input: `h-10 ${typography.sm} pl-9`, wrapper: "h-10" },
|
|
46
|
-
lg: { icon: "w-5 h-5", input: `h-11 ${typography.sm} pl-10`, wrapper: "h-11" }
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
let sizeConfig = $derived(sizes[size] || sizes.lg);
|
|
50
|
-
|
|
51
|
-
function handleInput(event: Event) {
|
|
52
|
-
value = (event.target as HTMLInputElement).value;
|
|
53
|
-
oninput?.(event);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function handleChange(event: Event) {
|
|
57
|
-
onchange?.(event);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function handleKeydown(event: KeyboardEvent) {
|
|
61
|
-
onkeydown?.(event);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function handleKeyup(event: KeyboardEvent) {
|
|
65
|
-
onkeyup?.(event);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function handleFocus(event: FocusEvent) {
|
|
69
|
-
onfocus?.(event);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function handleBlur(event: FocusEvent) {
|
|
73
|
-
onblur?.(event);
|
|
74
|
-
}
|
|
75
|
-
</script>
|
|
76
|
-
|
|
77
|
-
<div class="relative w-full {sizeConfig.wrapper} {className}">
|
|
78
|
-
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
|
79
|
-
<SearchOutline class={`${typography.iconMuted} ${sizeConfig.icon}`} />
|
|
80
|
-
</div>
|
|
81
|
-
<input
|
|
82
|
-
type="search"
|
|
83
|
-
{id}
|
|
84
|
-
{name}
|
|
85
|
-
{placeholder}
|
|
86
|
-
{disabled}
|
|
87
|
-
class="w-full pr-3 bg-gray-50 dark:bg-gray-700 font-medium border border-gray-300 dark:border-gray-600 rounded-lg transition-colors focus:outline-hidden focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 focus:border-blue-500 hover:border-blue-500 placeholder-gray-500 dark:placeholder-gray-400 [&::-webkit-search-cancel-button]:appearance-none {sizeConfig.input} {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
88
|
-
bind:value
|
|
89
|
-
oninput={handleInput}
|
|
90
|
-
onchange={handleChange}
|
|
91
|
-
onkeydown={handleKeydown}
|
|
92
|
-
onkeyup={handleKeyup}
|
|
93
|
-
onfocus={handleFocus}
|
|
94
|
-
onblur={handleBlur}
|
|
95
|
-
{...restProps}
|
|
96
|
-
/>
|
|
97
|
-
{#if children}
|
|
98
|
-
<div class={`absolute inset-y-0 right-0 flex items-center ${typography.iconMuted}`}>
|
|
99
|
-
{@render children()}
|
|
100
|
-
</div>
|
|
101
|
-
{/if}
|
|
102
|
-
</div>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { SearchOutline } from "../../primitives/Icons";
|
|
4
|
+
import { typography } from '../../tokens/typography';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
value?: unknown;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
size?: 'sm' | 'md' | 'lg';
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
id?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
oninput?: (event: Event) => void;
|
|
15
|
+
onchange?: (event: Event) => void;
|
|
16
|
+
onkeydown?: (event: KeyboardEvent) => void;
|
|
17
|
+
onkeyup?: (event: KeyboardEvent) => void;
|
|
18
|
+
onfocus?: (event: FocusEvent) => void;
|
|
19
|
+
onblur?: (event: FocusEvent) => void;
|
|
20
|
+
children?: Snippet;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// API compatible with flowbite-svelte Search
|
|
25
|
+
let {
|
|
26
|
+
value = $bindable(""),
|
|
27
|
+
placeholder = "Search",
|
|
28
|
+
size = "lg",
|
|
29
|
+
disabled = false,
|
|
30
|
+
id = "",
|
|
31
|
+
name = "",
|
|
32
|
+
class: className = "",
|
|
33
|
+
oninput,
|
|
34
|
+
onchange,
|
|
35
|
+
onkeydown,
|
|
36
|
+
onkeyup,
|
|
37
|
+
onfocus,
|
|
38
|
+
onblur,
|
|
39
|
+
children,
|
|
40
|
+
...restProps
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
const sizes = {
|
|
44
|
+
sm: { icon: "w-3.5 h-3.5", input: `h-8 ${typography.xs} pl-8`, wrapper: "h-8" },
|
|
45
|
+
md: { icon: "w-4 h-4", input: `h-10 ${typography.sm} pl-9`, wrapper: "h-10" },
|
|
46
|
+
lg: { icon: "w-5 h-5", input: `h-11 ${typography.sm} pl-10`, wrapper: "h-11" }
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let sizeConfig = $derived(sizes[size] || sizes.lg);
|
|
50
|
+
|
|
51
|
+
function handleInput(event: Event) {
|
|
52
|
+
value = (event.target as HTMLInputElement).value;
|
|
53
|
+
oninput?.(event);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleChange(event: Event) {
|
|
57
|
+
onchange?.(event);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
61
|
+
onkeydown?.(event);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handleKeyup(event: KeyboardEvent) {
|
|
65
|
+
onkeyup?.(event);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function handleFocus(event: FocusEvent) {
|
|
69
|
+
onfocus?.(event);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function handleBlur(event: FocusEvent) {
|
|
73
|
+
onblur?.(event);
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<div class="relative w-full {sizeConfig.wrapper} {className}">
|
|
78
|
+
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
|
79
|
+
<SearchOutline class={`${typography.iconMuted} ${sizeConfig.icon}`} />
|
|
80
|
+
</div>
|
|
81
|
+
<input
|
|
82
|
+
type="search"
|
|
83
|
+
{id}
|
|
84
|
+
{name}
|
|
85
|
+
{placeholder}
|
|
86
|
+
{disabled}
|
|
87
|
+
class="w-full pr-3 bg-gray-50 dark:bg-gray-700 font-medium border border-gray-300 dark:border-gray-600 rounded-lg transition-colors focus:outline-hidden focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 focus:border-blue-500 hover:border-blue-500 placeholder-gray-500 dark:placeholder-gray-400 [&::-webkit-search-cancel-button]:appearance-none {sizeConfig.input} {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
88
|
+
bind:value
|
|
89
|
+
oninput={handleInput}
|
|
90
|
+
onchange={handleChange}
|
|
91
|
+
onkeydown={handleKeydown}
|
|
92
|
+
onkeyup={handleKeyup}
|
|
93
|
+
onfocus={handleFocus}
|
|
94
|
+
onblur={handleBlur}
|
|
95
|
+
{...restProps}
|
|
96
|
+
/>
|
|
97
|
+
{#if children}
|
|
98
|
+
<div class={`absolute inset-y-0 right-0 flex items-center ${typography.iconMuted}`}>
|
|
99
|
+
{@render children()}
|
|
100
|
+
</div>
|
|
101
|
+
{/if}
|
|
102
|
+
</div>
|
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { tick } from "svelte";
|
|
3
|
-
import { ChevronDownOutline } from "../../primitives/Icons";
|
|
4
|
-
import { bloom } from "../../utils/transitions.js";
|
|
5
|
-
|
|
6
|
-
interface SelectOption {
|
|
7
|
-
label: string;
|
|
8
|
-
value: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
options?: SelectOption[];
|
|
13
|
-
selected?: SelectOption | null;
|
|
14
|
-
placeholder?: string;
|
|
15
|
-
class?: string;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
onselect?: (option: SelectOption) => void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let {
|
|
21
|
-
options = [],
|
|
22
|
-
selected = $bindable(null),
|
|
23
|
-
placeholder = "Select",
|
|
24
|
-
class: className = "",
|
|
25
|
-
disabled = false,
|
|
26
|
-
onselect,
|
|
27
|
-
}: Props = $props();
|
|
28
|
-
|
|
29
|
-
let isOpen = $state(false);
|
|
30
|
-
let triggerRef = $state<HTMLButtonElement>();
|
|
31
|
-
let menuRef = $state<HTMLDivElement>();
|
|
32
|
-
let containerRef = $state<HTMLDivElement>();
|
|
33
|
-
let focusedIndex = $state(-1);
|
|
34
|
-
let optionElements = $state<HTMLElement[]>([]);
|
|
35
|
-
|
|
36
|
-
async function toggleDropdown() {
|
|
37
|
-
if (disabled) return;
|
|
38
|
-
isOpen = !isOpen;
|
|
39
|
-
if (isOpen) {
|
|
40
|
-
focusedIndex = -1;
|
|
41
|
-
await tick();
|
|
42
|
-
updateOptionElements();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function updateOptionElements() {
|
|
47
|
-
if (menuRef) {
|
|
48
|
-
optionElements = Array.from(menuRef.querySelectorAll('[role="option"]'));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function focusOption(index: number) {
|
|
53
|
-
if (optionElements.length === 0) return;
|
|
54
|
-
if (index < 0) index = optionElements.length - 1;
|
|
55
|
-
if (index >= optionElements.length) index = 0;
|
|
56
|
-
focusedIndex = index;
|
|
57
|
-
optionElements[focusedIndex]?.focus();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function selectOption(option: SelectOption) {
|
|
61
|
-
selected = option;
|
|
62
|
-
onselect?.(option);
|
|
63
|
-
isOpen = false;
|
|
64
|
-
triggerRef?.focus();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// CRITICAL FIX: Click-outside detection using mousedown for reliable detection
|
|
68
|
-
function handleClickOutside(event: MouseEvent) {
|
|
69
|
-
if (!isOpen) return;
|
|
70
|
-
|
|
71
|
-
// Check if click is inside the container (trigger or menu)
|
|
72
|
-
if (containerRef && containerRef.contains(event.target as Node)) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Click was outside - close the dropdown
|
|
77
|
-
isOpen = false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function handleKeyDown(event: KeyboardEvent) {
|
|
81
|
-
if (!isOpen) return;
|
|
82
|
-
|
|
83
|
-
// Only handle if event originated from within this dropdown
|
|
84
|
-
if (!menuRef?.contains(event.target as Node) && !triggerRef?.contains(event.target as Node)) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
switch (event.key) {
|
|
89
|
-
case "Escape":
|
|
90
|
-
isOpen = false;
|
|
91
|
-
triggerRef?.focus();
|
|
92
|
-
event.preventDefault();
|
|
93
|
-
break;
|
|
94
|
-
case "ArrowDown":
|
|
95
|
-
focusOption(focusedIndex + 1);
|
|
96
|
-
event.preventDefault();
|
|
97
|
-
break;
|
|
98
|
-
case "ArrowUp":
|
|
99
|
-
focusOption(focusedIndex - 1);
|
|
100
|
-
event.preventDefault();
|
|
101
|
-
break;
|
|
102
|
-
case "Home":
|
|
103
|
-
focusOption(0);
|
|
104
|
-
event.preventDefault();
|
|
105
|
-
break;
|
|
106
|
-
case "End":
|
|
107
|
-
focusOption(optionElements.length - 1);
|
|
108
|
-
event.preventDefault();
|
|
109
|
-
break;
|
|
110
|
-
case "Tab":
|
|
111
|
-
isOpen = false;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
$effect(() => {
|
|
117
|
-
if (typeof window !== "undefined") {
|
|
118
|
-
// Use mousedown for click-outside to capture before click completes
|
|
119
|
-
document.addEventListener("mousedown", handleClickOutside, true);
|
|
120
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
121
|
-
return () => {
|
|
122
|
-
document.removeEventListener("mousedown", handleClickOutside, true);
|
|
123
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
</script>
|
|
128
|
-
|
|
129
|
-
<div bind:this={containerRef} class="relative inline-block text-left {className}">
|
|
130
|
-
<button
|
|
131
|
-
bind:this={triggerRef}
|
|
132
|
-
type="button"
|
|
133
|
-
class="inline-flex items-center justify-between w-full px-4 py-2.5 text-sm font-medium text-gray-900 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:ring-4 focus:outline-hidden focus:ring-blue-300 dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:hover:bg-gray-600 dark:focus:ring-blue-800 {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
134
|
-
onclick={toggleDropdown}
|
|
135
|
-
aria-haspopup="listbox"
|
|
136
|
-
aria-expanded={isOpen}
|
|
137
|
-
aria-label={placeholder}
|
|
138
|
-
{disabled}
|
|
139
|
-
>
|
|
140
|
-
<span class="truncate">
|
|
141
|
-
{selected?.label || placeholder}
|
|
142
|
-
</span>
|
|
143
|
-
<ChevronDownOutline class="w-4 h-4 ml-2 transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
|
|
144
|
-
</button>
|
|
145
|
-
|
|
146
|
-
{#if isOpen}
|
|
147
|
-
<div
|
|
148
|
-
bind:this={menuRef}
|
|
149
|
-
class="absolute z-10 mt-1 w-full bg-white divide-y divide-gray-100 rounded-lg shadow-lg dark:bg-gray-700 dark:divide-gray-600 max-h-60 overflow-y-auto"
|
|
150
|
-
role="listbox"
|
|
151
|
-
aria-label={placeholder}
|
|
152
|
-
transition:bloom={{ origin: "top left" }}
|
|
153
|
-
>
|
|
154
|
-
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
|
155
|
-
{#each options as option}
|
|
156
|
-
<li>
|
|
157
|
-
<button
|
|
158
|
-
type="button"
|
|
159
|
-
class="block w-full text-left px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white {selected?.value === option.value ? 'bg-blue-50 text-blue-700 dark:bg-gray-600 dark:text-white' : ''}"
|
|
160
|
-
onclick={() => selectOption(option)}
|
|
161
|
-
role="option"
|
|
162
|
-
aria-selected={selected?.value === option.value}
|
|
163
|
-
>
|
|
164
|
-
{option.label}
|
|
165
|
-
</button>
|
|
166
|
-
</li>
|
|
167
|
-
{/each}
|
|
168
|
-
</ul>
|
|
169
|
-
</div>
|
|
170
|
-
{/if}
|
|
171
|
-
</div>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from "svelte";
|
|
3
|
+
import { ChevronDownOutline } from "../../primitives/Icons";
|
|
4
|
+
import { bloom } from "../../utils/transitions.js";
|
|
5
|
+
|
|
6
|
+
interface SelectOption {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
options?: SelectOption[];
|
|
13
|
+
selected?: SelectOption | null;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
onselect?: (option: SelectOption) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
options = [],
|
|
22
|
+
selected = $bindable(null),
|
|
23
|
+
placeholder = "Select",
|
|
24
|
+
class: className = "",
|
|
25
|
+
disabled = false,
|
|
26
|
+
onselect,
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
let isOpen = $state(false);
|
|
30
|
+
let triggerRef = $state<HTMLButtonElement>();
|
|
31
|
+
let menuRef = $state<HTMLDivElement>();
|
|
32
|
+
let containerRef = $state<HTMLDivElement>();
|
|
33
|
+
let focusedIndex = $state(-1);
|
|
34
|
+
let optionElements = $state<HTMLElement[]>([]);
|
|
35
|
+
|
|
36
|
+
async function toggleDropdown() {
|
|
37
|
+
if (disabled) return;
|
|
38
|
+
isOpen = !isOpen;
|
|
39
|
+
if (isOpen) {
|
|
40
|
+
focusedIndex = -1;
|
|
41
|
+
await tick();
|
|
42
|
+
updateOptionElements();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function updateOptionElements() {
|
|
47
|
+
if (menuRef) {
|
|
48
|
+
optionElements = Array.from(menuRef.querySelectorAll('[role="option"]'));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function focusOption(index: number) {
|
|
53
|
+
if (optionElements.length === 0) return;
|
|
54
|
+
if (index < 0) index = optionElements.length - 1;
|
|
55
|
+
if (index >= optionElements.length) index = 0;
|
|
56
|
+
focusedIndex = index;
|
|
57
|
+
optionElements[focusedIndex]?.focus();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function selectOption(option: SelectOption) {
|
|
61
|
+
selected = option;
|
|
62
|
+
onselect?.(option);
|
|
63
|
+
isOpen = false;
|
|
64
|
+
triggerRef?.focus();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// CRITICAL FIX: Click-outside detection using mousedown for reliable detection
|
|
68
|
+
function handleClickOutside(event: MouseEvent) {
|
|
69
|
+
if (!isOpen) return;
|
|
70
|
+
|
|
71
|
+
// Check if click is inside the container (trigger or menu)
|
|
72
|
+
if (containerRef && containerRef.contains(event.target as Node)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Click was outside - close the dropdown
|
|
77
|
+
isOpen = false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
81
|
+
if (!isOpen) return;
|
|
82
|
+
|
|
83
|
+
// Only handle if event originated from within this dropdown
|
|
84
|
+
if (!menuRef?.contains(event.target as Node) && !triggerRef?.contains(event.target as Node)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
switch (event.key) {
|
|
89
|
+
case "Escape":
|
|
90
|
+
isOpen = false;
|
|
91
|
+
triggerRef?.focus();
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
break;
|
|
94
|
+
case "ArrowDown":
|
|
95
|
+
focusOption(focusedIndex + 1);
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
break;
|
|
98
|
+
case "ArrowUp":
|
|
99
|
+
focusOption(focusedIndex - 1);
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
break;
|
|
102
|
+
case "Home":
|
|
103
|
+
focusOption(0);
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
break;
|
|
106
|
+
case "End":
|
|
107
|
+
focusOption(optionElements.length - 1);
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
break;
|
|
110
|
+
case "Tab":
|
|
111
|
+
isOpen = false;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
$effect(() => {
|
|
117
|
+
if (typeof window !== "undefined") {
|
|
118
|
+
// Use mousedown for click-outside to capture before click completes
|
|
119
|
+
document.addEventListener("mousedown", handleClickOutside, true);
|
|
120
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
121
|
+
return () => {
|
|
122
|
+
document.removeEventListener("mousedown", handleClickOutside, true);
|
|
123
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<div bind:this={containerRef} class="relative inline-block text-left {className}">
|
|
130
|
+
<button
|
|
131
|
+
bind:this={triggerRef}
|
|
132
|
+
type="button"
|
|
133
|
+
class="inline-flex items-center justify-between w-full px-4 py-2.5 text-sm font-medium text-gray-900 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:ring-4 focus:outline-hidden focus:ring-blue-300 dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:hover:bg-gray-600 dark:focus:ring-blue-800 {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
134
|
+
onclick={toggleDropdown}
|
|
135
|
+
aria-haspopup="listbox"
|
|
136
|
+
aria-expanded={isOpen}
|
|
137
|
+
aria-label={placeholder}
|
|
138
|
+
{disabled}
|
|
139
|
+
>
|
|
140
|
+
<span class="truncate">
|
|
141
|
+
{selected?.label || placeholder}
|
|
142
|
+
</span>
|
|
143
|
+
<ChevronDownOutline class="w-4 h-4 ml-2 transition-transform duration-200 {isOpen ? 'rotate-180' : ''}" />
|
|
144
|
+
</button>
|
|
145
|
+
|
|
146
|
+
{#if isOpen}
|
|
147
|
+
<div
|
|
148
|
+
bind:this={menuRef}
|
|
149
|
+
class="absolute z-10 mt-1 w-full bg-white divide-y divide-gray-100 rounded-lg shadow-lg dark:bg-gray-700 dark:divide-gray-600 max-h-60 overflow-y-auto"
|
|
150
|
+
role="listbox"
|
|
151
|
+
aria-label={placeholder}
|
|
152
|
+
transition:bloom={{ origin: "top left" }}
|
|
153
|
+
>
|
|
154
|
+
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
|
155
|
+
{#each options as option}
|
|
156
|
+
<li>
|
|
157
|
+
<button
|
|
158
|
+
type="button"
|
|
159
|
+
class="block w-full text-left px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white {selected?.value === option.value ? 'bg-blue-50 text-blue-700 dark:bg-gray-600 dark:text-white' : ''}"
|
|
160
|
+
onclick={() => selectOption(option)}
|
|
161
|
+
role="option"
|
|
162
|
+
aria-selected={selected?.value === option.value}
|
|
163
|
+
>
|
|
164
|
+
{option.label}
|
|
165
|
+
</button>
|
|
166
|
+
</li>
|
|
167
|
+
{/each}
|
|
168
|
+
</ul>
|
|
169
|
+
</div>
|
|
170
|
+
{/if}
|
|
171
|
+
</div>
|