@getmicdrop/svelte-components 5.10.3 → 5.12.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.
- 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.spec.js +1 -1
- 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/datetime/__tests__/format.test.js +1 -1
- package/dist/datetime/__tests__/parse.test.js +1 -1
- package/dist/datetime/__tests__/timezone.test.js +1 -1
- package/dist/datetime/parse.js +1 -1
- package/dist/forms/createFormStore.svelte.js +0 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -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 +123 -122
- package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +99 -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 +314 -283
- package/dist/primitives/Button/Button.svelte.d.ts +2 -0
- package/dist/primitives/Button/Button.svelte.d.ts.map +1 -1
- 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 +174 -157
- package/dist/primitives/Modal/Modal.svelte.d.ts +2 -0
- package/dist/primitives/Modal/Modal.svelte.d.ts.map +1 -1
- 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 +141 -131
- package/dist/primitives/Toggle.svelte.d.ts +2 -0
- package/dist/primitives/Toggle.svelte.d.ts.map +1 -1
- 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 -131
- 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 +162 -152
- package/dist/recipes/modals/ConfirmationModal.svelte.d.ts +2 -0
- package/dist/recipes/modals/ConfirmationModal.svelte.d.ts.map +1 -1
- 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/haptic.d.ts +41 -0
- package/dist/utils/haptic.d.ts.map +1 -0
- package/dist/utils/haptic.js +115 -0
- package/dist/utils/transitions.js +4 -4
- package/dist/utils/utils.js +693 -693
- package/package.json +297 -297
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { ExclamationTriangleOutline } from "../../primitives/Icons";
|
|
3
|
-
import Button from "../../primitives/Button/Button.svelte";
|
|
4
|
-
import Cancel from "../../assets/svg/cancel.svg";
|
|
5
|
-
import Modal from "../../primitives/Modal/Modal.svelte";
|
|
6
|
-
import { typography } from '../../tokens/typography';
|
|
7
|
-
|
|
8
|
-
let {
|
|
9
|
-
show = $bindable(false),
|
|
10
|
-
size = "default", // "small" | "default" | "large"
|
|
11
|
-
title = "",
|
|
12
|
-
description = "",
|
|
13
|
-
closeBtn = false, // To show close button
|
|
14
|
-
persistent = false, // When true, prevents closing by clicking backdrop or pressing Escape
|
|
15
|
-
|
|
16
|
-
// Input configuration
|
|
17
|
-
inputLabel = "",
|
|
18
|
-
inputPlaceholder = "",
|
|
19
|
-
inputType = "text", // text | email | password | textarea
|
|
20
|
-
inputValue = $bindable(""),
|
|
21
|
-
inputRequired = false,
|
|
22
|
-
inputRows = 4, // For textarea
|
|
23
|
-
inputIcon = null, // SVG component for input prefix icon
|
|
24
|
-
helpText = "", // Helper text below input
|
|
25
|
-
|
|
26
|
-
// Validation
|
|
27
|
-
errorMessage = "",
|
|
28
|
-
showError = false,
|
|
29
|
-
validateEmail = false, // Auto-validate email format
|
|
30
|
-
|
|
31
|
-
// Buttons
|
|
32
|
-
primaryButtonText = "Confirm",
|
|
33
|
-
secondaryButtonText = "Cancel",
|
|
34
|
-
primaryButtonVariant = "blue-solid", // blue-solid | red-solid
|
|
35
|
-
|
|
36
|
-
// State
|
|
37
|
-
disabled = false,
|
|
38
|
-
loading = false,
|
|
39
|
-
|
|
40
|
-
// Callbacks
|
|
41
|
-
onconfirm,
|
|
42
|
-
oncancel,
|
|
43
|
-
onclose,
|
|
44
|
-
} = $props();
|
|
45
|
-
|
|
46
|
-
// Email validation
|
|
47
|
-
const isValidEmail = (email) => {
|
|
48
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
49
|
-
return emailRegex.test(email);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
let emailError = $derived(validateEmail && inputValue && !isValidEmail(inputValue));
|
|
53
|
-
let isEmpty = $derived(inputRequired && (!inputValue || inputValue.trim() === ""));
|
|
54
|
-
let hasError = $derived(showError || emailError);
|
|
55
|
-
let isDisabled = $derived(disabled || loading || isEmpty || emailError);
|
|
56
|
-
|
|
57
|
-
const handlePrimaryAction = () => {
|
|
58
|
-
if (!isDisabled) {
|
|
59
|
-
onconfirm?.({ value: inputValue });
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const handleSecondaryAction = () => {
|
|
64
|
-
if (!disabled && !loading) {
|
|
65
|
-
oncancel?.();
|
|
66
|
-
closeModal();
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const closeModal = () => {
|
|
71
|
-
show = false;
|
|
72
|
-
onclose?.();
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const handleClose = () => {
|
|
76
|
-
if (disabled || loading) return;
|
|
77
|
-
closeModal();
|
|
78
|
-
};
|
|
79
|
-
</script>
|
|
80
|
-
|
|
81
|
-
<Modal bind:show {size} {persistent}>
|
|
82
|
-
{#snippet header()}
|
|
83
|
-
<div class="text-left">
|
|
84
|
-
{#if closeBtn}
|
|
85
|
-
<div class="flex justify-end -mt-2 -mr-2 mb-2">
|
|
86
|
-
<Button variant="icon" size="xs" onclick={handleClose} {disabled}>
|
|
87
|
-
<img src={Cancel} alt="Close" class="w-5 h-5" />
|
|
88
|
-
</Button>
|
|
89
|
-
</div>
|
|
90
|
-
{/if}
|
|
91
|
-
{#if title}
|
|
92
|
-
<h3 class={typography.h2}>{title}</h3>
|
|
93
|
-
{/if}
|
|
94
|
-
</div>
|
|
95
|
-
{/snippet}
|
|
96
|
-
|
|
97
|
-
{#snippet body()}
|
|
98
|
-
<div class="text-left mt-4">
|
|
99
|
-
{#if description}
|
|
100
|
-
<p class={`${typography.smMuted} leading-relaxed mb-4`}>
|
|
101
|
-
{description}
|
|
102
|
-
</p>
|
|
103
|
-
{/if}
|
|
104
|
-
|
|
105
|
-
<div>
|
|
106
|
-
{#if inputLabel}
|
|
107
|
-
<label
|
|
108
|
-
for="modal-input"
|
|
109
|
-
class={`${typography.label} block mb-2`}
|
|
110
|
-
>
|
|
111
|
-
{inputLabel}
|
|
112
|
-
</label>
|
|
113
|
-
{/if}
|
|
114
|
-
|
|
115
|
-
{#if inputType === "textarea"}
|
|
116
|
-
<textarea
|
|
117
|
-
id="modal-input"
|
|
118
|
-
bind:value={inputValue}
|
|
119
|
-
placeholder={inputPlaceholder}
|
|
120
|
-
rows={inputRows}
|
|
121
|
-
class="w-full px-3 py-2 border {hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} bg-gray-50 dark:bg-gray-700 {typography.body} rounded-lg focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
|
|
122
|
-
disabled={disabled || loading}
|
|
123
|
-
></textarea>
|
|
124
|
-
{:else}
|
|
125
|
-
<div class="relative">
|
|
126
|
-
{#if inputIcon}
|
|
127
|
-
{@const InputIconComponent = inputIcon}
|
|
128
|
-
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
|
129
|
-
<InputIconComponent class={`w-5 h-5 ${typography.iconMuted}`} />
|
|
130
|
-
</div>
|
|
131
|
-
{/if}
|
|
132
|
-
<input
|
|
133
|
-
id="modal-input"
|
|
134
|
-
type={inputType}
|
|
135
|
-
bind:value={inputValue}
|
|
136
|
-
placeholder={inputPlaceholder}
|
|
137
|
-
required={inputRequired}
|
|
138
|
-
disabled={disabled || loading}
|
|
139
|
-
class="w-full {inputIcon ? 'pl-10' : 'px-3'} py-2 border {hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} rounded-lg {typography.body} bg-white dark:bg-gray-700 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
|
140
|
-
/>
|
|
141
|
-
</div>
|
|
142
|
-
{/if}
|
|
143
|
-
|
|
144
|
-
{#if hasError && (errorMessage || emailError)}
|
|
145
|
-
<p class={`${typography.label} text-red-600 dark:text-red-400 mt-2 flex items-center gap-1`}>
|
|
146
|
-
<ExclamationTriangleOutline class="w-4 h-4" />
|
|
147
|
-
{emailError ? "Please enter a valid email address" : errorMessage}
|
|
148
|
-
</p>
|
|
149
|
-
{:else if helpText && !hasError}
|
|
150
|
-
<p class={`${typography.xsMuted} pt-2`}>
|
|
151
|
-
{helpText}
|
|
152
|
-
</p>
|
|
153
|
-
{/if}
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
{/snippet}
|
|
157
|
-
|
|
158
|
-
{#snippet footer()}
|
|
159
|
-
<div class="flex gap-3">
|
|
160
|
-
{#if secondaryButtonText}
|
|
161
|
-
<Button
|
|
162
|
-
size="full"
|
|
163
|
-
variant="alternative"
|
|
164
|
-
onclick={handleSecondaryAction}
|
|
165
|
-
disabled={disabled || loading}
|
|
166
|
-
>
|
|
167
|
-
{secondaryButtonText}
|
|
168
|
-
</Button>
|
|
169
|
-
{/if}
|
|
170
|
-
|
|
171
|
-
<Button
|
|
172
|
-
size="full"
|
|
173
|
-
variant={primaryButtonVariant}
|
|
174
|
-
onclick={handlePrimaryAction}
|
|
175
|
-
disabled={isDisabled}
|
|
176
|
-
{loading}
|
|
177
|
-
>
|
|
178
|
-
{primaryButtonText}
|
|
179
|
-
</Button>
|
|
180
|
-
</div>
|
|
181
|
-
{/snippet}
|
|
182
|
-
</Modal>
|
|
1
|
+
<script>
|
|
2
|
+
import { ExclamationTriangleOutline } from "../../primitives/Icons";
|
|
3
|
+
import Button from "../../primitives/Button/Button.svelte";
|
|
4
|
+
import Cancel from "../../assets/svg/cancel.svg";
|
|
5
|
+
import Modal from "../../primitives/Modal/Modal.svelte";
|
|
6
|
+
import { typography } from '../../tokens/typography';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
show = $bindable(false),
|
|
10
|
+
size = "default", // "small" | "default" | "large"
|
|
11
|
+
title = "",
|
|
12
|
+
description = "",
|
|
13
|
+
closeBtn = false, // To show close button
|
|
14
|
+
persistent = false, // When true, prevents closing by clicking backdrop or pressing Escape
|
|
15
|
+
|
|
16
|
+
// Input configuration
|
|
17
|
+
inputLabel = "",
|
|
18
|
+
inputPlaceholder = "",
|
|
19
|
+
inputType = "text", // text | email | password | textarea
|
|
20
|
+
inputValue = $bindable(""),
|
|
21
|
+
inputRequired = false,
|
|
22
|
+
inputRows = 4, // For textarea
|
|
23
|
+
inputIcon = null, // SVG component for input prefix icon
|
|
24
|
+
helpText = "", // Helper text below input
|
|
25
|
+
|
|
26
|
+
// Validation
|
|
27
|
+
errorMessage = "",
|
|
28
|
+
showError = false,
|
|
29
|
+
validateEmail = false, // Auto-validate email format
|
|
30
|
+
|
|
31
|
+
// Buttons
|
|
32
|
+
primaryButtonText = "Confirm",
|
|
33
|
+
secondaryButtonText = "Cancel",
|
|
34
|
+
primaryButtonVariant = "blue-solid", // blue-solid | red-solid
|
|
35
|
+
|
|
36
|
+
// State
|
|
37
|
+
disabled = false,
|
|
38
|
+
loading = false,
|
|
39
|
+
|
|
40
|
+
// Callbacks
|
|
41
|
+
onconfirm,
|
|
42
|
+
oncancel,
|
|
43
|
+
onclose,
|
|
44
|
+
} = $props();
|
|
45
|
+
|
|
46
|
+
// Email validation
|
|
47
|
+
const isValidEmail = (email) => {
|
|
48
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
49
|
+
return emailRegex.test(email);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let emailError = $derived(validateEmail && inputValue && !isValidEmail(inputValue));
|
|
53
|
+
let isEmpty = $derived(inputRequired && (!inputValue || inputValue.trim() === ""));
|
|
54
|
+
let hasError = $derived(showError || emailError);
|
|
55
|
+
let isDisabled = $derived(disabled || loading || isEmpty || emailError);
|
|
56
|
+
|
|
57
|
+
const handlePrimaryAction = () => {
|
|
58
|
+
if (!isDisabled) {
|
|
59
|
+
onconfirm?.({ value: inputValue });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleSecondaryAction = () => {
|
|
64
|
+
if (!disabled && !loading) {
|
|
65
|
+
oncancel?.();
|
|
66
|
+
closeModal();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const closeModal = () => {
|
|
71
|
+
show = false;
|
|
72
|
+
onclose?.();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleClose = () => {
|
|
76
|
+
if (disabled || loading) return;
|
|
77
|
+
closeModal();
|
|
78
|
+
};
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<Modal bind:show {size} {persistent}>
|
|
82
|
+
{#snippet header()}
|
|
83
|
+
<div class="text-left">
|
|
84
|
+
{#if closeBtn}
|
|
85
|
+
<div class="flex justify-end -mt-2 -mr-2 mb-2">
|
|
86
|
+
<Button variant="icon" size="xs" onclick={handleClose} {disabled}>
|
|
87
|
+
<img src={Cancel} alt="Close" class="w-5 h-5" />
|
|
88
|
+
</Button>
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
{#if title}
|
|
92
|
+
<h3 class={typography.h2}>{title}</h3>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
95
|
+
{/snippet}
|
|
96
|
+
|
|
97
|
+
{#snippet body()}
|
|
98
|
+
<div class="text-left mt-4">
|
|
99
|
+
{#if description}
|
|
100
|
+
<p class={`${typography.smMuted} leading-relaxed mb-4`}>
|
|
101
|
+
{description}
|
|
102
|
+
</p>
|
|
103
|
+
{/if}
|
|
104
|
+
|
|
105
|
+
<div>
|
|
106
|
+
{#if inputLabel}
|
|
107
|
+
<label
|
|
108
|
+
for="modal-input"
|
|
109
|
+
class={`${typography.label} block mb-2`}
|
|
110
|
+
>
|
|
111
|
+
{inputLabel}
|
|
112
|
+
</label>
|
|
113
|
+
{/if}
|
|
114
|
+
|
|
115
|
+
{#if inputType === "textarea"}
|
|
116
|
+
<textarea
|
|
117
|
+
id="modal-input"
|
|
118
|
+
bind:value={inputValue}
|
|
119
|
+
placeholder={inputPlaceholder}
|
|
120
|
+
rows={inputRows}
|
|
121
|
+
class="w-full px-3 py-2 border {hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} bg-gray-50 dark:bg-gray-700 {typography.body} rounded-lg focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
|
|
122
|
+
disabled={disabled || loading}
|
|
123
|
+
></textarea>
|
|
124
|
+
{:else}
|
|
125
|
+
<div class="relative">
|
|
126
|
+
{#if inputIcon}
|
|
127
|
+
{@const InputIconComponent = inputIcon}
|
|
128
|
+
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
|
129
|
+
<InputIconComponent class={`w-5 h-5 ${typography.iconMuted}`} />
|
|
130
|
+
</div>
|
|
131
|
+
{/if}
|
|
132
|
+
<input
|
|
133
|
+
id="modal-input"
|
|
134
|
+
type={inputType}
|
|
135
|
+
bind:value={inputValue}
|
|
136
|
+
placeholder={inputPlaceholder}
|
|
137
|
+
required={inputRequired}
|
|
138
|
+
disabled={disabled || loading}
|
|
139
|
+
class="w-full {inputIcon ? 'pl-10' : 'px-3'} py-2 border {hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'} rounded-lg {typography.body} bg-white dark:bg-gray-700 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
{/if}
|
|
143
|
+
|
|
144
|
+
{#if hasError && (errorMessage || emailError)}
|
|
145
|
+
<p class={`${typography.label} text-red-600 dark:text-red-400 mt-2 flex items-center gap-1`}>
|
|
146
|
+
<ExclamationTriangleOutline class="w-4 h-4" />
|
|
147
|
+
{emailError ? "Please enter a valid email address" : errorMessage}
|
|
148
|
+
</p>
|
|
149
|
+
{:else if helpText && !hasError}
|
|
150
|
+
<p class={`${typography.xsMuted} pt-2`}>
|
|
151
|
+
{helpText}
|
|
152
|
+
</p>
|
|
153
|
+
{/if}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
{/snippet}
|
|
157
|
+
|
|
158
|
+
{#snippet footer()}
|
|
159
|
+
<div class="flex gap-3">
|
|
160
|
+
{#if secondaryButtonText}
|
|
161
|
+
<Button
|
|
162
|
+
size="full"
|
|
163
|
+
variant="alternative"
|
|
164
|
+
onclick={handleSecondaryAction}
|
|
165
|
+
disabled={disabled || loading}
|
|
166
|
+
>
|
|
167
|
+
{secondaryButtonText}
|
|
168
|
+
</Button>
|
|
169
|
+
{/if}
|
|
170
|
+
|
|
171
|
+
<Button
|
|
172
|
+
size="full"
|
|
173
|
+
variant={primaryButtonVariant}
|
|
174
|
+
onclick={handlePrimaryAction}
|
|
175
|
+
disabled={isDisabled}
|
|
176
|
+
{loading}
|
|
177
|
+
>
|
|
178
|
+
{primaryButtonText}
|
|
179
|
+
</Button>
|
|
180
|
+
</div>
|
|
181
|
+
{/snippet}
|
|
182
|
+
</Modal>
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/svelte";
|
|
2
|
-
import userEvent from "@testing-library/user-event";
|
|
3
|
-
import { expect, describe, test, vi } from "vitest";
|
|
4
|
-
import ModalStateManager from "./ModalStateManager.svelte";
|
|
5
|
-
|
|
6
|
-
describe("ModalStateManager Component Tests", () => {
|
|
7
|
-
test("Shows processing state when isProcessing is true", () => {
|
|
8
|
-
render(ModalStateManager, {
|
|
9
|
-
props: { isProcessing: true, isSuccess: false },
|
|
10
|
-
});
|
|
11
|
-
expect(screen.getByText("Processing your request")).toBeInTheDocument();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("Shows custom processing message", () => {
|
|
15
|
-
render(ModalStateManager, {
|
|
16
|
-
props: {
|
|
17
|
-
isProcessing: true,
|
|
18
|
-
isSuccess: false,
|
|
19
|
-
processingMessage: "Please wait...",
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
expect(screen.getByText("Please wait...")).toBeInTheDocument();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("Shows success state when isSuccess is true", () => {
|
|
26
|
-
render(ModalStateManager, {
|
|
27
|
-
props: { isProcessing: false, isSuccess: true, successTitle: "Done!" },
|
|
28
|
-
});
|
|
29
|
-
expect(screen.getByText("Done!")).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("Shows success message when provided", () => {
|
|
33
|
-
render(ModalStateManager, {
|
|
34
|
-
props: {
|
|
35
|
-
isProcessing: false,
|
|
36
|
-
isSuccess: true,
|
|
37
|
-
successTitle: "Success",
|
|
38
|
-
successMessage: "Your action was completed.",
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
expect(screen.getByText("Your action was completed.")).toBeInTheDocument();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("Shows continue button in success state", () => {
|
|
45
|
-
render(ModalStateManager, {
|
|
46
|
-
props: { isProcessing: false, isSuccess: true },
|
|
47
|
-
});
|
|
48
|
-
expect(screen.getByText("Continue")).toBeInTheDocument();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("Uses custom success button text", () => {
|
|
52
|
-
render(ModalStateManager, {
|
|
53
|
-
props: {
|
|
54
|
-
isProcessing: false,
|
|
55
|
-
isSuccess: true,
|
|
56
|
-
successButtonText: "Close",
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
expect(screen.getByText("Close")).toBeInTheDocument();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("Calls onSuccessClose when success button clicked", async () => {
|
|
63
|
-
const user = userEvent.setup();
|
|
64
|
-
const onSuccessClose = vi.fn();
|
|
65
|
-
|
|
66
|
-
render(ModalStateManager, {
|
|
67
|
-
props: { isProcessing: false, isSuccess: true, onSuccessClose },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
await user.click(screen.getByText("Continue"));
|
|
71
|
-
expect(onSuccessClose).toHaveBeenCalledTimes(1);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("Processing state shows spinner animation", () => {
|
|
75
|
-
const { container } = render(ModalStateManager, {
|
|
76
|
-
props: { isProcessing: true, isSuccess: false },
|
|
77
|
-
});
|
|
78
|
-
const spinner = container.querySelector(".animate-spin");
|
|
79
|
-
expect(spinner).toBeInTheDocument();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("Processing state has orange background", () => {
|
|
83
|
-
const { container } = render(ModalStateManager, {
|
|
84
|
-
props: { isProcessing: true, isSuccess: false },
|
|
85
|
-
});
|
|
86
|
-
const spinnerBg = container.querySelector(".bg-orange-300");
|
|
87
|
-
expect(spinnerBg).toBeInTheDocument();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("Success state priority over processing", () => {
|
|
91
|
-
// When both are true, success should show
|
|
92
|
-
render(ModalStateManager, {
|
|
93
|
-
props: { isProcessing: true, isSuccess: true, successTitle: "Complete" },
|
|
94
|
-
});
|
|
95
|
-
expect(screen.getByText("Complete")).toBeInTheDocument();
|
|
96
|
-
expect(
|
|
97
|
-
screen.queryByText("Processing your request")
|
|
98
|
-
).not.toBeInTheDocument();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
1
|
+
import { render, screen } from "@testing-library/svelte";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { expect, describe, test, vi } from "vitest";
|
|
4
|
+
import ModalStateManager from "./ModalStateManager.svelte";
|
|
5
|
+
|
|
6
|
+
describe("ModalStateManager Component Tests", () => {
|
|
7
|
+
test("Shows processing state when isProcessing is true", () => {
|
|
8
|
+
render(ModalStateManager, {
|
|
9
|
+
props: { isProcessing: true, isSuccess: false },
|
|
10
|
+
});
|
|
11
|
+
expect(screen.getByText("Processing your request")).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("Shows custom processing message", () => {
|
|
15
|
+
render(ModalStateManager, {
|
|
16
|
+
props: {
|
|
17
|
+
isProcessing: true,
|
|
18
|
+
isSuccess: false,
|
|
19
|
+
processingMessage: "Please wait...",
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
expect(screen.getByText("Please wait...")).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Shows success state when isSuccess is true", () => {
|
|
26
|
+
render(ModalStateManager, {
|
|
27
|
+
props: { isProcessing: false, isSuccess: true, successTitle: "Done!" },
|
|
28
|
+
});
|
|
29
|
+
expect(screen.getByText("Done!")).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("Shows success message when provided", () => {
|
|
33
|
+
render(ModalStateManager, {
|
|
34
|
+
props: {
|
|
35
|
+
isProcessing: false,
|
|
36
|
+
isSuccess: true,
|
|
37
|
+
successTitle: "Success",
|
|
38
|
+
successMessage: "Your action was completed.",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
expect(screen.getByText("Your action was completed.")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("Shows continue button in success state", () => {
|
|
45
|
+
render(ModalStateManager, {
|
|
46
|
+
props: { isProcessing: false, isSuccess: true },
|
|
47
|
+
});
|
|
48
|
+
expect(screen.getByText("Continue")).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("Uses custom success button text", () => {
|
|
52
|
+
render(ModalStateManager, {
|
|
53
|
+
props: {
|
|
54
|
+
isProcessing: false,
|
|
55
|
+
isSuccess: true,
|
|
56
|
+
successButtonText: "Close",
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
expect(screen.getByText("Close")).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("Calls onSuccessClose when success button clicked", async () => {
|
|
63
|
+
const user = userEvent.setup();
|
|
64
|
+
const onSuccessClose = vi.fn();
|
|
65
|
+
|
|
66
|
+
render(ModalStateManager, {
|
|
67
|
+
props: { isProcessing: false, isSuccess: true, onSuccessClose },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await user.click(screen.getByText("Continue"));
|
|
71
|
+
expect(onSuccessClose).toHaveBeenCalledTimes(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("Processing state shows spinner animation", () => {
|
|
75
|
+
const { container } = render(ModalStateManager, {
|
|
76
|
+
props: { isProcessing: true, isSuccess: false },
|
|
77
|
+
});
|
|
78
|
+
const spinner = container.querySelector(".animate-spin");
|
|
79
|
+
expect(spinner).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("Processing state has orange background", () => {
|
|
83
|
+
const { container } = render(ModalStateManager, {
|
|
84
|
+
props: { isProcessing: true, isSuccess: false },
|
|
85
|
+
});
|
|
86
|
+
const spinnerBg = container.querySelector(".bg-orange-300");
|
|
87
|
+
expect(spinnerBg).toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("Success state priority over processing", () => {
|
|
91
|
+
// When both are true, success should show
|
|
92
|
+
render(ModalStateManager, {
|
|
93
|
+
props: { isProcessing: true, isSuccess: true, successTitle: "Complete" },
|
|
94
|
+
});
|
|
95
|
+
expect(screen.getByText("Complete")).toBeInTheDocument();
|
|
96
|
+
expect(
|
|
97
|
+
screen.queryByText("Processing your request")
|
|
98
|
+
).not.toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
});
|