@@ -9,6 +9,7 @@ interface ChildrenProps {
9
9
}
10
10
11
11
const AUTHJS_TAB_KEY = "authjs.codeTab.framework"
12
+ const AUTHJS_TAB_KEY_ALL = "authjs.codeTab.framework.all"
12
13
13
14
Code . Next = NextCode
14
15
Code . NextClient = NextClientCode
@@ -34,15 +35,76 @@ const allFrameworks = {
34
35
[ ExpressCode . name ] : "Express" ,
35
36
}
36
37
37
- /**
38
- * Replace all non-alphabetic characters with a hyphen
39
- *
40
- * @param url - URL to parse
41
- * @returns - A string parsed from the URL
42
- */
43
- const parseParams = ( url : string ) : string => {
44
- let parsedUrl = url . toLowerCase ( ) . replaceAll ( / [ ^ a - z A - z ] + / g, "-" )
45
- return parsedUrl . endsWith ( "-" ) ? parsedUrl . slice ( 0 , - 1 ) : parsedUrl
38
+ const findFrameworkKey = (
39
+ text : string ,
40
+ frameworks : Record < string , string >
41
+ ) : string | null => {
42
+ const entry = Object . entries ( frameworks ) . find ( ( [ _ , value ] ) => value === text )
43
+ return entry ? entry [ 0 ] : null
44
+ }
45
+
46
+ const getIndexFrameworkFromUrl = (
47
+ url : string ,
48
+ frameworks : Record < string , string >
49
+ ) : number | null => {
50
+ const params = new URLSearchParams ( url )
51
+ const paramValue = params . get ( "framework" )
52
+ if ( ! paramValue ) return null
53
+
54
+ const pascalCase = paramValue . replace (
55
+ / ( ^ | - ) ( [ a - z ] ) / g,
56
+ ( _ , _separator , letter ) => letter . toUpperCase ( )
57
+ )
58
+
59
+ const index = Object . keys ( frameworks ) . findIndex ( ( key ) => key === pascalCase )
60
+ return index === - 1 ? null : index
61
+ }
62
+
63
+ const getIndexFrameworkFromStorage = (
64
+ frameworks : Record < string , string > ,
65
+ isAllFrameworks : boolean
66
+ ) : number | null => {
67
+ const storageKey = isAllFrameworks ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY
68
+ const storedIndex = window . localStorage . getItem ( storageKey )
69
+
70
+ if ( ! storedIndex ) {
71
+ return null
72
+ }
73
+
74
+ return parseInt ( storedIndex ) % Object . keys ( frameworks ) . length
75
+ }
76
+
77
+ const updateFrameworkStorage = (
78
+ frameworkKey : string ,
79
+ frameworks : Record < string , string > ,
80
+ isAllFrameworks : boolean
81
+ ) : void => {
82
+ const index = Object . keys ( frameworks ) . findIndex ( ( key ) => key === frameworkKey )
83
+ if ( index === - 1 ) return
84
+
85
+ const storageKey = isAllFrameworks ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY
86
+ window . localStorage . setItem ( storageKey , index . toString ( ) )
87
+
88
+ // Update other storage if framework exists in other object
89
+ const otherFrameworksKeys = Object . keys (
90
+ isAllFrameworks ? baseFrameworks : allFrameworks
91
+ )
92
+ const otherStorageKey = isAllFrameworks ? AUTHJS_TAB_KEY : AUTHJS_TAB_KEY_ALL
93
+
94
+ const existsInOther = otherFrameworksKeys . includes ( frameworkKey )
95
+ if ( existsInOther ) {
96
+ const otherIndex = otherFrameworksKeys . findIndex (
97
+ ( key ) => key === frameworkKey
98
+ )
99
+ window . localStorage . setItem ( otherStorageKey , otherIndex . toString ( ) )
100
+ // see https://github.com/shuding/nextra/blob/7ae958f02922e608151411042f658480b75164a6/packages/nextra/src/client/components/tabs/index.client.tsx#L106
101
+ window . dispatchEvent (
102
+ new StorageEvent ( "storage" , {
103
+ key : otherStorageKey ,
104
+ newValue : otherIndex . toString ( ) ,
105
+ } )
106
+ )
107
+ }
46
108
}
47
109
48
110
export function Code ( { children } : ChildrenProps ) {
@@ -58,37 +120,69 @@ export function Code({ children }: ChildrenProps) {
58
120
59
121
const renderedFrameworks = withNextJsPages ? allFrameworks : baseFrameworks
60
122
61
- const updateFrameworkStorage = ( value : string ) : void => {
123
+ const updateFrameworkInUrl = ( frameworkKey : string ) : void => {
62
124
const params = new URLSearchParams ( searchParams ?. toString ( ) )
63
- params . set ( "framework" , value )
64
- router . push ( `${ router . pathname } ?${ params . toString ( ) } ` )
125
+ const kebabCaseValue = frameworkKey
126
+ . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, "$1-$2" )
127
+ . toLowerCase ( )
128
+ params . set ( "framework" , kebabCaseValue )
129
+
130
+ router . push ( `${ router . pathname } ?${ params . toString ( ) } ` , undefined , {
131
+ scroll : false ,
132
+ } )
65
133
}
66
134
67
135
const handleClickFramework = ( event : MouseEvent < HTMLDivElement > ) => {
68
136
if ( ! ( event . target instanceof HTMLButtonElement ) ) return
69
137
const { textContent } = event . target as unknown as HTMLDivElement
70
- updateFrameworkStorage ( parseParams ( textContent ! ) )
138
+ if ( ! textContent ) return
139
+
140
+ const frameworkKey = findFrameworkKey ( textContent , renderedFrameworks )
141
+ if ( frameworkKey ) {
142
+ updateFrameworkInUrl ( frameworkKey )
143
+ updateFrameworkStorage ( frameworkKey , renderedFrameworks , withNextJsPages )
144
+
145
+ // Focus and scroll to maintain position when code blocks above are expanded
146
+ const element = event . target as HTMLButtonElement
147
+ const rect = element . getBoundingClientRect ( )
148
+ requestAnimationFrame ( ( ) => {
149
+ element . focus ( )
150
+ window . scrollBy ( 0 , element . getBoundingClientRect ( ) . top - rect . top )
151
+ } )
152
+ }
71
153
}
72
154
73
155
useEffect ( ( ) => {
74
- const length = Object . keys ( renderedFrameworks ) . length
75
- const getFrameworkStorage = window . localStorage . getItem ( AUTHJS_TAB_KEY )
76
- const indexFramework = parseInt ( getFrameworkStorage ?? "0" ) % length
77
- if ( ! getFrameworkStorage ) {
78
- window . localStorage . setItem ( AUTHJS_TAB_KEY , "0" )
79
- }
80
- updateFrameworkStorage (
81
- parseParams ( Object . values ( renderedFrameworks ) [ indexFramework ] )
156
+ const indexFrameworkFromStorage = getIndexFrameworkFromStorage (
157
+ renderedFrameworks ,
158
+ withNextJsPages
159
+ )
160
+ const indexFrameworkFromUrl = getIndexFrameworkFromUrl (
161
+ router . asPath ,
162
+ renderedFrameworks
82
163
)
83
- } , [ router . pathname , renderedFrameworks ] )
164
+
165
+ if ( indexFrameworkFromStorage === null ) {
166
+ updateFrameworkStorage (
167
+ Object . keys ( renderedFrameworks ) [ indexFrameworkFromUrl ?? 0 ] ,
168
+ renderedFrameworks ,
169
+ withNextJsPages
170
+ )
171
+ }
172
+
173
+ if ( ! indexFrameworkFromUrl ) {
174
+ const index = indexFrameworkFromStorage ?? 0
175
+ updateFrameworkInUrl ( Object . keys ( renderedFrameworks ) [ index ] )
176
+ }
177
+ } , [ router . pathname , renderedFrameworks , withNextJsPages ] )
84
178
85
179
return (
86
180
< div
87
181
className = "[&_div[role='tablist']]:!pb-0"
88
182
onClick = { handleClickFramework }
89
183
>
90
184
< Tabs
91
- storageKey = { AUTHJS_TAB_KEY }
185
+ storageKey = { withNextJsPages ? AUTHJS_TAB_KEY_ALL : AUTHJS_TAB_KEY }
92
186
items = { Object . values ( renderedFrameworks ) }
93
187
>
94
188
{ Object . keys ( renderedFrameworks ) . map ( ( f ) => {
0 commit comments