@marianmeres/stuic 3.21.3 → 3.23.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/components/Checkout/CheckoutCompleteStep.svelte +9 -0
- package/dist/components/Checkout/CheckoutCompleteStep.svelte.d.ts +3 -0
- package/dist/components/Checkout/CheckoutConfirmStep.svelte +15 -0
- package/dist/components/Checkout/CheckoutConfirmStep.svelte.d.ts +3 -0
- package/dist/components/Checkout/CheckoutGuestForm.svelte +5 -0
- package/dist/components/Checkout/CheckoutGuestForm.svelte.d.ts +3 -0
- package/dist/components/Checkout/CheckoutGuestOrLoginForm.svelte +96 -7
- package/dist/components/Checkout/CheckoutGuestOrLoginForm.svelte.d.ts +10 -0
- package/dist/components/Checkout/CheckoutLoginForm.svelte +33 -137
- package/dist/components/Checkout/CheckoutLoginForm.svelte.d.ts +3 -0
- package/dist/components/Checkout/CheckoutReviewStep.svelte +10 -0
- package/dist/components/Checkout/CheckoutReviewStep.svelte.d.ts +3 -0
- package/dist/components/Checkout/CheckoutShippingStep.svelte +9 -0
- package/dist/components/Checkout/CheckoutShippingStep.svelte.d.ts +3 -0
- package/dist/components/Checkout/_internal/checkout-i18n-defaults.js +3 -0
- package/dist/components/Checkout/_internal/checkout-types.d.ts +1 -0
- package/dist/components/Checkout/_internal/checkout-utils.js +1 -1
- package/dist/components/Checkout/_login-form.css +22 -61
- package/dist/components/Input/FieldObject.svelte +316 -0
- package/dist/components/Input/FieldObject.svelte.d.ts +35 -0
- package/dist/components/Input/index.d.ts +1 -0
- package/dist/components/Input/index.js +1 -0
- package/dist/components/LoginForm/LoginForm.svelte +295 -0
- package/dist/components/LoginForm/LoginForm.svelte.d.ts +64 -0
- package/dist/components/LoginForm/LoginFormModal.svelte +200 -0
- package/dist/components/LoginForm/LoginFormModal.svelte.d.ts +76 -0
- package/dist/components/LoginForm/_internal/login-form-i18n-defaults.d.ts +1 -0
- package/dist/components/LoginForm/_internal/login-form-i18n-defaults.js +26 -0
- package/dist/components/LoginForm/_internal/login-form-types.d.ts +9 -0
- package/dist/components/LoginForm/_internal/login-form-types.js +1 -0
- package/dist/components/LoginForm/_internal/login-form-utils.d.ts +4 -0
- package/dist/components/LoginForm/_internal/login-form-utils.js +18 -0
- package/dist/components/LoginForm/index.css +83 -0
- package/dist/components/LoginForm/index.d.ts +3 -0
- package/dist/components/LoginForm/index.js +2 -0
- package/dist/components/Modal/Modal.svelte +6 -1
- package/dist/components/Modal/Modal.svelte.d.ts +1 -0
- package/dist/components/Modal/index.css +1 -8
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { HTMLAttributes } from "svelte/elements";
|
|
4
4
|
import type { TranslateFn } from "../../types.js";
|
|
5
5
|
import type { CheckoutOrderData, CheckoutStep } from "./_internal/checkout-types.js";
|
|
6
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
6
7
|
|
|
7
8
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
8
9
|
/** Completed order data */
|
|
@@ -57,6 +58,9 @@
|
|
|
57
58
|
/** Additional content after the confirmation */
|
|
58
59
|
confirmationFooter?: Snippet<[{ orderId: string; order: CheckoutOrderData }]>;
|
|
59
60
|
|
|
61
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
62
|
+
notifications?: NotificationsStack;
|
|
63
|
+
|
|
60
64
|
t?: TranslateFn;
|
|
61
65
|
unstyled?: boolean;
|
|
62
66
|
class?: string;
|
|
@@ -79,6 +83,7 @@
|
|
|
79
83
|
emailSent,
|
|
80
84
|
isLoading = false,
|
|
81
85
|
error,
|
|
86
|
+
notifications,
|
|
82
87
|
hideProgress = false,
|
|
83
88
|
currentStep = "complete",
|
|
84
89
|
steps,
|
|
@@ -99,6 +104,10 @@
|
|
|
99
104
|
|
|
100
105
|
let t = $derived(tProp ?? t_default);
|
|
101
106
|
|
|
107
|
+
$effect(() => {
|
|
108
|
+
if (error && notifications) notifications.error(error);
|
|
109
|
+
});
|
|
110
|
+
|
|
102
111
|
let _class = $derived(
|
|
103
112
|
unstyled ? classProp : twMerge("stuic-checkout-complete-step", classProp)
|
|
104
113
|
);
|
|
@@ -2,6 +2,7 @@ import type { Snippet } from "svelte";
|
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { CheckoutOrderData, CheckoutStep } from "./_internal/checkout-types.js";
|
|
5
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
6
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
6
7
|
/** Completed order data */
|
|
7
8
|
order: CheckoutOrderData;
|
|
@@ -38,6 +39,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
|
|
|
38
39
|
orderId: string;
|
|
39
40
|
order: CheckoutOrderData;
|
|
40
41
|
}]>;
|
|
42
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
43
|
+
notifications?: NotificationsStack;
|
|
41
44
|
t?: TranslateFn;
|
|
42
45
|
unstyled?: boolean;
|
|
43
46
|
class?: string;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
CheckoutStep,
|
|
8
8
|
CheckoutValidationError,
|
|
9
9
|
} from "./_internal/checkout-types.js";
|
|
10
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
10
11
|
|
|
11
12
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
12
13
|
/** Order data to review and display totals */
|
|
@@ -73,6 +74,9 @@
|
|
|
73
74
|
/** Override entire right column */
|
|
74
75
|
rightColumn?: Snippet;
|
|
75
76
|
|
|
77
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
78
|
+
notifications?: NotificationsStack;
|
|
79
|
+
|
|
76
80
|
t?: TranslateFn;
|
|
77
81
|
unstyled?: boolean;
|
|
78
82
|
class?: string;
|
|
@@ -100,6 +104,7 @@
|
|
|
100
104
|
isLoading = false,
|
|
101
105
|
error,
|
|
102
106
|
validationErrors,
|
|
107
|
+
notifications,
|
|
103
108
|
isValid = true,
|
|
104
109
|
isSubmitting = false,
|
|
105
110
|
hideProgress = false,
|
|
@@ -129,6 +134,16 @@
|
|
|
129
134
|
|
|
130
135
|
let t = $derived(tProp ?? t_default);
|
|
131
136
|
|
|
137
|
+
$effect(() => {
|
|
138
|
+
if (error && notifications) notifications.error(error);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
$effect(() => {
|
|
142
|
+
if (validationErrors && validationErrors.length > 0 && notifications) {
|
|
143
|
+
notifications.error(validationErrors.map((e) => e.message).join(", "));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
132
147
|
let _class = $derived(
|
|
133
148
|
unstyled ? classProp : twMerge("stuic-checkout-confirm-step", classProp)
|
|
134
149
|
);
|
|
@@ -2,6 +2,7 @@ import type { Snippet } from "svelte";
|
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { CheckoutOrderData, CheckoutStep, CheckoutValidationError } from "./_internal/checkout-types.js";
|
|
5
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
6
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
6
7
|
/** Order data to review and display totals */
|
|
7
8
|
order: CheckoutOrderData;
|
|
@@ -38,6 +39,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
|
|
|
38
39
|
leftColumn?: Snippet;
|
|
39
40
|
/** Override entire right column */
|
|
40
41
|
rightColumn?: Snippet;
|
|
42
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
43
|
+
notifications?: NotificationsStack;
|
|
41
44
|
t?: TranslateFn;
|
|
42
45
|
unstyled?: boolean;
|
|
43
46
|
class?: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
CheckoutCustomerFormData,
|
|
7
7
|
CheckoutValidationError,
|
|
8
8
|
} from "./_internal/checkout-types.js";
|
|
9
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
9
10
|
|
|
10
11
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
11
12
|
/** Bindable form data. Default: createEmptyCustomerFormData() */
|
|
@@ -54,6 +55,9 @@
|
|
|
54
55
|
*/
|
|
55
56
|
validate?: (data: CheckoutCustomerFormData) => CheckoutValidationError[];
|
|
56
57
|
|
|
58
|
+
/** Optional notifications instance */
|
|
59
|
+
notifications?: NotificationsStack;
|
|
60
|
+
|
|
57
61
|
t?: TranslateFn;
|
|
58
62
|
unstyled?: boolean;
|
|
59
63
|
class?: string;
|
|
@@ -76,6 +80,7 @@
|
|
|
76
80
|
onSubmit,
|
|
77
81
|
isSubmitting = false,
|
|
78
82
|
errors: externalErrors = [],
|
|
83
|
+
notifications,
|
|
79
84
|
showB2bFields = true,
|
|
80
85
|
b2bExpanded = false,
|
|
81
86
|
fields,
|
|
@@ -2,6 +2,7 @@ import type { Snippet } from "svelte";
|
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { CheckoutCustomerFormData, CheckoutValidationError } from "./_internal/checkout-types.js";
|
|
5
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
6
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
6
7
|
/** Bindable form data. Default: createEmptyCustomerFormData() */
|
|
7
8
|
formData?: CheckoutCustomerFormData;
|
|
@@ -41,6 +42,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children">
|
|
|
41
42
|
* Return empty array = valid. When provided, replaces (not extends) built-in validation.
|
|
42
43
|
*/
|
|
43
44
|
validate?: (data: CheckoutCustomerFormData) => CheckoutValidationError[];
|
|
45
|
+
/** Optional notifications instance */
|
|
46
|
+
notifications?: NotificationsStack;
|
|
44
47
|
t?: TranslateFn;
|
|
45
48
|
unstyled?: boolean;
|
|
46
49
|
class?: string;
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import type { TranslateFn } from "../../types.js";
|
|
5
5
|
import type { Props as GuestFormProps } from "./CheckoutGuestForm.svelte";
|
|
6
6
|
import type { Props as LoginFormProps } from "./CheckoutLoginForm.svelte";
|
|
7
|
+
import type { Props as LoginFormModalProps } from "../LoginForm/LoginFormModal.svelte";
|
|
8
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
7
9
|
|
|
8
10
|
export type FormMode = "guest-only" | "login-only" | "tabbed" | "stacked";
|
|
9
11
|
|
|
@@ -23,6 +25,22 @@
|
|
|
23
25
|
*/
|
|
24
26
|
formMode?: FormMode;
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* When provided, clicking the login tab opens a LoginFormModal
|
|
30
|
+
* instead of rendering the form inline (tabbed mode only).
|
|
31
|
+
* Form-related props are taken from loginForm.
|
|
32
|
+
*/
|
|
33
|
+
loginModal?: Pick<
|
|
34
|
+
LoginFormModalProps,
|
|
35
|
+
| "title"
|
|
36
|
+
| "classModal"
|
|
37
|
+
| "classInner"
|
|
38
|
+
| "classForm"
|
|
39
|
+
| "noXClose"
|
|
40
|
+
| "onClose"
|
|
41
|
+
| "showRememberMe"
|
|
42
|
+
>;
|
|
43
|
+
|
|
26
44
|
/** Tab label for the guest form tab. Default from i18n. */
|
|
27
45
|
guestTabLabel?: string;
|
|
28
46
|
|
|
@@ -35,6 +53,9 @@
|
|
|
35
53
|
/** Optional heading rendered above the switcher/forms */
|
|
36
54
|
heading?: Snippet | string;
|
|
37
55
|
|
|
56
|
+
/** Optional notifications instance — forwarded to child forms */
|
|
57
|
+
notifications?: NotificationsStack;
|
|
58
|
+
|
|
38
59
|
t?: TranslateFn;
|
|
39
60
|
unstyled?: boolean;
|
|
40
61
|
class?: string;
|
|
@@ -51,14 +72,35 @@
|
|
|
51
72
|
import { t_default } from "./_internal/checkout-i18n-defaults.js";
|
|
52
73
|
import CheckoutGuestForm from "./CheckoutGuestForm.svelte";
|
|
53
74
|
import CheckoutLoginForm from "./CheckoutLoginForm.svelte";
|
|
75
|
+
import LoginFormModal from "../LoginForm/LoginFormModal.svelte";
|
|
54
76
|
import TabbedMenu from "../TabbedMenu/TabbedMenu.svelte";
|
|
55
77
|
import { H, type HLevel } from "../H/index.js";
|
|
56
78
|
import CheckoutSectionHeader from "./CheckoutSectionHeader.svelte";
|
|
57
79
|
|
|
80
|
+
// Map login_form.* keys → checkout.login.* keys (same as CheckoutLoginForm)
|
|
81
|
+
const LOGIN_FORM_KEY_MAP: Record<string, string> = {
|
|
82
|
+
"login_form.email_label": "checkout.login.email_label",
|
|
83
|
+
"login_form.email_placeholder": "checkout.login.email_placeholder",
|
|
84
|
+
"login_form.password_label": "checkout.login.password_label",
|
|
85
|
+
"login_form.password_placeholder": "checkout.login.password_placeholder",
|
|
86
|
+
"login_form.submit": "checkout.login.submit",
|
|
87
|
+
"login_form.submitting": "checkout.login.submitting",
|
|
88
|
+
"login_form.forgot_password": "checkout.login.forgot_password",
|
|
89
|
+
"login_form.email_required": "checkout.login.email_required",
|
|
90
|
+
"login_form.email_invalid": "checkout.login.email_invalid",
|
|
91
|
+
"login_form.password_required": "checkout.login.password_required",
|
|
92
|
+
"login_form.social_divider": "checkout.login.social_divider",
|
|
93
|
+
"login_form.remember_me": "checkout.login.remember_me",
|
|
94
|
+
"login_form.remember_me_tooltip": "checkout.login.remember_me_tooltip",
|
|
95
|
+
"login_form.modal_title": "checkout.login.modal_title",
|
|
96
|
+
};
|
|
97
|
+
|
|
58
98
|
let {
|
|
59
99
|
guestForm,
|
|
60
100
|
loginForm,
|
|
61
101
|
formMode = "tabbed",
|
|
102
|
+
loginModal,
|
|
103
|
+
notifications,
|
|
62
104
|
guestTabLabel,
|
|
63
105
|
loginTabLabel,
|
|
64
106
|
activeTab = $bindable("guest"),
|
|
@@ -74,9 +116,33 @@
|
|
|
74
116
|
|
|
75
117
|
let t = $derived(tProp ?? t_default);
|
|
76
118
|
|
|
119
|
+
let loginModalRef: LoginFormModal = $state()!;
|
|
120
|
+
|
|
121
|
+
// Adapted t for LoginFormModal (maps login_form.* → checkout.login.*)
|
|
122
|
+
let modalT = $derived(
|
|
123
|
+
loginModal
|
|
124
|
+
? (
|
|
125
|
+
key: string,
|
|
126
|
+
values?: false | null | undefined | Record<string, string | number>,
|
|
127
|
+
fallback?: string | boolean
|
|
128
|
+
) => t(LOGIN_FORM_KEY_MAP[key] ?? key, values, fallback)
|
|
129
|
+
: undefined
|
|
130
|
+
);
|
|
131
|
+
|
|
77
132
|
let tabItems = $derived([
|
|
78
133
|
{ id: "guest", label: guestTabLabel ?? t("checkout.guest_or_login.guest_tab") },
|
|
79
|
-
{
|
|
134
|
+
{
|
|
135
|
+
id: "login",
|
|
136
|
+
label: loginTabLabel ?? t("checkout.guest_or_login.login_tab"),
|
|
137
|
+
...(loginModal
|
|
138
|
+
? {
|
|
139
|
+
onSelect: () => {
|
|
140
|
+
loginModalRef?.open();
|
|
141
|
+
return false;
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
: {}),
|
|
145
|
+
},
|
|
80
146
|
]);
|
|
81
147
|
|
|
82
148
|
let _class = $derived(
|
|
@@ -100,11 +166,11 @@
|
|
|
100
166
|
|
|
101
167
|
{#if formMode === "guest-only"}
|
|
102
168
|
{#if guestForm}
|
|
103
|
-
<CheckoutGuestForm {...guestForm} t={tProp} {unstyled} />
|
|
169
|
+
<CheckoutGuestForm {...guestForm} {notifications} t={tProp} {unstyled} />
|
|
104
170
|
{/if}
|
|
105
171
|
{:else if formMode === "login-only"}
|
|
106
172
|
{#if loginForm}
|
|
107
|
-
<CheckoutLoginForm {...loginForm} t={tProp} {unstyled} />
|
|
173
|
+
<CheckoutLoginForm {...loginForm} {notifications} t={tProp} {unstyled} />
|
|
108
174
|
{/if}
|
|
109
175
|
{:else if formMode === "tabbed"}
|
|
110
176
|
<TabbedMenu
|
|
@@ -118,22 +184,45 @@
|
|
|
118
184
|
/>
|
|
119
185
|
{#if activeTab === "guest" && guestForm}
|
|
120
186
|
<div role="tabpanel">
|
|
121
|
-
<CheckoutGuestForm {...guestForm} t={tProp} {unstyled} />
|
|
187
|
+
<CheckoutGuestForm {...guestForm} {notifications} t={tProp} {unstyled} />
|
|
122
188
|
</div>
|
|
123
189
|
{:else if activeTab === "login" && loginForm}
|
|
124
190
|
<div role="tabpanel">
|
|
125
|
-
<CheckoutLoginForm {...loginForm} t={tProp} {unstyled} />
|
|
191
|
+
<CheckoutLoginForm {...loginForm} {notifications} t={tProp} {unstyled} />
|
|
126
192
|
</div>
|
|
127
193
|
{/if}
|
|
128
194
|
{:else if formMode === "stacked"}
|
|
129
195
|
{#if loginForm}
|
|
130
|
-
<CheckoutLoginForm {...loginForm} t={tProp} {unstyled} />
|
|
196
|
+
<CheckoutLoginForm {...loginForm} {notifications} t={tProp} {unstyled} />
|
|
131
197
|
{/if}
|
|
132
198
|
<div class={unstyled ? undefined : "stuic-checkout-guest-or-login-divider"}>
|
|
133
199
|
<span>{t("checkout.step.or_divider")}</span>
|
|
134
200
|
</div>
|
|
135
201
|
{#if guestForm}
|
|
136
|
-
<CheckoutGuestForm {...guestForm} t={tProp} {unstyled} />
|
|
202
|
+
<CheckoutGuestForm {...guestForm} {notifications} t={tProp} {unstyled} />
|
|
137
203
|
{/if}
|
|
138
204
|
{/if}
|
|
205
|
+
|
|
206
|
+
{#if loginModal && loginForm}
|
|
207
|
+
<LoginFormModal
|
|
208
|
+
bind:this={loginModalRef}
|
|
209
|
+
formData={loginForm.formData}
|
|
210
|
+
onSubmit={loginForm.onSubmit}
|
|
211
|
+
isSubmitting={loginForm.isSubmitting}
|
|
212
|
+
errors={loginForm.errors}
|
|
213
|
+
error={loginForm.error}
|
|
214
|
+
onForgotPassword={loginForm.onForgotPassword}
|
|
215
|
+
submitLabel={loginForm.submitLabel}
|
|
216
|
+
submittingLabel={loginForm.submittingLabel}
|
|
217
|
+
submitButton={loginForm.submitButton}
|
|
218
|
+
socialLogins={loginForm.socialLogins}
|
|
219
|
+
socialDividerLabel={loginForm.socialDividerLabel}
|
|
220
|
+
footer={loginForm.footer}
|
|
221
|
+
{notifications}
|
|
222
|
+
title={loginTabLabel ?? t("checkout.guest_or_login.login_tab")}
|
|
223
|
+
t={modalT}
|
|
224
|
+
{unstyled}
|
|
225
|
+
{...loginModal}
|
|
226
|
+
/>
|
|
227
|
+
{/if}
|
|
139
228
|
</div>
|
|
@@ -3,6 +3,8 @@ import type { HTMLAttributes } from "svelte/elements";
|
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { Props as GuestFormProps } from "./CheckoutGuestForm.svelte";
|
|
5
5
|
import type { Props as LoginFormProps } from "./CheckoutLoginForm.svelte";
|
|
6
|
+
import type { Props as LoginFormModalProps } from "../LoginForm/LoginFormModal.svelte";
|
|
7
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
6
8
|
export type FormMode = "guest-only" | "login-only" | "tabbed" | "stacked";
|
|
7
9
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
8
10
|
/** Guest form configuration. Required for "guest-only", "tabbed", "stacked" modes. */
|
|
@@ -17,6 +19,12 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
|
|
|
17
19
|
* - "stacked": Show both forms stacked vertically with a divider
|
|
18
20
|
*/
|
|
19
21
|
formMode?: FormMode;
|
|
22
|
+
/**
|
|
23
|
+
* When provided, clicking the login tab opens a LoginFormModal
|
|
24
|
+
* instead of rendering the form inline (tabbed mode only).
|
|
25
|
+
* Form-related props are taken from loginForm.
|
|
26
|
+
*/
|
|
27
|
+
loginModal?: Pick<LoginFormModalProps, "title" | "classModal" | "classInner" | "classForm" | "noXClose" | "onClose" | "showRememberMe">;
|
|
20
28
|
/** Tab label for the guest form tab. Default from i18n. */
|
|
21
29
|
guestTabLabel?: string;
|
|
22
30
|
/** Tab label for the login form tab. Default from i18n. */
|
|
@@ -25,6 +33,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
|
|
|
25
33
|
activeTab?: "guest" | "login";
|
|
26
34
|
/** Optional heading rendered above the switcher/forms */
|
|
27
35
|
heading?: Snippet | string;
|
|
36
|
+
/** Optional notifications instance — forwarded to child forms */
|
|
37
|
+
notifications?: NotificationsStack;
|
|
28
38
|
t?: TranslateFn;
|
|
29
39
|
unstyled?: boolean;
|
|
30
40
|
class?: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
CheckoutLoginFormData,
|
|
7
7
|
CheckoutValidationError,
|
|
8
8
|
} from "./_internal/checkout-types.js";
|
|
9
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
9
10
|
|
|
10
11
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
11
12
|
/** Bindable login data. Default: createEmptyLoginFormData() */
|
|
@@ -62,6 +63,9 @@
|
|
|
62
63
|
*/
|
|
63
64
|
footer?: Snippet;
|
|
64
65
|
|
|
66
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
67
|
+
notifications?: NotificationsStack;
|
|
68
|
+
|
|
65
69
|
t?: TranslateFn;
|
|
66
70
|
unstyled?: boolean;
|
|
67
71
|
class?: string;
|
|
@@ -72,27 +76,28 @@
|
|
|
72
76
|
<script lang="ts">
|
|
73
77
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
74
78
|
import { t_default } from "./_internal/checkout-i18n-defaults.js";
|
|
75
|
-
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
import { createEmptyLoginFormData } from "./_internal/checkout-utils.js";
|
|
80
|
+
import LoginForm from "../LoginForm/LoginForm.svelte";
|
|
81
|
+
|
|
82
|
+
// Map login_form.* keys → checkout.login.* keys for backwards compatibility
|
|
83
|
+
const KEY_MAP: Record<string, string> = {
|
|
84
|
+
"login_form.email_label": "checkout.login.email_label",
|
|
85
|
+
"login_form.email_placeholder": "checkout.login.email_placeholder",
|
|
86
|
+
"login_form.password_label": "checkout.login.password_label",
|
|
87
|
+
"login_form.password_placeholder": "checkout.login.password_placeholder",
|
|
88
|
+
"login_form.submit": "checkout.login.submit",
|
|
89
|
+
"login_form.submitting": "checkout.login.submitting",
|
|
90
|
+
"login_form.forgot_password": "checkout.login.forgot_password",
|
|
91
|
+
"login_form.email_required": "checkout.login.email_required",
|
|
92
|
+
"login_form.email_invalid": "checkout.login.email_invalid",
|
|
93
|
+
"login_form.password_required": "checkout.login.password_required",
|
|
94
|
+
"login_form.social_divider": "checkout.login.social_divider",
|
|
95
|
+
"login_form.remember_me": "checkout.login.remember_me",
|
|
96
|
+
"login_form.remember_me_tooltip": "checkout.login.remember_me_tooltip",
|
|
97
|
+
};
|
|
82
98
|
|
|
83
99
|
let {
|
|
84
100
|
formData = $bindable(createEmptyLoginFormData()),
|
|
85
|
-
onSubmit,
|
|
86
|
-
isSubmitting = false,
|
|
87
|
-
errors: externalErrors = [],
|
|
88
|
-
error,
|
|
89
|
-
onForgotPassword,
|
|
90
|
-
submitLabel,
|
|
91
|
-
submittingLabel,
|
|
92
|
-
submitButton,
|
|
93
|
-
socialLogins,
|
|
94
|
-
socialDividerLabel,
|
|
95
|
-
footer,
|
|
96
101
|
t: tProp,
|
|
97
102
|
unstyled = false,
|
|
98
103
|
class: classProp,
|
|
@@ -102,128 +107,19 @@
|
|
|
102
107
|
|
|
103
108
|
let t = $derived(tProp ?? t_default);
|
|
104
109
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
function fieldError(field: string): string | undefined {
|
|
117
|
-
return allErrors.find((e) => e.field === field)?.message;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function handleSubmit(e: SubmitEvent) {
|
|
121
|
-
e.preventDefault();
|
|
122
|
-
|
|
123
|
-
const validationErrors = validateLoginForm(formData, t);
|
|
124
|
-
internalErrors = validationErrors;
|
|
125
|
-
|
|
126
|
-
if (validationErrors.length === 0 && externalErrors.length === 0) {
|
|
127
|
-
onSubmit(formData);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
110
|
+
// Adapt the t function: LoginForm uses login_form.* keys,
|
|
111
|
+
// but checkout consumers provide checkout.login.* translations
|
|
112
|
+
let adaptedT = $derived(
|
|
113
|
+
(
|
|
114
|
+
key: string,
|
|
115
|
+
values?: false | null | undefined | Record<string, string | number>,
|
|
116
|
+
fallback?: string | boolean
|
|
117
|
+
) => t(KEY_MAP[key] ?? key, values, fallback)
|
|
118
|
+
);
|
|
130
119
|
|
|
131
120
|
let _class = $derived(
|
|
132
121
|
unstyled ? classProp : twMerge("stuic-checkout-login-form", classProp)
|
|
133
122
|
);
|
|
134
123
|
</script>
|
|
135
124
|
|
|
136
|
-
<
|
|
137
|
-
<!-- General error alert -->
|
|
138
|
-
<DismissibleMessage message={error} intent="destructive" onDismiss={false} />
|
|
139
|
-
|
|
140
|
-
<!--
|
|
141
|
-
svelte-ignore binding_property_non_reactive:
|
|
142
|
-
formData is a $bindable prop — deep reactivity depends on the consumer
|
|
143
|
-
passing a $state() object. The bindings work correctly regardless.
|
|
144
|
-
-->
|
|
145
|
-
<!-- Email -->
|
|
146
|
-
<!-- svelte-ignore binding_property_non_reactive -->
|
|
147
|
-
<FieldInput
|
|
148
|
-
bind:value={formData.email}
|
|
149
|
-
label={t("checkout.login.email_label")}
|
|
150
|
-
type="email"
|
|
151
|
-
placeholder={t("checkout.login.email_placeholder")}
|
|
152
|
-
required
|
|
153
|
-
name="checkout-login-email"
|
|
154
|
-
labelLeftBreakpoint={0}
|
|
155
|
-
validate={{
|
|
156
|
-
customValidator(val) {
|
|
157
|
-
return fieldError("email") || "";
|
|
158
|
-
},
|
|
159
|
-
}}
|
|
160
|
-
/>
|
|
161
|
-
|
|
162
|
-
<!-- Password -->
|
|
163
|
-
<!-- svelte-ignore binding_property_non_reactive -->
|
|
164
|
-
<FieldInput
|
|
165
|
-
bind:value={formData.password}
|
|
166
|
-
label={t("checkout.login.password_label")}
|
|
167
|
-
type="password"
|
|
168
|
-
placeholder={t("checkout.login.password_placeholder")}
|
|
169
|
-
required
|
|
170
|
-
name="checkout-login-password"
|
|
171
|
-
labelLeftBreakpoint={0}
|
|
172
|
-
validate={{
|
|
173
|
-
customValidator(val) {
|
|
174
|
-
return fieldError("password") || "";
|
|
175
|
-
},
|
|
176
|
-
}}
|
|
177
|
-
/>
|
|
178
|
-
|
|
179
|
-
<!-- CTA -->
|
|
180
|
-
{#if submitButton}
|
|
181
|
-
{@render submitButton({ isSubmitting, disabled: isSubmitting })}
|
|
182
|
-
{:else}
|
|
183
|
-
<div class={unstyled ? undefined : "stuic-checkout-login-submit"}>
|
|
184
|
-
<Button intent="primary" type="submit" disabled={isSubmitting} class="w-full">
|
|
185
|
-
{isSubmitting
|
|
186
|
-
? (submittingLabel ?? t("checkout.login.submitting"))
|
|
187
|
-
: (submitLabel ?? t("checkout.login.submit"))}
|
|
188
|
-
</Button>
|
|
189
|
-
</div>
|
|
190
|
-
{/if}
|
|
191
|
-
|
|
192
|
-
<!-- Forgot password -->
|
|
193
|
-
{#if onForgotPassword}
|
|
194
|
-
<div class={unstyled ? undefined : "stuic-checkout-login-forgot"}>
|
|
195
|
-
<Button
|
|
196
|
-
variant="link"
|
|
197
|
-
type="button"
|
|
198
|
-
class="text-muted-foreground"
|
|
199
|
-
size="sm"
|
|
200
|
-
onclick={onForgotPassword}
|
|
201
|
-
>
|
|
202
|
-
{t("checkout.login.forgot_password")}
|
|
203
|
-
</Button>
|
|
204
|
-
</div>
|
|
205
|
-
{/if}
|
|
206
|
-
|
|
207
|
-
<!-- Social logins -->
|
|
208
|
-
{#if socialLogins}
|
|
209
|
-
<div class={unstyled ? undefined : "stuic-checkout-login-social"}>
|
|
210
|
-
{#if socialDividerLabel !== false}
|
|
211
|
-
<div class={unstyled ? undefined : "stuic-checkout-login-social-divider"}>
|
|
212
|
-
<span>
|
|
213
|
-
{typeof socialDividerLabel === "string"
|
|
214
|
-
? socialDividerLabel
|
|
215
|
-
: t("checkout.login.social_divider")}
|
|
216
|
-
</span>
|
|
217
|
-
</div>
|
|
218
|
-
{/if}
|
|
219
|
-
<div class={unstyled ? undefined : "stuic-checkout-login-social-buttons"}>
|
|
220
|
-
{@render socialLogins()}
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
|
-
{/if}
|
|
224
|
-
|
|
225
|
-
<!-- Footer -->
|
|
226
|
-
{#if footer}
|
|
227
|
-
{@render footer()}
|
|
228
|
-
{/if}
|
|
229
|
-
</form>
|
|
125
|
+
<LoginForm bind:formData bind:el t={adaptedT} {unstyled} class={_class} {...rest} />
|
|
@@ -2,6 +2,7 @@ import type { Snippet } from "svelte";
|
|
|
2
2
|
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { CheckoutLoginFormData, CheckoutValidationError } from "./_internal/checkout-types.js";
|
|
5
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
5
6
|
export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children"> {
|
|
6
7
|
/** Bindable login data. Default: createEmptyLoginFormData() */
|
|
7
8
|
formData?: CheckoutLoginFormData;
|
|
@@ -48,6 +49,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children">
|
|
|
48
49
|
* Use for "Or continue as guest" links, sign-up links, etc.
|
|
49
50
|
*/
|
|
50
51
|
footer?: Snippet;
|
|
52
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
53
|
+
notifications?: NotificationsStack;
|
|
51
54
|
t?: TranslateFn;
|
|
52
55
|
unstyled?: boolean;
|
|
53
56
|
class?: string;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
CheckoutStep,
|
|
10
10
|
CheckoutValidationError,
|
|
11
11
|
} from "./_internal/checkout-types.js";
|
|
12
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
12
13
|
|
|
13
14
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
14
15
|
/** Cart items to display in the cart review */
|
|
@@ -94,6 +95,9 @@
|
|
|
94
95
|
/** Override the right column entirely */
|
|
95
96
|
rightColumn?: Snippet;
|
|
96
97
|
|
|
98
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
99
|
+
notifications?: NotificationsStack;
|
|
100
|
+
|
|
97
101
|
t?: TranslateFn;
|
|
98
102
|
unstyled?: boolean;
|
|
99
103
|
class?: string;
|
|
@@ -114,6 +118,7 @@
|
|
|
114
118
|
items,
|
|
115
119
|
isLoading = false,
|
|
116
120
|
error,
|
|
121
|
+
notifications,
|
|
117
122
|
hideProgress = false,
|
|
118
123
|
currentStep = "review",
|
|
119
124
|
steps,
|
|
@@ -137,6 +142,10 @@
|
|
|
137
142
|
|
|
138
143
|
let t = $derived(tProp ?? t_default);
|
|
139
144
|
|
|
145
|
+
$effect(() => {
|
|
146
|
+
if (error && notifications) notifications.error(error);
|
|
147
|
+
});
|
|
148
|
+
|
|
140
149
|
let _class = $derived(
|
|
141
150
|
unstyled ? classProp : twMerge("stuic-checkout-review-step", classProp)
|
|
142
151
|
);
|
|
@@ -202,6 +211,7 @@
|
|
|
202
211
|
{guestTabLabel}
|
|
203
212
|
{loginTabLabel}
|
|
204
213
|
heading={t("checkout.step.contact_title")}
|
|
214
|
+
{notifications}
|
|
205
215
|
t={tProp}
|
|
206
216
|
{unstyled}
|
|
207
217
|
/>
|
|
@@ -3,6 +3,7 @@ import type { HTMLAttributes } from "svelte/elements";
|
|
|
3
3
|
import type { TranslateFn } from "../../types.js";
|
|
4
4
|
import type { CartComponentItem } from "../Cart/Cart.svelte";
|
|
5
5
|
import type { CheckoutCustomerFormData, CheckoutLoginFormData, CheckoutStep, CheckoutValidationError } from "./_internal/checkout-types.js";
|
|
6
|
+
import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
|
|
6
7
|
export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
7
8
|
/** Cart items to display in the cart review */
|
|
8
9
|
items: CartComponentItem[];
|
|
@@ -63,6 +64,8 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
|
|
|
63
64
|
leftColumn?: Snippet;
|
|
64
65
|
/** Override the right column entirely */
|
|
65
66
|
rightColumn?: Snippet;
|
|
67
|
+
/** Optional notifications instance — errors will be sent via notifications.error() */
|
|
68
|
+
notifications?: NotificationsStack;
|
|
66
69
|
t?: TranslateFn;
|
|
67
70
|
unstyled?: boolean;
|
|
68
71
|
class?: string;
|