1
1
import { DocSearch } from '@docsearch/react'
2
2
import * as React from 'react'
3
3
import { CgClose , CgMenuLeft } from 'react-icons/cg'
4
- import { FaArrowLeft , FaArrowRight , FaTimes } from 'react-icons/fa'
5
- import { NavLink , useMatches } from '@remix-run/react'
6
- import { last } from '~/utils/utils'
4
+ import {
5
+ FaArrowLeft ,
6
+ FaArrowRight ,
7
+ FaDiscord ,
8
+ FaGithub ,
9
+ FaTimes ,
10
+ } from 'react-icons/fa'
11
+ import { NavLink , useMatches , useNavigate , useParams } from '@remix-run/react'
7
12
import { Carbon } from '~/components/Carbon'
8
13
import { LinkOrA } from '~/components/LinkOrA'
9
14
import { Search } from '~/components/Search'
@@ -12,27 +17,192 @@ import { useLocalStorage } from '~/utils/useLocalStorage'
12
17
import { DocsCalloutQueryGG } from '~/components/DocsCalloutQueryGG'
13
18
import { DocsCalloutBytes } from '~/components/DocsCalloutBytes'
14
19
import { DocsLogo } from '~/components/DocsLogo'
15
- import type { DocsConfig } from '~/utils/config'
20
+ import { generatePath , last } from '~/utils/utils'
21
+ import type { AvailableOptions } from '~/components/Select'
22
+ import type { ConfigSchema , MenuItem } from '~/utils/config'
16
23
17
- export function DocsLayout ( {
18
- name,
19
- version,
20
- colorFrom,
21
- colorTo,
22
- textColor,
24
+ /**
25
+ * Use framework in URL path
26
+ * Otherwise use framework in localStorage if it exists for this project
27
+ * Otherwise fallback to react
28
+ */
29
+ function useCurrentFramework ( frameworks : AvailableOptions ) {
30
+ const { framework : paramsFramework } = useParams ( )
31
+ const localStorageFramework = localStorage . getItem ( 'framework' )
32
+
33
+ return (
34
+ paramsFramework ||
35
+ ( localStorageFramework && localStorageFramework in frameworks
36
+ ? localStorageFramework
37
+ : 'react' )
38
+ )
39
+ }
40
+
41
+ const useMenuConfig = ( {
23
42
config,
24
- children,
43
+ framework,
44
+ repo,
25
45
} : {
46
+ config : ConfigSchema
47
+ framework : string
48
+ repo : string
49
+ } ) => {
50
+ const frameworkMenuItems =
51
+ config . frameworkMenus . find ( ( d ) => d . framework === framework ) ?. menuItems ??
52
+ [ ]
53
+
54
+ const localMenu : MenuItem = {
55
+ label : 'Menu' ,
56
+ children : [
57
+ {
58
+ label : 'Home' ,
59
+ to : '..' ,
60
+ } ,
61
+ {
62
+ label : (
63
+ < div className = "flex items-center gap-2" >
64
+ GitHub < FaGithub className = "text-lg opacity-20" />
65
+ </ div >
66
+ ) ,
67
+ to : `https://github.com/${ repo } ` ,
68
+ } ,
69
+ {
70
+ label : (
71
+ < div className = "flex items-center gap-2" >
72
+ Discord < FaDiscord className = "text-lg opacity-20" />
73
+ </ div >
74
+ ) ,
75
+ to : 'https://tlinz.com/discord' ,
76
+ } ,
77
+ ] ,
78
+ }
79
+
80
+ return [
81
+ localMenu ,
82
+ // Merge the two menus together based on their group labels
83
+ ...config . menu . map ( ( d ) => {
84
+ const match = frameworkMenuItems . find ( ( d2 ) => d2 . label === d . label )
85
+ return {
86
+ label : d . label ,
87
+ children : [
88
+ ...d . children . map ( ( d ) => ( { ...d , badge : 'core' } ) ) ,
89
+ ...( match ?. children ?? [ ] ) . map ( ( d ) => ( { ...d , badge : framework } ) ) ,
90
+ ] ,
91
+ }
92
+ } ) ,
93
+ ...frameworkMenuItems . filter (
94
+ ( d ) => ! config . menu . find ( ( dd ) => dd . label === d . label )
95
+ ) ,
96
+ ] . filter ( Boolean )
97
+ }
98
+
99
+ const useFrameworkConfig = ( {
100
+ framework,
101
+ frameworks,
102
+ } : {
103
+ framework : string
104
+ frameworks : AvailableOptions
105
+ } ) => {
106
+ const matches = useMatches ( )
107
+ const match = matches [ matches . length - 1 ]
108
+ const navigate = useNavigate ( )
109
+
110
+ const frameworkConfig = React . useMemo ( ( ) => {
111
+ return {
112
+ label : 'Framework' ,
113
+ selected : frameworks [ framework ] ? framework : 'react' ,
114
+ available : frameworks ,
115
+ onSelect : ( option : { label : string ; value : string } ) => {
116
+ const url = generatePath ( match . id , {
117
+ ...match . params ,
118
+ framework : option . value ,
119
+ } )
120
+
121
+ localStorage . setItem ( 'framework' , option . value )
122
+
123
+ navigate ( url )
124
+ } ,
125
+ }
126
+ } , [ frameworks , framework , match , navigate ] )
127
+
128
+ return frameworkConfig
129
+ }
130
+
131
+ const useVersionConfig = ( {
132
+ availableVersions,
133
+ } : {
134
+ availableVersions : string [ ]
135
+ } ) => {
136
+ const matches = useMatches ( )
137
+ const match = matches [ matches . length - 1 ]
138
+ const params = useParams ( )
139
+ const version = params . version !
140
+ const navigate = useNavigate ( )
141
+
142
+ const versionConfig = React . useMemo ( ( ) => {
143
+ const available = availableVersions . reduce (
144
+ ( acc : AvailableOptions , version ) => {
145
+ acc [ version ] = {
146
+ label : version ,
147
+ value : version ,
148
+ }
149
+ return acc
150
+ } ,
151
+ {
152
+ latest : {
153
+ label : 'Latest' ,
154
+ value : 'latest' ,
155
+ } ,
156
+ }
157
+ )
158
+
159
+ return {
160
+ label : 'Version' ,
161
+ selected : version ,
162
+ available,
163
+ onSelect : ( option : { label : string ; value : string } ) => {
164
+ const url = generatePath ( match . id , {
165
+ ...match . params ,
166
+ version : option . value ,
167
+ } )
168
+ navigate ( url )
169
+ } ,
170
+ }
171
+ } , [ version , match , navigate , availableVersions ] )
172
+
173
+ return versionConfig
174
+ }
175
+
176
+ type DocsLayoutProps = {
26
177
name : string
27
178
version : string
28
179
colorFrom : string
29
180
colorTo : string
30
181
textColor : string
31
- config : DocsConfig
182
+ config : ConfigSchema
183
+ frameworks : AvailableOptions
184
+ availableVersions : string [ ]
185
+ repo : string
32
186
children : React . ReactNode
33
- } ) {
34
- const frameworkConfig = config . frameworkConfig
35
- const versionConfig = config . versionConfig
187
+ }
188
+
189
+ export function DocsLayout ( {
190
+ name,
191
+ version,
192
+ colorFrom,
193
+ colorTo,
194
+ textColor,
195
+ config,
196
+ frameworks,
197
+ availableVersions,
198
+ repo,
199
+ children,
200
+ } : DocsLayoutProps ) {
201
+ const framework = useCurrentFramework ( frameworks )
202
+ const frameworkConfig = useFrameworkConfig ( { framework, frameworks } )
203
+ const versionConfig = useVersionConfig ( { availableVersions } )
204
+ const menuConfig = useMenuConfig ( { config, framework, repo } )
205
+
36
206
const matches = useMatches ( )
37
207
const lastMatch = last ( matches )
38
208
@@ -41,8 +211,8 @@ export function DocsLayout({
41
211
const detailsRef = React . useRef < HTMLElement > ( null ! )
42
212
43
213
const flatMenu = React . useMemo (
44
- ( ) => config . menu . flatMap ( ( d ) => d . children ) ,
45
- [ config . menu ]
214
+ ( ) => menuConfig . flatMap ( ( d ) => d . children ) ,
215
+ [ menuConfig ]
46
216
)
47
217
48
218
const docsMatch = matches . find ( ( d ) => d . pathname . includes ( '/docs' ) )
@@ -58,7 +228,7 @@ export function DocsLayout({
58
228
59
229
const [ showBytes , setShowBytes ] = useLocalStorage ( 'showBytes' , true )
60
230
61
- const menuItems = config . menu . map ( ( group , i ) => {
231
+ const menuItems = menuConfig . map ( ( group , i ) => {
62
232
return (
63
233
< div key = { i } >
64
234
< div className = "text-[.9em] uppercase font-black" > { group . label } </ div >
0 commit comments