@solid/react-component 0.1.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/index.cjs ADDED
@@ -0,0 +1,945 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ AuthGuard: () => AuthGuard,
25
+ DEFAULT_CONFIG: () => DEFAULT_CONFIG,
26
+ LoginFormControl: () => LoginFormControl,
27
+ SolidLoginNavigationProvider: () => SolidLoginNavigationProvider,
28
+ SolidLoginPage: () => SolidLoginPage,
29
+ useSolidLogin: () => useSolidLogin,
30
+ useSolidLoginNavigation: () => useSolidLoginNavigation,
31
+ validateIssuerUrl: () => validateIssuerUrl
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // src/login/AuthGuard.tsx
36
+ var import_react2 = require("react");
37
+ var import_solid_react = require("@ldo/solid-react");
38
+
39
+ // src/login/NavigationContext.tsx
40
+ var import_react = require("react");
41
+ var import_jsx_runtime = require("react/jsx-runtime");
42
+ var SolidLoginNavigationContext = (0, import_react.createContext)(null);
43
+ function SolidLoginNavigationProvider({
44
+ navigation,
45
+ config,
46
+ children
47
+ }) {
48
+ const fullConfig = {
49
+ loginPath: config?.loginPath ?? "/login",
50
+ homePath: config?.homePath ?? "/"
51
+ };
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SolidLoginNavigationContext.Provider, { value: { navigation, config: fullConfig }, children });
53
+ }
54
+ function useSolidLoginNavigation() {
55
+ return (0, import_react.useContext)(SolidLoginNavigationContext);
56
+ }
57
+
58
+ // src/login/AuthGuard.tsx
59
+ var import_jsx_runtime2 = require("react/jsx-runtime");
60
+ var RETURN_TO_KEY = "solid-login-returnTo";
61
+ function storageGet(storage, key) {
62
+ try {
63
+ return storage.getItem(key);
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ function storageSet(storage, key, value) {
69
+ try {
70
+ storage.setItem(key, value);
71
+ } catch {
72
+ }
73
+ }
74
+ function storageRemove(storage, key) {
75
+ try {
76
+ storage.removeItem(key);
77
+ } catch {
78
+ }
79
+ }
80
+ function isValidReturnPath(value) {
81
+ return typeof value === "string" && value.startsWith("/") && !value.startsWith("//");
82
+ }
83
+ function resolveReturnTo(searchParams, loginPath, homePath) {
84
+ const fromParam = searchParams.get("returnTo");
85
+ if (isValidReturnPath(fromParam)) return fromParam;
86
+ if (typeof window === "undefined") return homePath;
87
+ const fromStorage = storageGet(sessionStorage, RETURN_TO_KEY);
88
+ if (isValidReturnPath(fromStorage)) return fromStorage;
89
+ const browserPath = window.location.pathname;
90
+ if (browserPath !== loginPath && browserPath !== homePath && isValidReturnPath(browserPath)) {
91
+ return browserPath;
92
+ }
93
+ return homePath;
94
+ }
95
+ var defaultFallback = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
96
+ "div",
97
+ {
98
+ style: {
99
+ display: "flex",
100
+ minHeight: "100vh",
101
+ alignItems: "center",
102
+ justifyContent: "center",
103
+ background: "#fff"
104
+ },
105
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Loading..." })
106
+ }
107
+ );
108
+ function AuthGuardContent({
109
+ children,
110
+ fallback = defaultFallback
111
+ }) {
112
+ const { session, ranInitialAuthCheck = true } = (0, import_solid_react.useSolidAuth)();
113
+ const nav = useSolidLoginNavigation();
114
+ const pathname = nav?.navigation.getPathname() ?? "/";
115
+ const searchParams = nav?.navigation.getSearchParams() ?? { has: () => false, get: () => null };
116
+ const config = nav?.config ?? { loginPath: "/login", homePath: "/" };
117
+ const isOAuthCallback = searchParams.has("code") || searchParams.has("state");
118
+ const isLoginPage = pathname === config.loginPath;
119
+ const hasRedirectedRef = (0, import_react2.useRef)(false);
120
+ const prevPathnameRef = (0, import_react2.useRef)(pathname);
121
+ if (prevPathnameRef.current !== pathname) {
122
+ prevPathnameRef.current = pathname;
123
+ hasRedirectedRef.current = false;
124
+ }
125
+ (0, import_react2.useEffect)(() => {
126
+ if (typeof window === "undefined" || !isLoginPage || session.isLoggedIn) return;
127
+ const returnTo = searchParams.get("returnTo");
128
+ if (isValidReturnPath(returnTo)) {
129
+ storageSet(sessionStorage, RETURN_TO_KEY, returnTo);
130
+ }
131
+ }, [isLoginPage, session.isLoggedIn]);
132
+ (0, import_react2.useEffect)(() => {
133
+ if (!nav || !ranInitialAuthCheck || hasRedirectedRef.current) return;
134
+ const { navigation } = nav;
135
+ const sp = navigation.getSearchParams();
136
+ const path = navigation.getPathname();
137
+ const isCallback = sp.has("code") || sp.has("state");
138
+ const isLogin = path === config.loginPath;
139
+ if (isCallback && session.isLoggedIn) {
140
+ hasRedirectedRef.current = true;
141
+ let target = resolveReturnTo(sp, config.loginPath, config.homePath);
142
+ if (target === config.loginPath) target = config.homePath;
143
+ storageRemove(sessionStorage, RETURN_TO_KEY);
144
+ navigation.redirect(target);
145
+ return;
146
+ }
147
+ if (session.isLoggedIn && isLogin && !isCallback) {
148
+ hasRedirectedRef.current = true;
149
+ let target = resolveReturnTo(sp, config.loginPath, config.homePath);
150
+ if (target === config.loginPath) target = config.homePath;
151
+ storageRemove(sessionStorage, RETURN_TO_KEY);
152
+ navigation.replace(target);
153
+ return;
154
+ }
155
+ if (!session.isLoggedIn && !isLogin && !isCallback) {
156
+ hasRedirectedRef.current = true;
157
+ const current = path || "/";
158
+ if (current !== config.loginPath && current !== config.homePath) {
159
+ storageSet(sessionStorage, RETURN_TO_KEY, current);
160
+ }
161
+ const loginUrl = current === config.loginPath || current === config.homePath ? config.loginPath : `${config.loginPath}?returnTo=${encodeURIComponent(current)}`;
162
+ navigation.replace(loginUrl);
163
+ }
164
+ }, [ranInitialAuthCheck, session.isLoggedIn, pathname, nav, config.loginPath, config.homePath]);
165
+ if (!nav) {
166
+ if (process.env.NODE_ENV !== "production") {
167
+ console.warn(
168
+ "@solid/react-component: AuthGuard requires SolidLoginNavigationProvider (or use '@solid/react-component/login/next')"
169
+ );
170
+ }
171
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
172
+ }
173
+ if (!ranInitialAuthCheck) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
174
+ if (isOAuthCallback) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
175
+ if (!session.isLoggedIn && !isLoginPage) return null;
176
+ if (session.isLoggedIn && isLoginPage) return null;
177
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
178
+ }
179
+ function AuthGuard(props) {
180
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Suspense, { fallback: props.fallback ?? defaultFallback, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthGuardContent, { ...props }) });
181
+ }
182
+
183
+ // src/login/SolidLoginPage.tsx
184
+ var import_react4 = require("react");
185
+
186
+ // src/login/useSolidLogin.ts
187
+ var import_react3 = require("react");
188
+ var import_solid_react2 = require("@ldo/solid-react");
189
+ var DEFAULT_PRESETS = [
190
+ { label: "Solid Community", value: "https://solidcommunity.net/", secondaryLabel: "https://solidcommunity.net/" },
191
+ { label: "Inrupt", value: "https://login.inrupt.com", secondaryLabel: "https://login.inrupt.com" }
192
+ ];
193
+ function validateIssuerUrl(url) {
194
+ if (!url.trim()) return { valid: false, error: "Please enter a Solid Identity Provider URL" };
195
+ try {
196
+ const parsed = new URL(url);
197
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
198
+ return { valid: false, error: "URL must start with http:// or https://" };
199
+ }
200
+ } catch {
201
+ return { valid: false, error: "Please enter a valid URL" };
202
+ }
203
+ return { valid: true, error: null };
204
+ }
205
+ function useSolidLogin(options = {}) {
206
+ const { session, login } = (0, import_solid_react2.useSolidAuth)();
207
+ const [issuerInput, setIssuerInput] = (0, import_react3.useState)(options.defaultIssuer ?? "");
208
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
209
+ const [error, setError] = (0, import_react3.useState)(null);
210
+ const presetIssuers = options.presetIssuers ?? DEFAULT_PRESETS;
211
+ const validateAndSubmit = (0, import_react3.useCallback)(async () => {
212
+ const trimmed = issuerInput.trim();
213
+ const { valid, error: err } = validateIssuerUrl(trimmed);
214
+ if (!valid) {
215
+ setError(err);
216
+ return false;
217
+ }
218
+ setError(null);
219
+ setIsLoading(true);
220
+ try {
221
+ await login(trimmed, options.redirectUrl ? { redirectUrl: options.redirectUrl } : void 0);
222
+ return true;
223
+ } catch (e) {
224
+ console.error("Login failed:", e);
225
+ setIsLoading(false);
226
+ return false;
227
+ }
228
+ }, [issuerInput, login, options.redirectUrl]);
229
+ const setIssuer = (0, import_react3.useCallback)((value) => {
230
+ setIssuerInput(value);
231
+ setError(null);
232
+ }, []);
233
+ return {
234
+ session,
235
+ issuerInput,
236
+ setIssuerInput: setIssuer,
237
+ isLoading,
238
+ error,
239
+ presetIssuers,
240
+ validateAndSubmit,
241
+ login
242
+ };
243
+ }
244
+
245
+ // src/login/SolidLoginPage.tsx
246
+ var import_jsx_runtime3 = require("react/jsx-runtime");
247
+ var defaultTitle = "Sign in";
248
+ var defaultSubtitle = "to continue";
249
+ function ChevronDownIcon({
250
+ open,
251
+ style
252
+ }) {
253
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
254
+ "svg",
255
+ {
256
+ width: "20",
257
+ height: "20",
258
+ viewBox: "0 0 24 24",
259
+ fill: "none",
260
+ stroke: "currentColor",
261
+ strokeWidth: "2",
262
+ strokeLinecap: "round",
263
+ strokeLinejoin: "round",
264
+ style: {
265
+ transform: open ? "rotate(180deg)" : "rotate(0deg)",
266
+ transition: "transform 0.2s",
267
+ ...style
268
+ },
269
+ "aria-hidden": true,
270
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m6 9 6 6 6-6" })
271
+ }
272
+ );
273
+ }
274
+ function GitHubIcon({ style }) {
275
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
276
+ "svg",
277
+ {
278
+ width: "16",
279
+ height: "16",
280
+ fill: "currentColor",
281
+ viewBox: "0 0 24 24",
282
+ "aria-hidden": true,
283
+ style,
284
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
285
+ "path",
286
+ {
287
+ fillRule: "evenodd",
288
+ d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z",
289
+ clipRule: "evenodd"
290
+ }
291
+ )
292
+ }
293
+ );
294
+ }
295
+ function ReportIssueIcon({ style }) {
296
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
297
+ "svg",
298
+ {
299
+ width: "16",
300
+ height: "16",
301
+ fill: "none",
302
+ stroke: "currentColor",
303
+ viewBox: "0 0 24 24",
304
+ "aria-hidden": true,
305
+ style,
306
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
307
+ "path",
308
+ {
309
+ strokeLinecap: "round",
310
+ strokeLinejoin: "round",
311
+ strokeWidth: 2,
312
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
313
+ }
314
+ )
315
+ }
316
+ );
317
+ }
318
+ function ButtonSpinner() {
319
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
320
+ "span",
321
+ {
322
+ style: {
323
+ display: "inline-block",
324
+ width: 14,
325
+ height: 14,
326
+ border: "2px solid transparent",
327
+ borderTopColor: "currentColor",
328
+ borderRadius: "50%",
329
+ animation: "solid-login-spin 0.6s linear infinite",
330
+ marginRight: 8,
331
+ verticalAlign: "middle"
332
+ }
333
+ }
334
+ );
335
+ }
336
+ function SolidLoginPage({
337
+ onAlreadyLoggedIn,
338
+ redirectUrl,
339
+ defaultIssuer,
340
+ presetIssuers,
341
+ logo,
342
+ logoAlt = "Logo",
343
+ title = defaultTitle,
344
+ subtitle = defaultSubtitle,
345
+ inputPlaceholder = "Enter your provider URL or select from the list",
346
+ inputLabel = "Solid Identity Provider",
347
+ buttonLabel = "Next",
348
+ buttonLoadingLabel = "Signing in...",
349
+ className = "",
350
+ footerGitHubUrl,
351
+ footerIssuesUrl,
352
+ renderLogo,
353
+ renderForm,
354
+ renderFooter
355
+ }) {
356
+ const {
357
+ session,
358
+ issuerInput,
359
+ setIssuerInput,
360
+ isLoading,
361
+ error,
362
+ presetIssuers: presets,
363
+ validateAndSubmit
364
+ } = useSolidLogin({ defaultIssuer, presetIssuers, redirectUrl });
365
+ const [showDropdown, setShowDropdown] = (0, import_react4.useState)(false);
366
+ const [highlightedIndex, setHighlightedIndex] = (0, import_react4.useState)(-1);
367
+ const inputRef = (0, import_react4.useRef)(null);
368
+ const dropdownRef = (0, import_react4.useRef)(null);
369
+ const generatedId = (0, import_react4.useId)();
370
+ const inputId = `solid-login-combobox-${generatedId}`;
371
+ const listboxId = `${inputId}-listbox`;
372
+ (0, import_react4.useEffect)(() => {
373
+ if (session.isLoggedIn && onAlreadyLoggedIn) onAlreadyLoggedIn();
374
+ }, [session.isLoggedIn, onAlreadyLoggedIn]);
375
+ (0, import_react4.useEffect)(() => {
376
+ const handleClickOutside = (e) => {
377
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target) && inputRef.current && !inputRef.current.contains(e.target)) {
378
+ setShowDropdown(false);
379
+ }
380
+ };
381
+ if (showDropdown) {
382
+ document.addEventListener("mousedown", handleClickOutside);
383
+ return () => document.removeEventListener("mousedown", handleClickOutside);
384
+ }
385
+ }, [showDropdown]);
386
+ const filteredOptions = (0, import_react4.useMemo)(() => {
387
+ if (!issuerInput.trim()) return presets;
388
+ const q = issuerInput.toLowerCase();
389
+ return presets.filter(
390
+ (o) => o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q) || o.secondaryLabel && o.secondaryLabel.toLowerCase().includes(q)
391
+ );
392
+ }, [issuerInput, presets]);
393
+ if (session.isLoggedIn) return null;
394
+ const handleSubmit = (e) => {
395
+ e.preventDefault();
396
+ validateAndSubmit();
397
+ };
398
+ const handleSelect = (option) => {
399
+ setIssuerInput(option.value);
400
+ setShowDropdown(false);
401
+ setHighlightedIndex(-1);
402
+ inputRef.current?.focus();
403
+ };
404
+ const handleKeyDown = (e) => {
405
+ if (!showDropdown) {
406
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
407
+ e.preventDefault();
408
+ setShowDropdown(true);
409
+ setHighlightedIndex(-1);
410
+ }
411
+ return;
412
+ }
413
+ switch (e.key) {
414
+ case "ArrowDown":
415
+ e.preventDefault();
416
+ setHighlightedIndex(
417
+ (prev) => prev < filteredOptions.length - 1 ? prev + 1 : prev
418
+ );
419
+ break;
420
+ case "ArrowUp":
421
+ e.preventDefault();
422
+ setHighlightedIndex((prev) => prev > 0 ? prev - 1 : prev);
423
+ break;
424
+ case "Enter":
425
+ if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {
426
+ e.preventDefault();
427
+ handleSelect(filteredOptions[highlightedIndex]);
428
+ }
429
+ break;
430
+ case "Escape":
431
+ e.preventDefault();
432
+ setShowDropdown(false);
433
+ setHighlightedIndex(-1);
434
+ break;
435
+ }
436
+ };
437
+ const formProps = {
438
+ issuerInput,
439
+ setIssuerInput,
440
+ error,
441
+ presetIssuers: presets,
442
+ isLoading,
443
+ onSubmit: handleSubmit,
444
+ onIssuerChange: setIssuerInput
445
+ };
446
+ if (renderForm) {
447
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
448
+ "main",
449
+ {
450
+ className,
451
+ role: "main",
452
+ "aria-label": "Sign in page",
453
+ style: { display: "flex", minHeight: "100vh", background: "#fff" },
454
+ children: renderForm(formProps)
455
+ }
456
+ );
457
+ }
458
+ const showFooter = renderFooter || footerGitHubUrl != null && footerIssuesUrl != null;
459
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
461
+ @keyframes solid-login-spin {
462
+ to { transform: rotate(360deg); }
463
+ }
464
+ .solid-login-combobox-input:focus {
465
+ border-color: #7B42F6;
466
+ box-shadow: 0 0 0 1px #7B42F6;
467
+ }
468
+ @media (max-width: 1023px) {
469
+ .solid-login-left-panel { display: none !important; }
470
+ .solid-login-mobile-header { display: flex !important; }
471
+ }
472
+ @media (min-width: 1024px) {
473
+ .solid-login-mobile-header { display: none !important; }
474
+ }
475
+ @media (min-width: 1024px) {
476
+ .solid-login-right-panel { min-width: 450px; }
477
+ }
478
+ ` }),
479
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
480
+ "main",
481
+ {
482
+ className,
483
+ role: "main",
484
+ "aria-label": "Sign in page",
485
+ style: {
486
+ display: "flex",
487
+ flexDirection: "row",
488
+ flexWrap: "wrap",
489
+ minHeight: "100vh",
490
+ background: "#fff"
491
+ },
492
+ children: [
493
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
494
+ "section",
495
+ {
496
+ className: "solid-login-left-panel",
497
+ "aria-label": "Branding section",
498
+ style: {
499
+ display: "flex",
500
+ flex: "1 1 50%",
501
+ minWidth: 280,
502
+ alignItems: "center",
503
+ justifyContent: "center",
504
+ borderRight: "1px solid #e5e7eb",
505
+ background: "#F3EDFF",
506
+ padding: "2rem"
507
+ },
508
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { maxWidth: "28rem", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
509
+ "header",
510
+ {
511
+ style: {
512
+ display: "flex",
513
+ flexDirection: "column",
514
+ alignItems: "center",
515
+ justifyContent: "center",
516
+ gap: 4
517
+ },
518
+ children: [
519
+ renderLogo ? renderLogo() : logo ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
520
+ "div",
521
+ {
522
+ style: {
523
+ width: 300,
524
+ height: 90,
525
+ display: "flex",
526
+ alignItems: "center",
527
+ justifyContent: "center"
528
+ },
529
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
530
+ "img",
531
+ {
532
+ src: logo,
533
+ alt: logoAlt,
534
+ style: { width: "100%", height: "100%", objectFit: "contain" }
535
+ }
536
+ )
537
+ }
538
+ ) : null,
539
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
540
+ "h1",
541
+ {
542
+ style: {
543
+ marginBottom: 2,
544
+ fontSize: "2.25rem",
545
+ fontWeight: 400,
546
+ color: "#000"
547
+ },
548
+ children: title
549
+ }
550
+ ),
551
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { fontSize: "1rem", color: "#4b5563" }, children: subtitle })
552
+ ]
553
+ }
554
+ ) })
555
+ }
556
+ ),
557
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
558
+ "section",
559
+ {
560
+ className: "solid-login-right-panel",
561
+ "aria-label": "Sign in form section",
562
+ style: {
563
+ display: "flex",
564
+ flex: "1 1 50%",
565
+ flexDirection: "column",
566
+ alignItems: "center",
567
+ justifyContent: "center",
568
+ background: "#fff",
569
+ padding: "3rem 1rem",
570
+ minWidth: 0,
571
+ minHeight: 320
572
+ },
573
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { width: "100%", maxWidth: "28rem" }, children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
575
+ "header",
576
+ {
577
+ className: "solid-login-mobile-header",
578
+ style: {
579
+ display: "none",
580
+ flexDirection: "column",
581
+ alignItems: "center",
582
+ justifyContent: "center",
583
+ marginBottom: "2rem"
584
+ },
585
+ children: [
586
+ renderLogo ? renderLogo() : logo ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
587
+ "div",
588
+ {
589
+ style: {
590
+ width: 300,
591
+ height: 90,
592
+ display: "flex",
593
+ alignItems: "center",
594
+ justifyContent: "center",
595
+ marginBottom: 8
596
+ },
597
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
598
+ "img",
599
+ {
600
+ src: logo,
601
+ alt: logoAlt,
602
+ style: { width: "100%", height: "100%", objectFit: "contain" }
603
+ }
604
+ )
605
+ }
606
+ ) : null,
607
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
608
+ "h1",
609
+ {
610
+ style: {
611
+ marginBottom: 8,
612
+ fontSize: "1.875rem",
613
+ fontWeight: 400,
614
+ color: "#000",
615
+ textAlign: "center"
616
+ },
617
+ children: title
618
+ }
619
+ ),
620
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { fontSize: "1rem", color: "#4b5563", textAlign: "center" }, children: subtitle })
621
+ ]
622
+ }
623
+ ),
624
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
625
+ "form",
626
+ {
627
+ onSubmit: handleSubmit,
628
+ "aria-label": "Sign in form",
629
+ noValidate: true,
630
+ style: { display: "flex", flexDirection: "column", gap: "1.5rem" },
631
+ children: [
632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
633
+ "label",
634
+ {
635
+ htmlFor: inputId,
636
+ style: {
637
+ display: "block",
638
+ marginBottom: 8,
639
+ fontSize: "0.875rem",
640
+ fontWeight: 500,
641
+ color: "#000"
642
+ },
643
+ children: inputLabel
644
+ }
645
+ ),
646
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative" }, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
648
+ "input",
649
+ {
650
+ ref: inputRef,
651
+ id: inputId,
652
+ type: "text",
653
+ value: issuerInput,
654
+ onChange: (e) => setIssuerInput(e.target.value),
655
+ onFocus: () => {
656
+ setShowDropdown(true);
657
+ setHighlightedIndex(-1);
658
+ },
659
+ onKeyDown: handleKeyDown,
660
+ placeholder: inputPlaceholder,
661
+ disabled: isLoading,
662
+ "aria-invalid": !!error,
663
+ "aria-expanded": showDropdown,
664
+ "aria-controls": listboxId,
665
+ "aria-autocomplete": "list",
666
+ "aria-activedescendant": highlightedIndex >= 0 ? `${inputId}-option-${highlightedIndex}` : void 0,
667
+ role: "combobox",
668
+ autoComplete: "off",
669
+ className: "solid-login-combobox-input",
670
+ style: {
671
+ width: "100%",
672
+ height: 48,
673
+ paddingLeft: 16,
674
+ paddingRight: 40,
675
+ fontSize: "1rem",
676
+ color: "#000",
677
+ background: "#fff",
678
+ border: `1px solid ${error ? "#fca5a5" : "#d1d5db"}`,
679
+ borderRadius: 6,
680
+ outline: "none",
681
+ boxSizing: "border-box"
682
+ },
683
+ onBlur: () => {
684
+ }
685
+ }
686
+ ),
687
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
688
+ "button",
689
+ {
690
+ type: "button",
691
+ onClick: () => {
692
+ setShowDropdown(!showDropdown);
693
+ inputRef.current?.focus();
694
+ },
695
+ "aria-label": showDropdown ? "Hide options" : "Show options",
696
+ "aria-expanded": showDropdown,
697
+ tabIndex: -1,
698
+ style: {
699
+ position: "absolute",
700
+ right: 12,
701
+ top: "50%",
702
+ transform: "translateY(-50%)",
703
+ padding: 0,
704
+ border: "none",
705
+ background: "none",
706
+ cursor: "pointer",
707
+ color: "#9ca3af"
708
+ },
709
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, { open: showDropdown })
710
+ }
711
+ ),
712
+ showDropdown && filteredOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
713
+ "div",
714
+ {
715
+ ref: dropdownRef,
716
+ id: listboxId,
717
+ role: "listbox",
718
+ "aria-label": "Options",
719
+ style: {
720
+ position: "absolute",
721
+ zIndex: 10,
722
+ marginTop: 4,
723
+ width: "100%",
724
+ maxHeight: 240,
725
+ overflow: "auto",
726
+ border: "1px solid #e5e7eb",
727
+ borderRadius: 6,
728
+ background: "#fff",
729
+ boxShadow: "0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)"
730
+ },
731
+ children: filteredOptions.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
732
+ "button",
733
+ {
734
+ id: `${inputId}-option-${index}`,
735
+ type: "button",
736
+ role: "option",
737
+ "aria-selected": issuerInput === option.value,
738
+ onClick: () => handleSelect(option),
739
+ onMouseEnter: () => setHighlightedIndex(index),
740
+ style: {
741
+ width: "100%",
742
+ padding: "12px 16px",
743
+ textAlign: "left",
744
+ border: "none",
745
+ background: highlightedIndex === index ? "#f3f4f6" : "transparent",
746
+ cursor: "pointer",
747
+ fontSize: "0.875rem",
748
+ color: "#111"
749
+ },
750
+ children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 500, marginBottom: 2 }, children: option.label }),
752
+ option.secondaryLabel && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
753
+ "div",
754
+ {
755
+ style: {
756
+ fontSize: "0.75rem",
757
+ color: "#6b7280",
758
+ overflow: "hidden",
759
+ textOverflow: "ellipsis",
760
+ whiteSpace: "nowrap"
761
+ },
762
+ children: option.secondaryLabel
763
+ }
764
+ )
765
+ ]
766
+ },
767
+ option.value
768
+ ))
769
+ }
770
+ )
771
+ ] }),
772
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
773
+ "p",
774
+ {
775
+ role: "alert",
776
+ style: { fontSize: "0.75rem", color: "#dc2626", marginTop: 4 },
777
+ children: error
778
+ }
779
+ ),
780
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
781
+ "div",
782
+ {
783
+ style: {
784
+ display: "flex",
785
+ justifyContent: "flex-end",
786
+ paddingTop: 16
787
+ },
788
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
789
+ "button",
790
+ {
791
+ type: "submit",
792
+ disabled: isLoading,
793
+ "aria-busy": isLoading,
794
+ "aria-label": isLoading ? "Signing in, please wait" : "Continue to sign in",
795
+ style: {
796
+ padding: "8px 16px",
797
+ fontSize: "0.875rem",
798
+ fontWeight: 500,
799
+ color: "#fff",
800
+ background: isLoading ? "#d1d5db" : "#7B42F6",
801
+ border: "none",
802
+ borderRadius: 6,
803
+ cursor: isLoading ? "not-allowed" : "pointer",
804
+ display: "inline-flex",
805
+ alignItems: "center",
806
+ justifyContent: "center",
807
+ boxShadow: "none"
808
+ },
809
+ children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ButtonSpinner, {}),
811
+ buttonLoadingLabel
812
+ ] }) : buttonLabel
813
+ }
814
+ )
815
+ }
816
+ )
817
+ ]
818
+ }
819
+ ),
820
+ showFooter && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
821
+ "footer",
822
+ {
823
+ className: "solid-login-footer-wrap",
824
+ style: {
825
+ marginTop: "6rem",
826
+ display: "flex",
827
+ flexDirection: "column",
828
+ alignItems: "flex-end",
829
+ width: "100%"
830
+ },
831
+ children: renderFooter ? renderFooter() : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
832
+ "div",
833
+ {
834
+ style: {
835
+ display: "flex",
836
+ alignItems: "center",
837
+ gap: 16,
838
+ fontSize: "0.875rem",
839
+ color: "#6b7280"
840
+ },
841
+ children: [
842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
843
+ "a",
844
+ {
845
+ href: footerGitHubUrl,
846
+ target: "_blank",
847
+ rel: "noopener noreferrer",
848
+ style: {
849
+ display: "inline-flex",
850
+ alignItems: "center",
851
+ gap: 6,
852
+ color: "inherit",
853
+ textDecoration: "none"
854
+ },
855
+ "aria-label": "View source code on GitHub",
856
+ children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GitHubIcon, {}),
858
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "GitHub" })
859
+ ]
860
+ }
861
+ ),
862
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#d1d5db" }, children: "\xB7" }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
864
+ "a",
865
+ {
866
+ href: footerIssuesUrl,
867
+ target: "_blank",
868
+ rel: "noopener noreferrer",
869
+ style: {
870
+ display: "inline-flex",
871
+ alignItems: "center",
872
+ gap: 6,
873
+ color: "inherit",
874
+ textDecoration: "none"
875
+ },
876
+ "aria-label": "Report an issue on GitHub",
877
+ children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReportIssueIcon, {}),
879
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Report an issue" })
880
+ ]
881
+ }
882
+ )
883
+ ]
884
+ }
885
+ )
886
+ }
887
+ )
888
+ ] })
889
+ }
890
+ )
891
+ ]
892
+ }
893
+ )
894
+ ] });
895
+ }
896
+
897
+ // src/login/LoginFormControl.tsx
898
+ var import_jsx_runtime4 = require("react/jsx-runtime");
899
+ function LoginFormControl({
900
+ children,
901
+ defaultIssuer,
902
+ presetIssuers
903
+ }) {
904
+ const {
905
+ session,
906
+ issuerInput,
907
+ setIssuerInput,
908
+ isLoading,
909
+ error,
910
+ presetIssuers: presets,
911
+ validateAndSubmit
912
+ } = useSolidLogin({ defaultIssuer, presetIssuers });
913
+ if (session.isLoggedIn) return null;
914
+ const handleSubmit = (e) => {
915
+ e.preventDefault();
916
+ validateAndSubmit();
917
+ };
918
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children({
919
+ issuerInput,
920
+ setIssuerInput,
921
+ isLoading,
922
+ error,
923
+ presetIssuers: presets,
924
+ onSubmit: handleSubmit,
925
+ onIssuerChange: setIssuerInput
926
+ }) });
927
+ }
928
+
929
+ // src/login/navigation.ts
930
+ var DEFAULT_CONFIG = {
931
+ loginPath: "/login",
932
+ homePath: "/"
933
+ };
934
+ // Annotate the CommonJS export names for ESM import in node:
935
+ 0 && (module.exports = {
936
+ AuthGuard,
937
+ DEFAULT_CONFIG,
938
+ LoginFormControl,
939
+ SolidLoginNavigationProvider,
940
+ SolidLoginPage,
941
+ useSolidLogin,
942
+ useSolidLoginNavigation,
943
+ validateIssuerUrl
944
+ });
945
+ //# sourceMappingURL=index.cjs.map