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