Skip to content

Commit 0eb1b5a

Browse files
tabs fixes (#635)
* fix: refine styling for code blocks and tabs * fix: improve tab matching logic and enhance component structure * fix: update FrameworkCodeBlock to handle content and code blocks more effectively * fix: add support for local-install mode in PackageManagerTabs * fix: update PackageManagerTabs and transformTabsComponent to support multiple package installations per framework * fix: add support for 'create' mode in PackageManagerTabs for package management commands * fix: add support for 'custom' mode in PackageManagerTabs for flexible package management commands * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 8417665 commit 0eb1b5a

File tree

6 files changed

+157
-65
lines changed

6 files changed

+157
-65
lines changed

src/components/CodeBlock.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export function CodeBlock({
194194
style={props.style}
195195
>
196196
{(title || showTypeCopyButton) && (
197-
<div className="flex items-center justify-between px-4 py-2 border-b border-gray-500/20 bg-gray-50 dark:bg-gray-900">
197+
<div className="flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-gray-900">
198198
<div className="text-xs text-gray-700 dark:text-gray-300">
199199
{title || (lang?.toLowerCase() === 'bash' ? 'sh' : (lang ?? ''))}
200200
</div>

src/components/FrameworkCodeBlock.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react'
22
import { useLocalCurrentFramework } from './FrameworkSelect'
33
import { useCurrentUserQuery } from '~/hooks/useCurrentUser'
44
import { useParams } from '@tanstack/react-router'
5-
import { FileTabs } from './FileTabs'
5+
import { Tabs } from './Tabs'
66
import type { Framework } from '~/libraries/types'
77

88
type CodeBlockMeta = {
@@ -22,8 +22,9 @@ type FrameworkCodeBlockProps = {
2222
/**
2323
* Renders code blocks for the currently selected framework.
2424
* - If no blocks for framework: shows nothing
25-
* - If 1 block: shows just the code block (minimal style)
26-
* - If multiple blocks: shows as FileTabs (file tabs with names)
25+
* - If 1 code block: shows just the code block (minimal style)
26+
* - If multiple code blocks: shows as file tabs
27+
* - If no code blocks but has content: shows the content directly
2728
*/
2829
export function FrameworkCodeBlock({
2930
id,
@@ -43,17 +44,25 @@ export function FrameworkCodeBlock({
4344
const normalizedFramework = actualFramework.toLowerCase()
4445

4546
// Find the framework's code blocks
46-
const frameworkBlocks = codeBlocksByFramework[normalizedFramework]
47+
const frameworkBlocks = codeBlocksByFramework[normalizedFramework] || []
4748
const frameworkPanel = panelsByFramework[normalizedFramework]
4849

49-
if (!frameworkBlocks || frameworkBlocks.length === 0 || !frameworkPanel) {
50+
// If no panel content at all for this framework, show nothing
51+
if (!frameworkPanel) {
5052
return null
5153
}
5254

55+
// If no code blocks, just render the content directly
56+
if (frameworkBlocks.length === 0) {
57+
return <div className="framework-code-block">{frameworkPanel}</div>
58+
}
59+
60+
// If 1 code block, render minimal style
5361
if (frameworkBlocks.length === 1) {
5462
return <div className="framework-code-block">{frameworkPanel}</div>
5563
}
5664

65+
// Multiple code blocks - show as file tabs
5766
const tabs = frameworkBlocks.map((block, index) => ({
5867
slug: `file-${index}`,
5968
name: block.title || 'Untitled',
@@ -63,9 +72,9 @@ export function FrameworkCodeBlock({
6372

6473
return (
6574
<div className="framework-code-block">
66-
<FileTabs id={`${id}-${normalizedFramework}`} tabs={tabs}>
75+
<Tabs id={`${id}-${normalizedFramework}`} tabs={tabs} variant="files">
6776
{childrenArray}
68-
</FileTabs>
77+
</Tabs>
6978
</div>
7079
)
7180
}

src/components/PackageManagerTabs.tsx

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { CodeBlock } from './CodeBlock'
77
import type { Framework } from '~/libraries/types'
88

99
type PackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn'
10-
type InstallMode = 'install' | 'dev-install'
10+
type InstallMode =
11+
| 'install'
12+
| 'dev-install'
13+
| 'local-install'
14+
| 'create'
15+
| 'custom'
1116

1217
// Use zustand for cross-component synchronization
1318
// This ensures all PackageManagerTabs instances on the page stay in sync
@@ -27,7 +32,7 @@ const usePackageManagerStore = create<{
2732

2833
type PackageManagerTabsProps = {
2934
id: string
30-
packagesByFramework: Record<string, string[]>
35+
packagesByFramework: Record<string, string[][]>
3136
mode: InstallMode
3237
frameworks: Framework[]
3338
}
@@ -36,45 +41,109 @@ const PACKAGE_MANAGERS: PackageManager[] = ['npm', 'pnpm', 'yarn', 'bun']
3641

3742
function getInstallCommand(
3843
packageManager: PackageManager,
39-
packages: string[],
44+
packageGroups: string[][],
4045
mode: InstallMode,
4146
): string[] {
4247
const commands: string[] = []
4348

49+
if (mode === 'custom') {
50+
for (const packages of packageGroups) {
51+
const pkgStr = packages.join(' ')
52+
switch (packageManager) {
53+
case 'npm':
54+
commands.push(`npm ${pkgStr}`)
55+
break
56+
case 'pnpm':
57+
commands.push(`pnpm ${pkgStr}`)
58+
break
59+
case 'yarn':
60+
commands.push(`yarn ${pkgStr}`)
61+
break
62+
case 'bun':
63+
commands.push(`bun ${pkgStr}`)
64+
break
65+
}
66+
}
67+
}
68+
69+
if (mode === 'create') {
70+
for (const packages of packageGroups) {
71+
const pkgStr = packages.join(' ')
72+
switch (packageManager) {
73+
case 'npm':
74+
commands.push(`npm create ${pkgStr}`)
75+
break
76+
case 'pnpm':
77+
commands.push(`pnpm create ${pkgStr}`)
78+
break
79+
case 'yarn':
80+
commands.push(`yarn create ${pkgStr}`)
81+
break
82+
case 'bun':
83+
commands.push(`bun create ${pkgStr}`)
84+
break
85+
}
86+
}
87+
}
88+
89+
if (mode === 'local-install') {
90+
// Each group becomes one command line
91+
for (const packages of packageGroups) {
92+
const pkgStr = packages.join(' ')
93+
switch (packageManager) {
94+
case 'npm':
95+
commands.push(`npx ${pkgStr}`)
96+
break
97+
case 'pnpm':
98+
commands.push(`pnpx ${pkgStr}`)
99+
break
100+
case 'yarn':
101+
commands.push(`yarn dlx ${pkgStr}`)
102+
break
103+
case 'bun':
104+
commands.push(`bunx ${pkgStr}`)
105+
break
106+
}
107+
}
108+
return commands
109+
}
110+
44111
if (mode === 'dev-install') {
45-
for (const pkg of packages) {
112+
for (const packages of packageGroups) {
113+
const pkgStr = packages.join(' ')
46114
switch (packageManager) {
47115
case 'npm':
48-
commands.push(`npm i -D ${pkg}`)
116+
commands.push(`npm i -D ${pkgStr}`)
49117
break
50118
case 'pnpm':
51-
commands.push(`pnpm add -D ${pkg}`)
119+
commands.push(`pnpm add -D ${pkgStr}`)
52120
break
53121
case 'yarn':
54-
commands.push(`yarn add -D ${pkg}`)
122+
commands.push(`yarn add -D ${pkgStr}`)
55123
break
56124
case 'bun':
57-
commands.push(`bun add -d ${pkg}`)
125+
commands.push(`bun add -d ${pkgStr}`)
58126
break
59127
}
60128
}
61129
return commands
62130
}
63131

64132
// install mode
65-
for (const pkg of packages) {
133+
for (const packages of packageGroups) {
134+
const pkgStr = packages.join(' ')
66135
switch (packageManager) {
67136
case 'npm':
68-
commands.push(`npm i ${pkg}`)
137+
commands.push(`npm i ${pkgStr}`)
69138
break
70139
case 'pnpm':
71-
commands.push(`pnpm add ${pkg}`)
140+
commands.push(`pnpm add ${pkgStr}`)
72141
break
73142
case 'yarn':
74-
commands.push(`yarn add ${pkg}`)
143+
commands.push(`yarn add ${pkgStr}`)
75144
break
76145
case 'bun':
77-
commands.push(`bun add ${pkg}`)
146+
commands.push(`bun add ${pkgStr}`)
78147
break
79148
}
80149
}
@@ -100,10 +169,10 @@ export function PackageManagerTabs({
100169
'react') as Framework
101170

102171
const normalizedFramework = actualFramework.toLowerCase()
103-
const packages = packagesByFramework[normalizedFramework]
172+
const packageGroups = packagesByFramework[normalizedFramework]
104173

105174
// Hide component if current framework not in package list
106-
if (!packages || packages.length === 0) {
175+
if (!packageGroups || packageGroups.length === 0) {
107176
return null
108177
}
109178

@@ -121,7 +190,7 @@ export function PackageManagerTabs({
121190

122191
// Generate children (command blocks) for each package manager
123192
const children = PACKAGE_MANAGERS.map((pm) => {
124-
const commands = getInstallCommand(pm, packages, mode)
193+
const commands = getInstallCommand(pm, packageGroups, mode)
125194
const commandText = commands.join('\n')
126195
return (
127196
<CodeBlock key={pm}>

src/components/Tabs.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export function Tabs({
5050
if (tabsProp.length === 0) return null
5151

5252
return (
53-
<div className="not-prose my-4">
54-
<div className="flex items-center justify-start gap-2 rounded-t-md border-1 border-b-none border-gray-200 dark:border-gray-800 overflow-x-auto overflow-y-hidden bg-white dark:bg-gray-950">
53+
<div className="my-4">
54+
<div className="not-prose flex items-center justify-start gap-2 rounded-t-md border-1 border-b-none border-gray-200 dark:border-gray-800 overflow-x-auto overflow-y-hidden bg-white dark:bg-gray-950">
5555
{tabsProp.map((tab) => {
5656
return (
5757
<Tab
@@ -64,7 +64,9 @@ export function Tabs({
6464
)
6565
})}
6666
</div>
67-
<div className="border border-gray-500/20 rounded-b-md p-4 bg-gray-100 dark:bg-gray-900">
67+
<div
68+
className={`border border-gray-500/20 rounded-b-md bg-gray-100 dark:bg-gray-900`}
69+
>
6870
{childrenArray.map((child, index) => {
6971
const tab = tabsProp[index]
7072
if (!tab) return null
@@ -73,7 +75,7 @@ export function Tabs({
7375
key={`${id}-${tab.slug}`}
7476
data-tab={tab.slug}
7577
hidden={tab.slug !== activeSlug}
76-
className="prose dark:prose-invert max-w-none flex flex-col gap-2 text-base"
78+
className="max-w-none flex flex-col gap-2 text-base"
7779
>
7880
{child}
7981
</div>
@@ -96,8 +98,13 @@ const Tab = React.memo(
9698
setActiveSlug: (slug: string) => void
9799
}) => {
98100
const option = React.useMemo(
99-
() => frameworkOptions.find((o) => o.value === tab.slug),
100-
[tab.slug],
101+
() =>
102+
frameworkOptions.find(
103+
(o) =>
104+
o.value === tab.slug.toLowerCase() ||
105+
o.label.toLowerCase() === tab.name.toLowerCase(),
106+
),
107+
[tab.slug, tab.name],
101108
)
102109

103110
return (

src/styles/app.css

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ mark {
878878

879879
/* Hide the title bar but keep copy button accessible */
880880
.file-tabs-panel .codeblock > div:first-child {
881-
@apply absolute right-2 top-2 bg-transparent border-0 p-0 z-10;
881+
@apply absolute right-2 top-2 bg-transparent border-none p-0 z-10;
882882
}
883883

884884
/* Hide the title text, keep only the button */
@@ -892,7 +892,11 @@ mark {
892892
}
893893

894894
.package-manager-tabs [data-tab] .codeblock > div:first-child {
895-
@apply absolute right-2 top-2 bg-transparent border-0 p-0 z-10;
895+
@apply absolute right-2 top-2 bg-transparent border-none p-0 z-10;
896+
}
897+
898+
.package-manager-tabs .codeblock {
899+
border: none;
896900
}
897901

898902
.package-manager-tabs
@@ -905,25 +909,15 @@ mark {
905909

906910
/* Remove padding from tab content wrapper for package manager */
907911
.package-manager-tabs .not-prose > div:last-child {
908-
@apply p-0 border-0 rounded-b-none bg-transparent;
912+
@apply p-0 border-none rounded-b-none bg-transparent;
909913
}
910914

911915
/* Restore bottom border radius on the code block itself */
912916
.package-manager-tabs [data-tab] .codeblock {
913917
@apply rounded-b-md;
914918
}
915919

916-
/* Framework code blocks - minimal style for single code blocks */
920+
/* Framework code blocks - single blocks look like regular code blocks */
917921
.framework-code-block > .codeblock {
918-
@apply my-4 rounded-md;
919-
}
920-
921-
/* Hide the title bar but keep copy button accessible for single blocks */
922-
.framework-code-block > .codeblock > div:first-child {
923-
@apply absolute right-2 top-2 bg-transparent border-0 p-0 z-10;
924-
}
925-
926-
/* Hide the title text, keep only the button */
927-
.framework-code-block > .codeblock > div:first-child > div:first-child {
928-
@apply hidden;
922+
@apply my-4;
929923
}

0 commit comments

Comments
 (0)