@getgreenline/blaze-ui 1.0.24 → 1.0.26

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.
Files changed (35) hide show
  1. package/dist/components/alert-dialog.d.ts +4 -1
  2. package/dist/components/alert-dialog.d.ts.map +1 -1
  3. package/dist/components/alert-dialog.js +3 -2
  4. package/dist/components/button.d.ts.map +1 -1
  5. package/dist/components/button.js +1 -1
  6. package/dist/components/data-table.d.ts.map +1 -1
  7. package/dist/components/data-table.js +100 -31
  8. package/dist/components/login-screen.d.ts +4 -0
  9. package/dist/components/login-screen.d.ts.map +1 -0
  10. package/dist/components/login-screen.js +299 -0
  11. package/dist/components/login-screen.types.d.ts +82 -0
  12. package/dist/components/login-screen.types.d.ts.map +1 -0
  13. package/dist/components/login-screen.views.d.ts +114 -0
  14. package/dist/components/login-screen.views.d.ts.map +1 -0
  15. package/dist/components/login-screen.views.js +53 -0
  16. package/dist/components/search-bar.d.ts +15 -0
  17. package/dist/components/search-bar.d.ts.map +1 -0
  18. package/dist/components/search-bar.js +25 -0
  19. package/dist/components/selection-panel.d.ts +29 -0
  20. package/dist/components/selection-panel.d.ts.map +1 -0
  21. package/dist/components/selection-panel.js +166 -0
  22. package/dist/components/sheet.d.ts +2 -1
  23. package/dist/components/sheet.d.ts.map +1 -1
  24. package/dist/components/sheet.js +5 -4
  25. package/dist/components/visually-hidden.d.ts +16 -0
  26. package/dist/components/visually-hidden.d.ts.map +1 -0
  27. package/dist/components/visually-hidden.js +22 -0
  28. package/dist/globals.css +295 -68
  29. package/dist/index.d.ts +5 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -0
  32. package/dist/lib/portal-wrapper.d.ts +32 -0
  33. package/dist/lib/portal-wrapper.d.ts.map +1 -0
  34. package/dist/lib/portal-wrapper.js +34 -0
  35. package/package.json +1 -1
