@@ -5,25 +5,91 @@ interface CodeGroupProps {
55 titles ?: string [ ] ;
66}
77
8+ function isCodeBlockLike ( el : React . ReactElement ) : boolean {
9+ if ( el . type === "pre" ) return true ;
10+ const className =
11+ typeof el . props ?. className === "string" ? el . props . className : "" ;
12+ if (
13+ className . includes ( "language-" ) ||
14+ className . includes ( "astro-code" ) ||
15+ className . includes ( "code-block" )
16+ )
17+ return true ;
18+ return false ;
19+ }
20+
21+ function collectCodeBlocks ( node : React . ReactNode ) : React . ReactElement [ ] {
22+ const result : React . ReactElement [ ] = [ ] ;
23+ React . Children . forEach ( node , ( child ) => {
24+ if ( ! React . isValidElement ( child ) ) return ;
25+ if ( child . type === React . Fragment && child . props ?. children != null ) {
26+ result . push ( ...collectCodeBlocks ( child . props . children ) ) ;
27+ return ;
28+ }
29+ if ( child . type === "pre" || isCodeBlockLike ( child ) ) {
30+ result . push ( child ) ;
31+ return ;
32+ }
33+ if ( child . props ?. children != null ) {
34+ result . push ( ...collectCodeBlocks ( child . props . children ) ) ;
35+ }
36+ } ) ;
37+ return result ;
38+ }
39+
840export function CodeGroup ( { children, titles = [ ] } : CodeGroupProps ) {
941 const [ activeIndex , setActiveIndex ] = React . useState ( 0 ) ;
10-
11- // Extract code blocks from children
12- const codeBlocks = React . Children . toArray ( children ) ;
13-
42+
43+ const codeBlocks = React . useMemo ( ( ) => {
44+ let blocks = collectCodeBlocks ( children ) ;
45+ if ( blocks . length > 0 ) return blocks ;
46+ const direct = React . Children . toArray ( children ) . filter (
47+ ( c ) : c is React . ReactElement => React . isValidElement ( c ) && c . type != null
48+ ) ;
49+ if ( direct . length > 1 ) return direct ;
50+ if (
51+ direct . length === 1 &&
52+ direct [ 0 ] . props ?. children != null
53+ ) {
54+ const inner = React . Children . toArray ( direct [ 0 ] . props . children ) . filter (
55+ ( c ) : c is React . ReactElement =>
56+ React . isValidElement ( c ) && c . type != null
57+ ) ;
58+ if ( inner . length > 0 ) return inner ;
59+ }
60+ return direct ;
61+ } , [ children ] ) ;
62+
63+ const tabTitles =
64+ titles . length >= codeBlocks . length
65+ ? titles . slice ( 0 , codeBlocks . length )
66+ : [
67+ ...titles ,
68+ ...codeBlocks
69+ . slice ( titles . length )
70+ . map ( ( _ , i ) => `Tab ${ titles . length + i + 1 } ` ) ,
71+ ] ;
72+
73+ if ( codeBlocks . length === 0 ) {
74+ return (
75+ < div className = "code-group not-prose my-4 rounded-lg border border-[var(--color-border)] overflow-hidden p-4 text-[var(--color-text-muted)] text-sm" >
76+ No code blocks found inside CodeGroup.
77+ </ div >
78+ ) ;
79+ }
80+
1481 return (
15- < div className = "not-prose my-4 rounded-lg border border-[var(--color-border)] overflow-hidden" >
82+ < div className = "code-group not-prose my-4 rounded-lg border border-[var(--color-border)] overflow-hidden" >
1683 { /* Tabs */ }
17- < div className = "flex bg-[var(--color-bg-tertiary)] border-b border-[var(--color-border)]" >
18- { codeBlocks . map ( ( _ , index ) => {
84+ < div className = "flex flex-wrap gap-0 bg-[var(--color-bg-tertiary)] border-b border-[var(--color-border)]" >
85+ { tabTitles . map ( ( title , index ) => {
1986 const isActive = activeIndex === index ;
20- const title = titles [ index ] || `Tab ${ index + 1 } ` ;
2187 return (
2288 < button
2389 key = { index }
2490 type = "button"
2591 onClick = { ( ) => setActiveIndex ( index ) }
26- className = { `px-4 py-2 text-sm font-medium transition-colors ${
92+ className = { `shrink-0 px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
2793 isActive
2894 ? "bg-[var(--color-bg-secondary)] text-[var(--color-text)] border-b-2 border-primary-500 -mb-px"
2995 : "text-[var(--color-text-muted)] hover:text-[var(--color-text)]"
@@ -34,7 +100,7 @@ export function CodeGroup({ children, titles = [] }: CodeGroupProps) {
34100 ) ;
35101 } ) }
36102 </ div >
37-
103+
38104 { /* Content */ }
39105 < div className = "bg-[var(--color-bg-secondary)]" >
40106 { codeBlocks . map ( ( block , index ) => (
0 commit comments