11import { Md } from '@m2d/react-markdown/client' ;
22import { capitalize } from 'es-toolkit/string' ;
3- import { useEffect , useState } from 'react' ;
3+ import { useRouter } from 'next/router' ;
4+ import { useEffect , useMemo , useState } from 'react' ;
45import { View } from 'react-native' ;
56import { type Theme } from 'react-shiki' ;
67import rehypeRaw from 'rehype-raw' ;
@@ -12,8 +13,7 @@ import useSWR from 'swr';
1213import { A , P } from '~/common/styleguide' ;
1314import { CCFile , ChangelogFile , Check , ContributingFile , ReadmeFile } from '~/components/Icons' ;
1415import CopyButton from '~/components/Package/CopyButton' ;
15- import MarkdownContentTab from '~/components/Package/MarkdownContentTab' ;
16- import ReadmeHeading from '~/components/Package/ReadmeHeading' ;
16+ import ThreeDotsLoader from '~/components/Package/ThreeDotsLoader' ;
1717import rndDark from '~/styles/shiki/rnd-dark.json' ;
1818import rndLight from '~/styles/shiki/rnd-light.json' ;
1919import { type LibraryType , type MarkdownTab , type MarkdownTabsType } from '~/types' ;
@@ -23,8 +23,10 @@ import { getReadmeAssetURL } from '~/util/getReadmeAssetUrl';
2323import { parseGitHubUrl } from '~/util/parseGitHubUrl' ;
2424import tw from '~/util/tailwind' ;
2525
26- import ReadmeCodeBlock from './ReadmeCodeBlock' ;
27- import ThreeDotsLoader from './ThreeDotsLoader' ;
26+ import MarkdownCodeBlock from './MarkdownCodeBlock' ;
27+ import MarkdownContentTab from './MarkdownContentTab' ;
28+ import MarkdownHeading from './MarkdownHeading' ;
29+ import { DEFAULT_MARKDOWN_TAB , MARKDOWN_CONTENT_QUERY_PARAM , parseMarkdownTab } from './utils' ;
2830
2931type Props = {
3032 packageName ?: string ;
@@ -33,45 +35,63 @@ type Props = {
3335} ;
3436
3537export default function MarkdownContentBox ( { packageName, library, loader = false } : Props ) {
36- const [ activeTab , setActiveTab ] = useState < MarkdownTabsType > ( 'Readme' ) ;
38+ const router = useRouter ( ) ;
3739 const repoUrl = library ?. github . urls . repo ;
3840
39- const contentTabs : MarkdownTab [ ] = [
40- {
41- title : 'Readme' as const ,
42- Icon : ReadmeFile ,
43- url : library ?. template
44- ? getTabContentUrl ( library , 'README.md' )
45- : `https://unpkg.com/${ packageName } /README.md` ,
46- } ,
47- ...( library ?. github ?. hasChangelog
48- ? [
49- {
50- title : 'Changelog' as const ,
51- Icon : ChangelogFile ,
52- url : getTabContentUrl ( library , 'CHANGELOG.md' ) ,
53- } ,
54- ]
55- : [ ] ) ,
56- ...( library ?. github ?. hasContributing
57- ? [
58- {
59- title : 'Contributing' as const ,
60- Icon : ContributingFile ,
61- url : getTabContentUrl ( library , 'CONTRIBUTING.md' ) ,
62- } ,
63- ]
64- : [ ] ) ,
65- ...( library ?. github ?. hasCC
66- ? [
67- {
68- title : 'Code of Conduct' as const ,
69- Icon : CCFile ,
70- url : getTabContentUrl ( library , 'CODE_OF_CONDUCT.md' ) ,
71- } ,
72- ]
73- : [ ] ) ,
74- ] . flat ( ) ;
41+ const contentTabs = useMemo < MarkdownTab [ ] > (
42+ ( ) =>
43+ [
44+ {
45+ title : 'Readme' as const ,
46+ Icon : ReadmeFile ,
47+ url : library ?. template
48+ ? getTabContentUrl ( library , 'README.md' )
49+ : `https://unpkg.com/${ packageName } /README.md` ,
50+ } ,
51+ ...( library ?. github ?. hasChangelog
52+ ? [
53+ {
54+ title : 'Changelog' as const ,
55+ Icon : ChangelogFile ,
56+ url : getTabContentUrl ( library , 'CHANGELOG.md' ) ,
57+ } ,
58+ ]
59+ : [ ] ) ,
60+ ...( library ?. github ?. hasContributing
61+ ? [
62+ {
63+ title : 'Contributing' as const ,
64+ Icon : ContributingFile ,
65+ url : getTabContentUrl ( library , 'CONTRIBUTING.md' ) ,
66+ } ,
67+ ]
68+ : [ ] ) ,
69+ ...( library ?. github ?. hasCC
70+ ? [
71+ {
72+ title : 'Code of Conduct' as const ,
73+ Icon : CCFile ,
74+ url : getTabContentUrl ( library , 'CODE_OF_CONDUCT.md' ) ,
75+ } ,
76+ ]
77+ : [ ] ) ,
78+ ] . flat ( ) ,
79+ [ library , packageName ]
80+ ) ;
81+
82+ const availableTabs = useMemo < MarkdownTabsType [ ] > (
83+ ( ) => contentTabs . map ( ( { title } ) => title ) ,
84+ [ contentTabs ]
85+ ) ;
86+ const routeTab = useMemo (
87+ ( ) => parseMarkdownTab ( router . query [ MARKDOWN_CONTENT_QUERY_PARAM ] , availableTabs ) ,
88+ [ availableTabs , router . query ]
89+ ) ;
90+ const [ activeTab , setActiveTab ] = useState < MarkdownTabsType > ( routeTab ) ;
91+
92+ useEffect ( ( ) => {
93+ setActiveTab ( currentTab => ( currentTab === routeTab ? currentTab : routeTab ) ) ;
94+ } , [ routeTab ] ) ;
7595
7696 const { data, error, isLoading } = useSWR (
7797 contentTabs . find ( ( { title } ) => title === activeTab ) ?. url ,
@@ -111,6 +131,29 @@ export default function MarkdownContentBox({ packageName, library, loader = fals
111131 }
112132 } , [ noData ] ) ;
113133
134+ function handleTabChange ( nextTab : MarkdownTabsType ) {
135+ if ( nextTab === activeTab ) {
136+ return ;
137+ }
138+
139+ setActiveTab ( nextTab ) ;
140+
141+ const url = new URL ( window . location . href ) ;
142+
143+ if ( nextTab === DEFAULT_MARKDOWN_TAB ) {
144+ url . searchParams . delete ( MARKDOWN_CONTENT_QUERY_PARAM ) ;
145+ } else {
146+ url . searchParams . set ( MARKDOWN_CONTENT_QUERY_PARAM , nextTab ) ;
147+ }
148+
149+ url . hash = '' ;
150+
151+ void router . replace ( `${ url . pathname } ${ url . search } ` , undefined , {
152+ shallow : true ,
153+ scroll : false ,
154+ } ) ;
155+ }
156+
114157 return (
115158 < View
116159 style = { tw `my-2 rounded-xl border border-palette-gray2 text-black dark:border-default dark:text-white` } >
@@ -120,7 +163,7 @@ export default function MarkdownContentBox({ packageName, library, loader = fals
120163 < MarkdownContentTab
121164 tab = { tab }
122165 activeTab = { activeTab }
123- setActiveTab = { setActiveTab }
166+ onPress = { availableTabs . length > 1 ? handleTabChange : undefined }
124167 key = { `tab-${ tab . title . toLocaleLowerCase ( ) } ` }
125168 />
126169 ) ) }
@@ -144,22 +187,22 @@ export default function MarkdownContentBox({ packageName, library, loader = fals
144187 id = "markdownContentContainer"
145188 components = { {
146189 h1 : ( { children, node } : any ) => (
147- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
190+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
148191 ) ,
149192 h2 : ( { children, node } : any ) => (
150- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
193+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
151194 ) ,
152195 h3 : ( { children, node } : any ) => (
153- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
196+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
154197 ) ,
155198 h4 : ( { children, node } : any ) => (
156- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
199+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
157200 ) ,
158201 h5 : ( { children, node } : any ) => (
159- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
202+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
160203 ) ,
161204 h6 : ( { children, node } : any ) => (
162- < ReadmeHeading tagName = { node . tagName } > { children } </ ReadmeHeading >
205+ < MarkdownHeading tagName = { node . tagName } > { children } </ MarkdownHeading >
163206 ) ,
164207 br : ( ) => null ,
165208 hr : ( ) => null ,
@@ -217,7 +260,7 @@ export default function MarkdownContentBox({ packageName, library, loader = fals
217260 const langClass = children ?. props ?. className ;
218261 if ( langClass ) {
219262 return (
220- < ReadmeCodeBlock
263+ < MarkdownCodeBlock
221264 code = { children . props . children }
222265 theme = { ( tw . prefixMatch ( 'dark' ) ? rndDark : rndLight ) as Theme }
223266 lang = { langClass ? ( langClass . split ( '-' ) [ 1 ] ?? 'sh' ) . toLowerCase ( ) : 'sh' }
0 commit comments