@@ -0,0 +1,299 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { cn } from '../lib/utils.js';
4
+ import { SignInView, ForgotPasswordView, ForgotPasswordSentView, SsoEmailView, FooterLinks, SplitMediaPane } from './login-screen.views.js';
5
+
6
+ const DEFAULT_FOOTER_LINKS = [
7
+ { label: "Help", href: "#" },
8
+ { label: "Privacy", href: "#" },
9
+ { label: "Terms", href: "#" },
10
+ ];
11
+ const DEFAULT_TESTIMONIAL = {
12
+ quote: '"BLAZE has completely transformed how we manage our dispensary. The platform is intuitive, fast, and our team loves it."',
13
+ authorName: "Sarah Johnson",
14
+ authorTitle: "Operations Director, Green Valley Dispensary",
15
+ };
16
+ const DEFAULT_LEGAL_NOTICE = (jsxs(Fragment, { children: ["By continuing, you agree to our", " ", jsx("a", { href: "#", className: "tw:underline tw:hover:text-foreground", children: "Terms of Service" }), " ", "and", " ", jsx("a", { href: "#", className: "tw:underline tw:hover:text-foreground", children: "Privacy Policy" })] }));
17
+ function clampPercent(value) {
18
+ if (Number.isNaN(value)) {
19
+ return 40;
20
+ }
21
+ return Math.min(100, Math.max(0, value));
22
+ }
23
+ function clampOpacity(value) {
24
+ if (Number.isNaN(value)) {
25
+ return 0.35;
26
+ }
27
+ return Math.min(1, Math.max(0, value));
28
+ }
29
+ function getIdentifierInputType(mode) {
30
+ if (mode === "phone") {
31
+ return "tel";
32
+ }
33
+ if (mode === "email-or-phone") {
34
+ return "text";
35
+ }
36
+ return "email";
37
+ }
38
+ function getIdentifierDefaults(mode) {
39
+ if (mode === "phone") {
40
+ return {
41
+ label: "Phone number",
42
+ placeholder: "Enter your phone number",
43
+ };
44
+ }
45
+ if (mode === "email-or-phone") {
46
+ return {
47
+ label: "Email or phone",
48
+ placeholder: "Enter your email or phone number",
49
+ };
50
+ }
51
+ return {
52
+ label: "Email",
53
+ placeholder: "Enter your email address",
54
+ };
55
+ }
56
+ function LoginScreen({ className, splitLayout = true, splitImageWidthPercent = 40, imageSrc = "/login-bg.jpg", imageAlt = "Login background", imageOverlayContent, testimonial = DEFAULT_TESTIMONIAL, imageOverlayOpacity = 0.35, logo, title = "Welcome to Blaze", description = "Sign in with your email and password", tabs = [], defaultTabId, tabId, onTabChange, identifierMode = "email", identifierLabel, identifierPlaceholder, passwordLabel = "Password", passwordPlaceholder = "Enter your password", rememberMeLabel = "Remember Me", forgotPasswordLabel = "Forgot Password?", forgotPasswordTitle = "Reset your password", forgotPasswordDescription = "Enter your email and we will send reset instructions.", forgotPasswordEmailLabel = "Email", forgotPasswordEmailPlaceholder = "Enter your email address", forgotPasswordSubmitLabel = "Send Reset Link", forgotPasswordBackLabel = "Back to sign in", forgotPasswordSentTitle = "Check your email", forgotPasswordSentDescriptionPrefix = "We sent a password reset link to", forgotPasswordSentInstructions = "Click the link in the email to reset your password. If you don't see it, check your spam folder.", forgotPasswordResendLabel = "Resend email", forgotPasswordResendDelaySeconds = 60, submitLabel = "Log In", submitLoadingLabel = "Signing in...", ssoLabel = "Or", ssoButtonLabel = "Continue with Enterprise SSO", ssoEmailTitle = "Enter your email", ssoEmailDescription = "We'll check if you have an existing account or need to create one", ssoEmailLabel = "Email address", ssoEmailPlaceholder = "name@company.com", ssoEmailSubmitLabel = "Continue", ssoEmailHint = "Enterprise users will be redirected to their company's SSO portal", ssoEmailBackAriaLabel = "Go back", legalNotice, footerLinks = DEFAULT_FOOTER_LINKS, isSubmitting = false, isForgotPasswordSubmitting = false, isSsoSubmitting = false, onSubmit, onForgotPasswordSubmit, onSsoClick, onSsoSubmit, }) {
57
+ const id = React.useId();
58
+ const identifierId = `${id}-identifier`;
59
+ const passwordId = `${id}-password`;
60
+ const rememberId = `${id}-remember`;
61
+ const forgotEmailId = `${id}-forgot-email`;
62
+ const ssoEmailId = `${id}-sso-email`;
63
+ const [mode, setMode] = React.useState("sign-in");
64
+ const [identifier, setIdentifier] = React.useState("");
65
+ const [password, setPassword] = React.useState("");
66
+ const [forgotEmail, setForgotEmail] = React.useState("");
67
+ const [ssoEmail, setSsoEmail] = React.useState("");
68
+ const [rememberMe, setRememberMe] = React.useState(false);
69
+ const [showPassword, setShowPassword] = React.useState(false);
70
+ const [isResending, setIsResending] = React.useState(false);
71
+ const [imageLoadError, setImageLoadError] = React.useState(false);
72
+ const [logoLoadError, setLogoLoadError] = React.useState(false);
73
+ const [isSplitViewport, setIsSplitViewport] = React.useState(true);
74
+ const [forgotPasswordError, setForgotPasswordError] = React.useState(null);
75
+ const tabListRef = React.useRef(null);
76
+ const resendDelaySeconds = React.useMemo(() => {
77
+ if (Number.isNaN(forgotPasswordResendDelaySeconds)) {
78
+ return 60;
79
+ }
80
+ return Math.max(0, Math.floor(forgotPasswordResendDelaySeconds));
81
+ }, [forgotPasswordResendDelaySeconds]);
82
+ const [resendCountdown, setResendCountdown] = React.useState(resendDelaySeconds);
83
+ const firstTabId = tabs[0]?.id ?? null;
84
+ const [internalTabId, setInternalTabId] = React.useState(defaultTabId ?? firstTabId);
85
+ React.useEffect(() => {
86
+ if (tabId !== undefined) {
87
+ return;
88
+ }
89
+ if (!internalTabId && firstTabId) {
90
+ setInternalTabId(firstTabId);
91
+ }
92
+ }, [firstTabId, internalTabId, tabId]);
93
+ React.useEffect(() => {
94
+ setImageLoadError(false);
95
+ }, [imageSrc]);
96
+ React.useEffect(() => {
97
+ const handleResize = () => {
98
+ setIsSplitViewport(window.innerWidth >= 768);
99
+ };
100
+ handleResize();
101
+ window.addEventListener("resize", handleResize);
102
+ return () => {
103
+ window.removeEventListener("resize", handleResize);
104
+ };
105
+ }, []);
106
+ const activeTabId = tabId ?? internalTabId ?? firstTabId;
107
+ const shouldShowSplitPane = splitLayout && isSplitViewport;
108
+ const hasSplitImage = Boolean(imageSrc) && !imageLoadError;
109
+ const imageWidth = clampPercent(splitImageWidthPercent);
110
+ const overlayOpacity = clampOpacity(imageOverlayOpacity);
111
+ const resolvedTestimonial = testimonial === null
112
+ ? null
113
+ : {
114
+ quote: testimonial?.quote || DEFAULT_TESTIMONIAL.quote,
115
+ authorName: testimonial?.authorName || DEFAULT_TESTIMONIAL.authorName,
116
+ authorTitle: testimonial?.authorTitle === undefined
117
+ ? DEFAULT_TESTIMONIAL.authorTitle
118
+ : testimonial.authorTitle,
119
+ };
120
+ const identifierDefaults = React.useMemo(() => getIdentifierDefaults(identifierMode), [identifierMode]);
121
+ const resolvedIdentifierLabel = identifierLabel ?? identifierDefaults.label;
122
+ const resolvedIdentifierPlaceholder = identifierPlaceholder ?? identifierDefaults.placeholder;
123
+ const setTabSelection = React.useCallback((tab) => {
124
+ if (tab.disabled) {
125
+ return;
126
+ }
127
+ if (tabId === undefined) {
128
+ setInternalTabId(tab.id);
129
+ }
130
+ onTabChange?.(tab.id);
131
+ }, [onTabChange, tabId]);
132
+ const handleTabClick = React.useCallback((tab, options = {}) => {
133
+ if (tab.disabled) {
134
+ return;
135
+ }
136
+ setTabSelection(tab);
137
+ if (tab.onSelect) {
138
+ tab.onSelect(tab.id);
139
+ return;
140
+ }
141
+ if (options.allowNavigation === false ||
142
+ !tab.href ||
143
+ typeof window === "undefined") {
144
+ return;
145
+ }
146
+ if (tab.external) {
147
+ window.open(tab.href, "_blank", "noopener,noreferrer");
148
+ return;
149
+ }
150
+ window.location.assign(tab.href);
151
+ }, [setTabSelection]);
152
+ const handleTabKeyDown = React.useCallback((event, currentIndex) => {
153
+ const enabledTabIndices = tabs.reduce((acc, tab, index) => {
154
+ if (!tab.disabled) {
155
+ acc.push(index);
156
+ }
157
+ return acc;
158
+ }, []);
159
+ if (enabledTabIndices.length === 0) {
160
+ return;
161
+ }
162
+ const isPrev = event.key === "ArrowLeft" || event.key === "ArrowUp";
163
+ const isNext = event.key === "ArrowRight" || event.key === "ArrowDown";
164
+ const isHome = event.key === "Home";
165
+ const isEnd = event.key === "End";
166
+ if (!isPrev && !isNext && !isHome && !isEnd) {
167
+ return;
168
+ }
169
+ event.preventDefault();
170
+ let nextIndex = currentIndex;
171
+ if (isHome) {
172
+ nextIndex = enabledTabIndices[0] ?? currentIndex;
173
+ }
174
+ else if (isEnd) {
175
+ nextIndex = enabledTabIndices[enabledTabIndices.length - 1] ?? currentIndex;
176
+ }
177
+ else {
178
+ const enabledPosition = enabledTabIndices.indexOf(currentIndex);
179
+ const currentEnabledPosition = enabledPosition === -1 ? 0 : enabledPosition;
180
+ const delta = isNext ? 1 : -1;
181
+ const nextEnabledPosition = (currentEnabledPosition + delta + enabledTabIndices.length) %
182
+ enabledTabIndices.length;
183
+ nextIndex = enabledTabIndices[nextEnabledPosition] ?? currentIndex;
184
+ }
185
+ const nextTab = tabs[nextIndex];
186
+ if (!nextTab) {
187
+ return;
188
+ }
189
+ handleTabClick(nextTab, { allowNavigation: false });
190
+ const tabButtons = tabListRef.current?.querySelectorAll('[role="tab"]');
191
+ tabButtons?.[nextIndex]?.focus();
192
+ }, [handleTabClick, tabs]);
193
+ const handleSignInSubmit = async (event) => {
194
+ event.preventDefault();
195
+ const nextIdentifier = identifier.trim();
196
+ const nextPassword = password.trim();
197
+ if (!nextIdentifier || !nextPassword) {
198
+ return;
199
+ }
200
+ await onSubmit?.({
201
+ identifier: nextIdentifier,
202
+ password: nextPassword,
203
+ rememberMe,
204
+ tabId: activeTabId,
205
+ });
206
+ };
207
+ const handleForgotPasswordClick = () => {
208
+ if (identifier.includes("@")) {
209
+ setForgotEmail(identifier.trim());
210
+ }
211
+ setForgotPasswordError(null);
212
+ setMode("forgot-password");
213
+ };
214
+ const handleBackToSignIn = () => {
215
+ setForgotPasswordError(null);
216
+ setMode("sign-in");
217
+ };
218
+ const handleForgotPasswordSubmit = async (event) => {
219
+ event.preventDefault();
220
+ const nextForgotEmail = forgotEmail.trim();
221
+ if (!nextForgotEmail) {
222
+ return;
223
+ }
224
+ setForgotPasswordError(null);
225
+ try {
226
+ await onForgotPasswordSubmit?.(nextForgotEmail);
227
+ }
228
+ catch (error) {
229
+ const message = error instanceof Error && error.message
230
+ ? error.message
231
+ : "Failed to send reset email. Please try again.";
232
+ setForgotPasswordError(message);
233
+ return;
234
+ }
235
+ setResendCountdown(resendDelaySeconds);
236
+ setMode("forgot-password-sent");
237
+ };
238
+ const handleSsoClick = async () => {
239
+ if (onSsoClick) {
240
+ await onSsoClick();
241
+ return;
242
+ }
243
+ if (identifier.includes("@")) {
244
+ setSsoEmail(identifier.trim());
245
+ }
246
+ setMode("sso-email");
247
+ };
248
+ const handleSsoEmailSubmit = async (event) => {
249
+ event.preventDefault();
250
+ const nextSsoEmail = ssoEmail.trim();
251
+ if (!nextSsoEmail) {
252
+ return;
253
+ }
254
+ await onSsoSubmit?.(nextSsoEmail);
255
+ };
256
+ const handleResendEmail = async () => {
257
+ const nextForgotEmail = forgotEmail.trim();
258
+ if (!nextForgotEmail) {
259
+ return;
260
+ }
261
+ setForgotPasswordError(null);
262
+ setIsResending(true);
263
+ try {
264
+ await onForgotPasswordSubmit?.(nextForgotEmail);
265
+ setResendCountdown(resendDelaySeconds);
266
+ }
267
+ catch (error) {
268
+ const message = error instanceof Error && error.message
269
+ ? error.message
270
+ : "Failed to resend reset email. Please try again.";
271
+ setForgotPasswordError(message);
272
+ }
273
+ finally {
274
+ setIsResending(false);
275
+ }
276
+ };
277
+ React.useEffect(() => {
278
+ if (mode !== "forgot-password-sent") {
279
+ return;
280
+ }
281
+ if (resendCountdown <= 0) {
282
+ return;
283
+ }
284
+ const timeoutId = window.setTimeout(() => {
285
+ setResendCountdown((previous) => Math.max(0, previous - 1));
286
+ }, 1000);
287
+ return () => {
288
+ window.clearTimeout(timeoutId);
289
+ };
290
+ }, [mode, resendCountdown]);
291
+ const modeView = mode === "sign-in" ? (jsx(SignInView, { tabs: tabs, activeTabId: activeTabId, tabListRef: tabListRef, onTabClick: (tab) => handleTabClick(tab), onTabKeyDown: handleTabKeyDown, identifierId: identifierId, identifierLabel: resolvedIdentifierLabel, identifierType: getIdentifierInputType(identifierMode), identifierPlaceholder: resolvedIdentifierPlaceholder, identifier: identifier, onIdentifierChange: setIdentifier, passwordId: passwordId, passwordLabel: passwordLabel, passwordPlaceholder: passwordPlaceholder, password: password, onPasswordChange: setPassword, showPassword: showPassword, onTogglePassword: () => setShowPassword((previous) => !previous), rememberId: rememberId, rememberMe: rememberMe, onRememberMeChange: setRememberMe, rememberMeLabel: rememberMeLabel, forgotPasswordLabel: forgotPasswordLabel, onForgotPasswordClick: handleForgotPasswordClick, isSubmitting: isSubmitting, submitLabel: submitLabel, submitLoadingLabel: submitLoadingLabel, onSubmit: handleSignInSubmit, ssoLabel: ssoLabel, ssoButtonLabel: ssoButtonLabel, onSsoClick: handleSsoClick, legalNotice: legalNotice ?? DEFAULT_LEGAL_NOTICE, logo: logo, logoLoadError: logoLoadError, onLogoError: () => setLogoLoadError(true), title: title, description: description, autoCompleteIdentifier: identifierMode === "phone" ? "tel" : "email" })) : mode === "forgot-password" ? (jsx(ForgotPasswordView, { title: forgotPasswordTitle, description: forgotPasswordDescription, emailId: forgotEmailId, emailLabel: forgotPasswordEmailLabel, email: forgotEmail, onEmailChange: setForgotEmail, emailPlaceholder: forgotPasswordEmailPlaceholder, submitLabel: forgotPasswordSubmitLabel, backLabel: forgotPasswordBackLabel, isSubmitting: isForgotPasswordSubmitting, onSubmit: handleForgotPasswordSubmit, onBack: handleBackToSignIn, error: forgotPasswordError })) : mode === "forgot-password-sent" ? (jsx(ForgotPasswordSentView, { backAriaLabel: ssoEmailBackAriaLabel, onBack: handleBackToSignIn, title: forgotPasswordSentTitle, descriptionPrefix: forgotPasswordSentDescriptionPrefix, email: forgotEmail, instructions: forgotPasswordSentInstructions, resendLabel: forgotPasswordResendLabel, resendCountdown: resendCountdown, onResend: handleResendEmail, isResending: isResending, error: forgotPasswordError, backLabel: forgotPasswordBackLabel })) : (jsx(SsoEmailView, { backAriaLabel: ssoEmailBackAriaLabel, onBack: () => setMode("sign-in"), title: ssoEmailTitle, description: ssoEmailDescription, emailId: ssoEmailId, emailLabel: ssoEmailLabel, email: ssoEmail, onEmailChange: setSsoEmail, emailPlaceholder: ssoEmailPlaceholder, submitLabel: ssoEmailSubmitLabel, isSubmitting: isSsoSubmitting, onSubmit: handleSsoEmailSubmit, hint: ssoEmailHint }));
292
+ const content = (jsxs("div", { className: "tw:w-full tw:max-w-md", children: [jsx("div", { className: "tw:rounded-xl tw:border tw:border-border tw:bg-card tw:p-6 tw:shadow-sm sm:tw:p-8", children: jsx("div", { className: "tw:flex tw:flex-col tw:gap-8", children: modeView }) }), jsx(FooterLinks, { links: footerLinks })] }));
293
+ if (!splitLayout) {
294
+ return (jsx("div", { className: cn("tw:min-h-svh tw:w-full tw:bg-background", className), children: jsx("div", { className: "tw:flex tw:min-h-svh tw:w-full tw:items-center tw:justify-center tw:p-6 sm:tw:p-12", children: content }) }));
295
+ }
296
+ return (jsx("div", { className: cn("tw:min-h-svh tw:w-full tw:bg-background", className), children: jsxs("div", { className: "tw:flex tw:min-h-svh tw:w-full", children: [shouldShowSplitPane ? (jsx(SplitMediaPane, { widthPercent: imageWidth, hasSplitImage: hasSplitImage, imageSrc: imageSrc, imageAlt: imageAlt, onImageError: () => setImageLoadError(true), overlayOpacity: overlayOpacity, imageOverlayContent: imageOverlayContent, testimonial: resolvedTestimonial })) : null, jsx("div", { className: cn("tw:flex tw:w-full tw:items-center tw:justify-center tw:p-6 sm:tw:p-12", shouldShowSplitPane && "tw:flex-1"), children: content })] }) }));
297
+ }
298
+
299
+ export { LoginScreen };
@@ -0,0 +1,82 @@
1
+ import type * as React from "react";
2
+ export type LoginScreenIdentifierMode = "email" | "phone" | "email-or-phone";
3
+ export type LoginScreenTab = {
4
+ id: string;
5
+ label: string;
6
+ href?: string;
7
+ external?: boolean;
8
+ onSelect?: (tabId: string) => void;
9
+ disabled?: boolean;
10
+ };
11
+ export type LoginScreenFooterLink = {
12
+ label: string;
13
+ href: string;
14
+ external?: boolean;
15
+ };
16
+ export type LoginScreenSubmitPayload = {
17
+ identifier: string;
18
+ password: string;
19
+ rememberMe: boolean;
20
+ tabId: string | null;
21
+ };
22
+ export type LoginScreenTestimonial = {
23
+ quote: string;
24
+ authorName: string;
25
+ authorTitle?: string;
26
+ };
27
+ export interface LoginScreenProps {
28
+ className?: string;
29
+ splitLayout?: boolean;
30
+ splitImageWidthPercent?: number;
31
+ imageSrc?: string;
32
+ imageAlt?: string;
33
+ imageOverlayContent?: React.ReactNode;
34
+ testimonial?: LoginScreenTestimonial | null;
35
+ imageOverlayOpacity?: number;
36
+ logo?: React.ReactNode;
37
+ title?: string;
38
+ description?: string;
39
+ tabs?: LoginScreenTab[];
40
+ defaultTabId?: string;
41
+ tabId?: string;
42
+ onTabChange?: (tabId: string) => void;
43
+ identifierMode?: LoginScreenIdentifierMode;
44
+ identifierLabel?: string;
45
+ identifierPlaceholder?: string;
46
+ passwordLabel?: string;
47
+ passwordPlaceholder?: string;
48
+ rememberMeLabel?: string;
49
+ forgotPasswordLabel?: string;
50
+ forgotPasswordTitle?: string;
51
+ forgotPasswordDescription?: string;
52
+ forgotPasswordEmailLabel?: string;
53
+ forgotPasswordEmailPlaceholder?: string;
54
+ forgotPasswordSubmitLabel?: string;
55
+ forgotPasswordBackLabel?: string;
56
+ forgotPasswordSentTitle?: string;
57
+ forgotPasswordSentDescriptionPrefix?: string;
58
+ forgotPasswordSentInstructions?: string;
59
+ forgotPasswordResendLabel?: string;
60
+ forgotPasswordResendDelaySeconds?: number;
61
+ submitLabel?: string;
62
+ submitLoadingLabel?: string;
63
+ ssoLabel?: string | null;
64
+ ssoButtonLabel?: string;
65
+ ssoEmailTitle?: string;
66
+ ssoEmailDescription?: string;
67
+ ssoEmailLabel?: string;
68
+ ssoEmailPlaceholder?: string;
69
+ ssoEmailSubmitLabel?: string;
70
+ ssoEmailHint?: string;
71
+ ssoEmailBackAriaLabel?: string;
72
+ legalNotice?: React.ReactNode;
73
+ footerLinks?: LoginScreenFooterLink[];
74
+ isSubmitting?: boolean;
75
+ isForgotPasswordSubmitting?: boolean;
76
+ isSsoSubmitting?: boolean;
77
+ onSubmit?: (payload: LoginScreenSubmitPayload) => void | Promise<void>;
78
+ onForgotPasswordSubmit?: (email: string) => void | Promise<void>;
79
+ onSsoClick?: () => void;
80
+ onSsoSubmit?: (email: string) => void | Promise<void>;
81
+ }
82
+ //# sourceMappingURL=login-screen.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-screen.types.d.ts","sourceRoot":"","sources":["../../src/components/login-screen.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,OAAO,GAAG,gBAAgB,CAAA;AAE5E,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACrC,WAAW,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAA;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAErC,cAAc,CAAC,EAAE,yBAAyB,CAAA;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,8BAA8B,CAAC,EAAE,MAAM,CAAA;IACvC,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,mCAAmC,CAAC,EAAE,MAAM,CAAA;IAC5C,8BAA8B,CAAC,EAAE,MAAM,CAAA;IACvC,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,gCAAgC,CAAC,EAAE,MAAM,CAAA;IAEzC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAA;IAErC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,eAAe,CAAC,EAAE,OAAO,CAAA;IAEzB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtE,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD"}
@@ -0,0 +1,114 @@
1
+ import * as React from "react";
2
+ import type { LoginScreenFooterLink, LoginScreenTab, LoginScreenTestimonial } from "./login-screen.types";
3
+ interface BrandHeaderProps {
4
+ logo?: React.ReactNode;
5
+ logoLoadError: boolean;
6
+ onLogoError: () => void;
7
+ title: string;
8
+ description?: string;
9
+ }
10
+ interface SignInViewProps {
11
+ tabs: LoginScreenTab[];
12
+ activeTabId: string | null;
13
+ tabListRef: React.RefObject<HTMLDivElement | null>;
14
+ onTabClick: (tab: LoginScreenTab) => void;
15
+ onTabKeyDown: (event: React.KeyboardEvent<HTMLButtonElement>, currentIndex: number) => void;
16
+ identifierId: string;
17
+ identifierLabel: string;
18
+ identifierType: React.HTMLInputTypeAttribute;
19
+ identifierPlaceholder: string;
20
+ identifier: string;
21
+ onIdentifierChange: (value: string) => void;
22
+ passwordId: string;
23
+ passwordLabel: string;
24
+ passwordPlaceholder: string;
25
+ password: string;
26
+ onPasswordChange: (value: string) => void;
27
+ showPassword: boolean;
28
+ onTogglePassword: () => void;
29
+ rememberId: string;
30
+ rememberMe: boolean;
31
+ onRememberMeChange: (checked: boolean) => void;
32
+ rememberMeLabel: string;
33
+ forgotPasswordLabel: string;
34
+ onForgotPasswordClick: () => void;
35
+ isSubmitting: boolean;
36
+ submitLabel: string;
37
+ submitLoadingLabel: string;
38
+ onSubmit: (event: React.FormEvent<HTMLFormElement>) => void | Promise<void>;
39
+ ssoLabel: string | null;
40
+ ssoButtonLabel: string;
41
+ onSsoClick: () => void | Promise<void>;
42
+ legalNotice: React.ReactNode;
43
+ logo?: React.ReactNode;
44
+ logoLoadError: boolean;
45
+ onLogoError: () => void;
46
+ title: string;
47
+ description?: string;
48
+ autoCompleteIdentifier?: string;
49
+ }
50
+ interface ForgotPasswordViewProps {
51
+ title: string;
52
+ description: string;
53
+ emailId: string;
54
+ emailLabel: string;
55
+ email: string;
56
+ onEmailChange: (value: string) => void;
57
+ emailPlaceholder: string;
58
+ submitLabel: string;
59
+ backLabel: string;
60
+ isSubmitting: boolean;
61
+ onSubmit: (event: React.FormEvent<HTMLFormElement>) => void | Promise<void>;
62
+ onBack: () => void;
63
+ error: string | null;
64
+ }
65
+ interface ForgotPasswordSentViewProps {
66
+ backAriaLabel: string;
67
+ onBack: () => void;
68
+ title: string;
69
+ descriptionPrefix: string;
70
+ email: string;
71
+ instructions: string;
72
+ resendLabel: string;
73
+ resendCountdown: number;
74
+ onResend: () => void | Promise<void>;
75
+ isResending: boolean;
76
+ error: string | null;
77
+ backLabel: string;
78
+ }
79
+ interface SsoEmailViewProps {
80
+ backAriaLabel: string;
81
+ onBack: () => void;
82
+ title: string;
83
+ description: string;
84
+ emailId: string;
85
+ emailLabel: string;
86
+ email: string;
87
+ onEmailChange: (value: string) => void;
88
+ emailPlaceholder: string;
89
+ submitLabel: string;
90
+ isSubmitting: boolean;
91
+ onSubmit: (event: React.FormEvent<HTMLFormElement>) => void | Promise<void>;
92
+ hint: string;
93
+ }
94
+ interface SplitMediaPaneProps {
95
+ widthPercent: number;
96
+ hasSplitImage: boolean;
97
+ imageSrc?: string;
98
+ imageAlt: string;
99
+ onImageError: () => void;
100
+ overlayOpacity: number;
101
+ imageOverlayContent?: React.ReactNode;
102
+ testimonial: LoginScreenTestimonial | null;
103
+ }
104
+ export declare function BrandHeader({ logo, logoLoadError, onLogoError, title, description, }: BrandHeaderProps): import("react/jsx-runtime").JSX.Element;
105
+ export declare function SignInView({ tabs, activeTabId, tabListRef, onTabClick, onTabKeyDown, identifierId, identifierLabel, identifierType, identifierPlaceholder, identifier, onIdentifierChange, passwordId, passwordLabel, passwordPlaceholder, password, onPasswordChange, showPassword, onTogglePassword, rememberId, rememberMe, onRememberMeChange, rememberMeLabel, forgotPasswordLabel, onForgotPasswordClick, isSubmitting, submitLabel, submitLoadingLabel, onSubmit, ssoLabel, ssoButtonLabel, onSsoClick, legalNotice, logo, logoLoadError, onLogoError, title, description, autoCompleteIdentifier, }: SignInViewProps): import("react/jsx-runtime").JSX.Element;
106
+ export declare function ForgotPasswordView({ title, description, emailId, emailLabel, email, onEmailChange, emailPlaceholder, submitLabel, backLabel, isSubmitting, onSubmit, onBack, error, }: ForgotPasswordViewProps): import("react/jsx-runtime").JSX.Element;
107
+ export declare function ForgotPasswordSentView({ backAriaLabel, onBack, title, descriptionPrefix, email, instructions, resendLabel, resendCountdown, onResend, isResending, error, backLabel, }: ForgotPasswordSentViewProps): import("react/jsx-runtime").JSX.Element;
108
+ export declare function SsoEmailView({ backAriaLabel, onBack, title, description, emailId, emailLabel, email, onEmailChange, emailPlaceholder, submitLabel, isSubmitting, onSubmit, hint, }: SsoEmailViewProps): import("react/jsx-runtime").JSX.Element;
109
+ export declare function SplitMediaPane({ widthPercent, hasSplitImage, imageSrc, imageAlt, onImageError, overlayOpacity, imageOverlayContent, testimonial, }: SplitMediaPaneProps): import("react/jsx-runtime").JSX.Element;
110
+ export declare function FooterLinks({ links }: {
111
+ links: LoginScreenFooterLink[];
112
+ }): import("react/jsx-runtime").JSX.Element | null;
113
+ export {};
114
+ //# sourceMappingURL=login-screen.views.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-screen.views.d.ts","sourceRoot":"","sources":["../../src/components/login-screen.views.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { EyeOff, Eye, Building2, ArrowLeft, LockKeyhole } from 'lucide-react';
4
+ import { cn } from '../lib/utils.js';
5
+ import { Button } from './button.js';
6
+ import { Checkbox } from './checkbox.js';
7
+ import { Input } from './input.js';
8
+ import { Label } from './label.js';
9
+
10
+ function getLinkAttributes(external) {
11
+ if (!external) {
12
+ return {};
13
+ }
14
+ return {
15
+ target: "_blank",
16
+ rel: "noopener noreferrer",
17
+ };
18
+ }
19
+ function BrandHeader({ logo, logoLoadError, onLogoError, title, description, }) {
20
+ return (jsxs("div", { className: "tw:flex tw:flex-col tw:items-center tw:gap-4 tw:text-center", children: [logo ??
21
+ (logoLoadError ? (jsx("div", { className: "tw:flex tw:size-16 tw:items-center tw:justify-center tw:rounded-xl tw:bg-sky-100 tw:text-xl tw:font-semibold tw:text-primary", children: "B" })) : (jsx("img", { src: "/blaze-logo.png", alt: "Blaze", className: "tw:h-16 tw:w-16", onError: onLogoError }))), jsxs("div", { className: "tw:space-y-2", children: [jsx("h1", { className: "tw:text-3xl tw:font-semibold tw:tracking-tight", children: title }), description ? (jsx("p", { className: "tw:text-sm tw:text-muted-foreground", children: description })) : null] })] }));
22
+ }
23
+ function SignInView({ tabs, activeTabId, tabListRef, onTabClick, onTabKeyDown, identifierId, identifierLabel, identifierType, identifierPlaceholder, identifier, onIdentifierChange, passwordId, passwordLabel, passwordPlaceholder, password, onPasswordChange, showPassword, onTogglePassword, rememberId, rememberMe, onRememberMeChange, rememberMeLabel, forgotPasswordLabel, onForgotPasswordClick, isSubmitting, submitLabel, submitLoadingLabel, onSubmit, ssoLabel, ssoButtonLabel, onSsoClick, legalNotice, logo, logoLoadError, onLogoError, title, description, autoCompleteIdentifier, }) {
24
+ return (jsxs(Fragment, { children: [jsx(BrandHeader, { logo: logo, logoLoadError: logoLoadError, onLogoError: onLogoError, title: title, description: description }), tabs.length > 0 ? (jsx("div", { role: "tablist", ref: tabListRef, "aria-label": "Login destinations", className: "tw:inline-flex tw:w-full tw:items-center tw:rounded-lg tw:bg-muted tw:p-1", children: tabs.map((tab, index) => {
25
+ const isActive = tab.id === activeTabId;
26
+ return (jsx("button", { type: "button", role: "tab", "aria-selected": isActive, "aria-disabled": tab.disabled, tabIndex: isActive ? 0 : -1, disabled: tab.disabled, onClick: () => onTabClick(tab), onKeyDown: (event) => onTabKeyDown(event, index), className: cn("tw:flex-1 tw:rounded-md tw:px-3 tw:py-2 tw:text-sm tw:font-medium tw:transition-colors", "tw:outline-none tw:focus-visible:ring-2 tw:focus-visible:ring-ring tw:focus-visible:ring-offset-2", isActive
27
+ ? "tw:bg-background tw:text-foreground tw:shadow-sm"
28
+ : "tw:text-muted-foreground hover:tw:text-foreground", tab.disabled && "tw:cursor-not-allowed tw:opacity-50"), children: tab.label }, tab.id));
29
+ }) })) : null, jsxs("form", { onSubmit: onSubmit, className: "tw:flex tw:flex-col tw:gap-5", children: [jsxs("div", { className: "tw:flex tw:flex-col tw:gap-2", children: [jsx(Label, { htmlFor: identifierId, children: identifierLabel }), jsx(Input, { id: identifierId, type: identifierType, placeholder: identifierPlaceholder, value: identifier, onChange: (event) => onIdentifierChange(event.target.value), autoComplete: autoCompleteIdentifier, className: "tw:h-12", disabled: isSubmitting, required: true })] }), jsxs("div", { className: "tw:flex tw:flex-col tw:gap-2", children: [jsx(Label, { htmlFor: passwordId, children: passwordLabel }), jsxs("div", { className: "tw:relative", children: [jsx(Input, { id: passwordId, type: showPassword ? "text" : "password", placeholder: passwordPlaceholder, value: password, onChange: (event) => onPasswordChange(event.target.value), autoComplete: "current-password", className: "tw:h-12 tw:pr-10", disabled: isSubmitting, required: true }), jsx("button", { type: "button", className: "tw:absolute tw:right-3 tw:top-1/2 tw:-translate-y-1/2 tw:text-muted-foreground hover:tw:text-foreground", onClick: onTogglePassword, "aria-label": showPassword ? "Hide password" : "Show password", children: showPassword ? (jsx(EyeOff, { className: "tw:size-4" })) : (jsx(Eye, { className: "tw:size-4" })) })] })] }), jsxs("div", { className: "tw:flex tw:items-center tw:justify-between", children: [jsxs("div", { className: "tw:flex tw:items-center tw:gap-2", children: [jsx(Checkbox, { id: rememberId, checked: rememberMe, onCheckedChange: (checked) => onRememberMeChange(checked === true), disabled: isSubmitting }), jsx(Label, { htmlFor: rememberId, className: "tw:cursor-pointer tw:text-sm tw:font-normal", children: rememberMeLabel })] }), jsx("button", { type: "button", className: "tw:text-sm tw:text-primary hover:tw:underline", onClick: onForgotPasswordClick, disabled: isSubmitting, children: forgotPasswordLabel })] }), jsx(Button, { type: "submit", className: "tw:h-12 tw:w-full", loading: isSubmitting, loadingText: submitLoadingLabel, disabled: !identifier.trim() || !password.trim(), children: submitLabel })] }), ssoLabel !== null ? (jsxs(Fragment, { children: [jsxs("div", { className: "tw:relative", children: [jsx("div", { className: "tw:absolute tw:inset-0 tw:flex tw:items-center", children: jsx("span", { className: "tw:w-full tw:border-t tw:border-border" }) }), jsx("div", { className: "tw:relative tw:flex tw:justify-center tw:text-xs tw:uppercase", children: jsx("span", { className: "tw:bg-card tw:px-2 tw:text-muted-foreground", children: ssoLabel }) })] }), jsxs(Button, { type: "button", variant: "outline", className: "tw:h-12 tw:w-full", onClick: onSsoClick, disabled: isSubmitting, children: [jsx(Building2, { className: "tw:size-4" }), ssoButtonLabel] })] })) : null, jsx("p", { className: "tw:text-center tw:text-xs tw:text-muted-foreground", children: legalNotice })] }));
30
+ }
31
+ function ForgotPasswordView({ title, description, emailId, emailLabel, email, onEmailChange, emailPlaceholder, submitLabel, backLabel, isSubmitting, onSubmit, onBack, error, }) {
32
+ return (jsxs("form", { onSubmit: onSubmit, className: "tw:flex tw:flex-col tw:gap-5", children: [jsxs("div", { className: "tw:space-y-2 tw:text-center", children: [jsx("h2", { className: "tw:text-xl tw:font-semibold", children: title }), jsx("p", { className: "tw:text-sm tw:text-muted-foreground", children: description })] }), jsxs("div", { className: "tw:flex tw:flex-col tw:gap-2", children: [jsx(Label, { htmlFor: emailId, children: emailLabel }), jsx(Input, { id: emailId, type: "email", value: email, onChange: (event) => onEmailChange(event.target.value), placeholder: emailPlaceholder, autoComplete: "email", className: "tw:h-12", disabled: isSubmitting, required: true })] }), jsx(Button, { type: "submit", className: "tw:h-12 tw:w-full", loading: isSubmitting, loadingText: submitLabel, disabled: !email.trim(), children: submitLabel }), error ? jsx("p", { className: "tw:text-center tw:text-sm tw:text-destructive", children: error }) : null, jsx(Button, { type: "button", variant: "ghost", className: "tw:w-full", onClick: onBack, disabled: isSubmitting, children: backLabel })] }));
33
+ }
34
+ function ForgotPasswordSentView({ backAriaLabel, onBack, title, descriptionPrefix, email, instructions, resendLabel, resendCountdown, onResend, isResending, error, backLabel, }) {
35
+ return (jsxs("div", { className: "tw:flex tw:flex-col tw:gap-8", children: [jsx("div", { className: "tw:flex tw:items-center", children: jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onBack, "aria-label": backAriaLabel, children: jsx(ArrowLeft, { className: "tw:size-4" }) }) }), jsxs("div", { className: "tw:flex tw:flex-col tw:items-center tw:gap-4 tw:text-center", children: [jsx("div", { className: "tw:flex tw:size-12 tw:items-center tw:justify-center tw:rounded-xl tw:bg-sky-100 tw:text-primary", children: jsx(LockKeyhole, { className: "tw:size-6" }) }), jsx("h2", { className: "tw:text-3xl tw:font-semibold tw:tracking-tight", children: title }), jsxs("p", { className: "tw:text-sm tw:text-muted-foreground", children: [descriptionPrefix, jsx("br", {}), email] })] }), jsxs("div", { className: "tw:flex tw:flex-col tw:gap-4 tw:text-center", children: [jsx("p", { className: "tw:text-sm tw:text-muted-foreground", children: instructions }), resendCountdown > 0 ? (jsxs("p", { className: "tw:text-sm tw:text-muted-foreground", children: [resendLabel, " in ", resendCountdown, "s"] })) : (jsx(Button, { type: "button", variant: "outline", className: "tw:mx-auto", loading: isResending, loadingText: `${resendLabel}...`, onClick: onResend, children: resendLabel })), error ? jsx("p", { className: "tw:text-center tw:text-sm tw:text-destructive", children: error }) : null, jsx(Button, { type: "button", variant: "ghost", className: "tw:w-full", onClick: onBack, disabled: isResending, children: backLabel })] })] }));
36
+ }
37
+ function SsoEmailView({ backAriaLabel, onBack, title, description, emailId, emailLabel, email, onEmailChange, emailPlaceholder, submitLabel, isSubmitting, onSubmit, hint, }) {
38
+ return (jsxs("div", { className: "tw:flex tw:flex-col tw:gap-8", children: [jsx("div", { className: "tw:flex tw:items-center", children: jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onBack, "aria-label": backAriaLabel, children: jsx(ArrowLeft, { className: "tw:size-4" }) }) }), jsxs("div", { className: "tw:flex tw:flex-col tw:items-center tw:gap-4 tw:text-center", children: [jsx("div", { className: "tw:flex tw:size-12 tw:items-center tw:justify-center tw:rounded-xl tw:bg-sky-100 tw:text-primary", children: jsx(LockKeyhole, { className: "tw:size-6" }) }), jsxs("div", { className: "tw:space-y-2", children: [jsx("h2", { className: "tw:text-3xl tw:font-semibold tw:tracking-tight", children: title }), jsx("p", { className: "tw:text-sm tw:text-muted-foreground", children: description })] })] }), jsxs("form", { onSubmit: onSubmit, className: "tw:flex tw:flex-col tw:gap-5", children: [jsxs("div", { className: "tw:flex tw:flex-col tw:gap-2", children: [jsx(Label, { htmlFor: emailId, children: emailLabel }), jsx(Input, { id: emailId, type: "email", value: email, onChange: (event) => onEmailChange(event.target.value), placeholder: emailPlaceholder, autoComplete: "email", className: "tw:h-12", disabled: isSubmitting, required: true })] }), jsx(Button, { type: "submit", className: "tw:h-12 tw:w-full", loading: isSubmitting, loadingText: submitLabel, disabled: !email.trim(), children: submitLabel })] }), jsx("p", { className: "tw:text-center tw:text-sm tw:text-muted-foreground", children: hint })] }));
39
+ }
40
+ function SplitMediaPane({ widthPercent, hasSplitImage, imageSrc, imageAlt, onImageError, overlayOpacity, imageOverlayContent, testimonial, }) {
41
+ const splitImageStyle = {
42
+ width: `${widthPercent}%`,
43
+ };
44
+ return (jsxs("div", { className: "tw:relative tw:overflow-hidden tw:shrink-0", style: splitImageStyle, children: [hasSplitImage ? (jsx("img", { src: imageSrc, alt: imageAlt, className: "tw:h-full tw:w-full tw:object-cover", onError: onImageError })) : (jsx("div", { className: "tw:h-full tw:w-full tw:bg-gradient-to-br tw:from-slate-700 tw:via-slate-600 tw:to-slate-900" })), jsx("div", { className: "tw:absolute tw:inset-0 tw:bg-black", style: { opacity: overlayOpacity } }), jsx("div", { className: "tw:absolute tw:inset-0 tw:bg-gradient-to-t tw:from-black/85 tw:via-black/45 tw:to-black/20" }), imageOverlayContent || testimonial ? (jsx("div", { className: "tw:absolute tw:inset-x-0 tw:bottom-0 tw:z-10 tw:p-8 tw:pb-12", children: imageOverlayContent ?? (jsxs("blockquote", { className: "tw:text-white", children: [jsx("p", { className: "tw:text-lg tw:font-medium tw:leading-relaxed tw:[text-shadow:0_2px_12px_rgba(0,0,0,0.85)]", children: testimonial?.quote }), jsxs("footer", { className: "tw:mt-4 tw:[text-shadow:0_2px_8px_rgba(0,0,0,0.8)]", children: [jsx("p", { className: "tw:text-sm tw:font-semibold tw:text-white", children: testimonial?.authorName }), testimonial?.authorTitle ? (jsx("p", { className: "tw:text-sm tw:text-white/80", children: testimonial.authorTitle })) : null] })] })) })) : null] }));
45
+ }
46
+ function FooterLinks({ links }) {
47
+ if (links.length === 0) {
48
+ return null;
49
+ }
50
+ return (jsx("div", { className: "tw:mt-6 tw:flex tw:flex-wrap tw:items-center tw:justify-center tw:gap-4 tw:text-xs tw:text-muted-foreground", children: links.map((link, index) => (jsxs(React.Fragment, { children: [index > 0 ? jsx("span", { "aria-hidden": "true", children: "·" }) : null, jsx("a", { href: link.href, ...getLinkAttributes(link.external), className: "hover:tw:text-foreground hover:tw:underline", children: link.label })] }, `${link.label}-${index}`))) }));
51
+ }
52
+
53
+ export { BrandHeader, FooterLinks, ForgotPasswordSentView, ForgotPasswordView, SignInView, SplitMediaPane, SsoEmailView };
@@ -0,0 +1,15 @@
1
+ export interface SearchBarProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ onSearch: (value: string) => void;
5
+ onClear: () => void;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ loading?: boolean;
9
+ applyLabel?: string;
10
+ clearLabel?: string;
11
+ className?: string;
12
+ inputAriaLabel?: string;
13
+ }
14
+ export declare function SearchBar({ value, onChange, onSearch, onClear, placeholder, disabled, loading, applyLabel, clearLabel, className, inputAriaLabel, }: SearchBarProps): import("react/jsx-runtime").JSX.Element;
15
+ //# sourceMappingURL=search-bar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-bar.d.ts","sourceRoot":"","sources":["../../src/components/search-bar.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,WAAyB,EACzB,QAAgB,EAChB,OAAe,EACf,UAAoB,EACpB,UAAoB,EACpB,SAAS,EACT,cAA+B,GAChC,EAAE,cAAc,2CAsEhB"}
@@ -0,0 +1,25 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { SearchIcon, XIcon } from 'lucide-react';
3
+ import { cn } from '../lib/utils.js';
4
+ import { Input } from './input.js';
5
+ import { Button } from './button.js';
6
+
7
+ function SearchBar({ value, onChange, onSearch, onClear, placeholder = "Search...", disabled = false, loading = false, applyLabel = "Apply", clearLabel = "Clear", className, inputAriaLabel = "Search input", }) {
8
+ const handleKeyDown = (e) => {
9
+ if (e.key === "Enter") {
10
+ e.preventDefault();
11
+ onSearch(value);
12
+ }
13
+ else if (e.key === "Escape") {
14
+ e.preventDefault();
15
+ onClear();
16
+ }
17
+ };
18
+ const handleClearClick = () => {
19
+ onChange("");
20
+ onClear();
21
+ };
22
+ return (jsxs("div", { "data-slot": "search-bar", className: cn("tw:!flex tw:!w-full tw:!gap-2", className), children: [jsxs("div", { className: "tw:!relative tw:!flex-1", children: [jsx(SearchIcon, { className: "tw:!absolute tw:!left-3 tw:!top-1/2 tw:!-translate-y-1/2 tw:!h-4 tw:!w-4 tw:text-muted-foreground tw:!pointer-events-none", "aria-hidden": "true" }), jsx(Input, { type: "text", value: value, onChange: (e) => onChange(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, className: "tw:!pl-9", "aria-label": inputAriaLabel }), value && !disabled && (jsx("button", { type: "button", onClick: handleClearClick, className: "tw:!absolute tw:!right-3 tw:!top-1/2 tw:!-translate-y-1/2 tw:!h-4 tw:!w-4 tw:text-muted-foreground hover:tw:text-foreground tw:transition-colors tw:!cursor-pointer", "aria-label": "Clear search", children: jsx(XIcon, { className: "tw:!h-4 tw:!w-4" }) }))] }), jsx(Button, { variant: "default", size: "sm", onClick: () => onSearch(value), disabled: disabled || !value, loading: loading, className: "tw:!min-w-[80px]", "aria-label": "Apply search", children: applyLabel }), jsx(Button, { variant: "outline", size: "sm", onClick: handleClearClick, disabled: disabled || !value, className: "tw:!min-w-[80px]", "aria-label": "Clear search", children: clearLabel })] }));
23
+ }
24
+
25
+ export { SearchBar };