@getgreenline/blaze-ui 1.0.25 → 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.
@@ -0,0 +1,4 @@
1
+ import type { LoginScreenProps } from "./login-screen.types";
2
+ export type { LoginScreenIdentifierMode, LoginScreenTab, LoginScreenFooterLink, LoginScreenSubmitPayload, LoginScreenTestimonial, LoginScreenProps, } from "./login-screen.types";
3
+ export declare function LoginScreen({ className, splitLayout, splitImageWidthPercent, imageSrc, imageAlt, imageOverlayContent, testimonial, imageOverlayOpacity, logo, title, description, tabs, defaultTabId, tabId, onTabChange, identifierMode, identifierLabel, identifierPlaceholder, passwordLabel, passwordPlaceholder, rememberMeLabel, forgotPasswordLabel, forgotPasswordTitle, forgotPasswordDescription, forgotPasswordEmailLabel, forgotPasswordEmailPlaceholder, forgotPasswordSubmitLabel, forgotPasswordBackLabel, forgotPasswordSentTitle, forgotPasswordSentDescriptionPrefix, forgotPasswordSentInstructions, forgotPasswordResendLabel, forgotPasswordResendDelaySeconds, submitLabel, submitLoadingLabel, ssoLabel, ssoButtonLabel, ssoEmailTitle, ssoEmailDescription, ssoEmailLabel, ssoEmailPlaceholder, ssoEmailSubmitLabel, ssoEmailHint, ssoEmailBackAriaLabel, legalNotice, footerLinks, isSubmitting, isForgotPasswordSubmitting, isSsoSubmitting, onSubmit, onForgotPasswordSubmit, onSsoClick, onSsoSubmit, }: LoginScreenProps): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=login-screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-screen.d.ts","sourceRoot":"","sources":["../../src/components/login-screen.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAwF7B,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,WAAkB,EAClB,sBAA2B,EAC3B,QAA0B,EAC1B,QAA6B,EAC7B,mBAAmB,EACnB,WAAiC,EACjC,mBAA0B,EAC1B,IAAI,EACJ,KAA0B,EAC1B,WAAoD,EACpD,IAAS,EACT,YAAY,EACZ,KAAK,EACL,WAAW,EACX,cAAwB,EACxB,eAAe,EACf,qBAAqB,EACrB,aAA0B,EAC1B,mBAA2C,EAC3C,eAA+B,EAC/B,mBAAwC,EACxC,mBAA2C,EAC3C,yBAAmF,EACnF,wBAAkC,EAClC,8BAA2D,EAC3D,yBAA6C,EAC7C,uBAA2C,EAC3C,uBAA4C,EAC5C,mCAAwE,EACxE,8BAAmI,EACnI,yBAA0C,EAC1C,gCAAqC,EACrC,WAAsB,EACtB,kBAAoC,EACpC,QAAe,EACf,cAA+C,EAC/C,aAAkC,EAClC,mBAAyF,EACzF,aAA+B,EAC/B,mBAAwC,EACxC,mBAAgC,EAChC,YAAkF,EAClF,qBAAiC,EACjC,WAAW,EACX,WAAkC,EAClC,YAAoB,EACpB,0BAAkC,EAClC,eAAuB,EACvB,QAAQ,EACR,sBAAsB,EACtB,UAAU,EACV,WAAW,GACZ,EAAE,gBAAgB,2CAsclB"}
@@ -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":"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"}
@@ -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 };
package/dist/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export * from "./components/spinner";
38
38
  export * from "./components/table";
39
39
  export * from "./components/data-table";
40
40
  export * from "./components/tabs";
41
+ export * from "./components/login-screen";
41
42
  export * from "./components/textarea";
42
43
  export * from "./components/toggle";
43
44
  export * from "./components/tooltip";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ export { Spinner } from './components/spinner.js';
38
38
  export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from './components/table.js';
39
39
  export { DataTable } from './components/data-table.js';
40
40
  export { Tabs, TabsContent, TabsList, TabsTrigger } from './components/tabs.js';
41
+ export { LoginScreen } from './components/login-screen.js';
41
42
  export { Textarea } from './components/textarea.js';
42
43
  export { Toggle, toggleVariants } from './components/toggle.js';
43
44
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './components/tooltip.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getgreenline/blaze-ui",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@base-ui/react": "^1.1.0",