mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-15 11:51:34 +00:00
feat(www): code blocks package manager (#6075)
* feat(www): code blocks * fix: code style
This commit is contained in:
108
apps/www/components/code-block-command.tsx
Normal file
108
apps/www/components/code-block-command.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { CheckIcon, ClipboardIcon } from "lucide-react"
|
||||
|
||||
import { NpmCommands } from "@/types/unist"
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { copyToClipboardWithMeta } from "@/components/copy-button"
|
||||
import { Tabs } from "@/registry/default/ui/tabs"
|
||||
import { Button } from "@/registry/new-york/ui/button"
|
||||
import { TabsContent, TabsList, TabsTrigger } from "@/registry/new-york/ui/tabs"
|
||||
|
||||
export function CodeBlockCommand({
|
||||
__npmCommand__,
|
||||
__yarnCommand__,
|
||||
__pnpmCommand__,
|
||||
__bunCommand__,
|
||||
}: React.ComponentProps<"pre"> & NpmCommands) {
|
||||
const [config, setConfig] = useConfig()
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasCopied) {
|
||||
const timer = setTimeout(() => setHasCopied(false), 2000)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [hasCopied])
|
||||
|
||||
const packageManager = config.packageManager || "pnpm"
|
||||
const tabs = React.useMemo(() => {
|
||||
return {
|
||||
pnpm: __pnpmCommand__,
|
||||
npm: __npmCommand__,
|
||||
yarn: __yarnCommand__,
|
||||
bun: __bunCommand__,
|
||||
}
|
||||
}, [__npmCommand__, __pnpmCommand__, __yarnCommand__, __bunCommand__])
|
||||
|
||||
const copyCommand = React.useCallback(() => {
|
||||
const command = tabs[packageManager]
|
||||
|
||||
if (!command) {
|
||||
return
|
||||
}
|
||||
|
||||
copyToClipboardWithMeta(command, {
|
||||
name: "copy_npm_command",
|
||||
properties: {
|
||||
command,
|
||||
pm: packageManager,
|
||||
},
|
||||
})
|
||||
setHasCopied(true)
|
||||
}, [packageManager, tabs])
|
||||
|
||||
return (
|
||||
<div className="relative mt-6 max-h-[650px] overflow-x-auto rounded-xl bg-zinc-950 dark:bg-zinc-900">
|
||||
<Tabs
|
||||
defaultValue={packageManager}
|
||||
onValueChange={(value) => {
|
||||
setConfig({
|
||||
...config,
|
||||
packageManager: value as "pnpm" | "npm" | "yarn" | "bun",
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-zinc-800 bg-zinc-900 px-3 pt-2.5">
|
||||
<TabsList className="h-7 translate-y-[2px] gap-3 bg-transparent p-0 pl-1">
|
||||
{Object.entries(tabs).map(([key, value]) => {
|
||||
return (
|
||||
<TabsTrigger
|
||||
key={key}
|
||||
value={key}
|
||||
className="rounded-none border-b border-transparent bg-transparent p-0 pb-1.5 font-mono text-zinc-400 data-[state=active]:border-b-zinc-50 data-[state=active]:bg-transparent data-[state=active]:text-zinc-50"
|
||||
>
|
||||
{key}
|
||||
</TabsTrigger>
|
||||
)
|
||||
})}
|
||||
</TabsList>
|
||||
</div>
|
||||
{Object.entries(tabs).map(([key, value]) => {
|
||||
return (
|
||||
<TabsContent key={key} value={key} className="mt-0">
|
||||
<pre className="px-4 py-5">
|
||||
<code
|
||||
className="relative font-mono text-sm leading-none"
|
||||
data-language="bash"
|
||||
>
|
||||
{value}
|
||||
</code>
|
||||
</pre>
|
||||
</TabsContent>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="absolute right-2.5 top-2 z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:h-3 [&_svg]:w-3"
|
||||
onClick={copyCommand}
|
||||
>
|
||||
<span className="sr-only">Copy</span>
|
||||
{hasCopied ? <CheckIcon /> : <ClipboardIcon />}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { Event } from "@/lib/events"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { Callout } from "@/components/callout"
|
||||
import { CodeBlockCommand } from "@/components/code-block-command"
|
||||
import { CodeBlockWrapper } from "@/components/code-block-wrapper"
|
||||
import { ComponentExample } from "@/components/component-example"
|
||||
import { ComponentPreview } from "@/components/component-preview"
|
||||
@@ -192,16 +193,30 @@ const components = {
|
||||
__src__?: string
|
||||
__event__?: Event["name"]
|
||||
} & NpmCommands) => {
|
||||
const isNpmCommand =
|
||||
__npmCommand__ && __yarnCommand__ && __pnpmCommand__ && __bunCommand__
|
||||
|
||||
if (isNpmCommand) {
|
||||
return (
|
||||
<CodeBlockCommand
|
||||
__npmCommand__={__npmCommand__}
|
||||
__yarnCommand__={__yarnCommand__}
|
||||
__pnpmCommand__={__pnpmCommand__}
|
||||
__bunCommand__={__bunCommand__}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<StyleWrapper styleName={__style__}>
|
||||
<pre
|
||||
className={cn(
|
||||
"mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border bg-zinc-950 py-4 dark:bg-zinc-900",
|
||||
"mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-xl bg-zinc-950 py-4 dark:bg-zinc-900",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{__rawString__ && !__npmCommand__ && (
|
||||
{__rawString__ && (
|
||||
<CopyButton
|
||||
value={__rawString__}
|
||||
src={__src__}
|
||||
@@ -209,20 +224,6 @@ const components = {
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
{__npmCommand__ &&
|
||||
__yarnCommand__ &&
|
||||
__pnpmCommand__ &&
|
||||
__bunCommand__ && (
|
||||
<CopyNpmCommandButton
|
||||
commands={{
|
||||
__npmCommand__,
|
||||
__yarnCommand__,
|
||||
__pnpmCommand__,
|
||||
__bunCommand__,
|
||||
}}
|
||||
className={cn("absolute right-4 top-4", __withMeta__ && "top-16")}
|
||||
/>
|
||||
)}
|
||||
</StyleWrapper>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ Do you want to use CSS variables for colors? › no / yes
|
||||
### Install Tailwind CSS
|
||||
|
||||
```bash
|
||||
npm add -D tailwindcss@latest autoprefixer@latest
|
||||
npm install -D tailwindcss@latest autoprefixer@latest
|
||||
```
|
||||
|
||||
Then we create a `postcss.config.js` file:
|
||||
|
||||
@@ -19,7 +19,9 @@ Install `tailwindcss` and its peer dependencies, then generate your `tailwind.co
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss postcss autoprefixer
|
||||
```
|
||||
|
||||
```bash
|
||||
npx tailwindcss init -p
|
||||
```
|
||||
|
||||
@@ -93,11 +95,10 @@ Add the following code to the `tsconfig.app.json` file to resolve paths, for you
|
||||
|
||||
### Update vite.config.ts
|
||||
|
||||
Add the following code to the vite.config.ts so your app can resolve paths without error
|
||||
Add the following code to the vite.config.ts so your app can resolve paths without error:
|
||||
|
||||
```bash
|
||||
# (so you can import "path" without error)
|
||||
npm i -D @types/node
|
||||
npm install -D @types/node
|
||||
```
|
||||
|
||||
```typescript
|
||||
|
||||
@@ -8,12 +8,14 @@ type Config = {
|
||||
style: Style["name"]
|
||||
theme: BaseColor["name"]
|
||||
radius: number
|
||||
packageManager: "npm" | "yarn" | "pnpm" | "bun"
|
||||
}
|
||||
|
||||
const configAtom = atomWithStorage<Config>("config", {
|
||||
style: "new-york",
|
||||
theme: "zinc",
|
||||
radius: 0.5,
|
||||
packageManager: "pnpm",
|
||||
})
|
||||
|
||||
export function useConfig() {
|
||||
|
||||
@@ -37,11 +37,14 @@ export function rehypeComponent() {
|
||||
} else {
|
||||
const component = Index[style.name][name]
|
||||
src = fileName
|
||||
? component.files.find((file: string) => {
|
||||
return (
|
||||
file.endsWith(`${fileName}.tsx`) ||
|
||||
file.endsWith(`${fileName}.ts`)
|
||||
)
|
||||
? component.files.find((file: unknown) => {
|
||||
if (typeof file === "string") {
|
||||
return (
|
||||
file.endsWith(`${fileName}.tsx`) ||
|
||||
file.endsWith(`${fileName}.ts`)
|
||||
)
|
||||
}
|
||||
return false
|
||||
}) || component.files[0]?.path
|
||||
: component.files[0]?.path
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function rehypeNpmCommand() {
|
||||
)
|
||||
}
|
||||
|
||||
// npx create.
|
||||
// npx create-.
|
||||
if (node.properties?.["__rawString__"]?.startsWith("npx create-")) {
|
||||
const npmCommand = node.properties?.["__rawString__"]
|
||||
node.properties["__npmCommand__"] = npmCommand
|
||||
@@ -44,6 +44,24 @@ export function rehypeNpmCommand() {
|
||||
)
|
||||
}
|
||||
|
||||
// npm create.
|
||||
if (node.properties?.["__rawString__"]?.startsWith("npm create")) {
|
||||
const npmCommand = node.properties?.["__rawString__"]
|
||||
node.properties["__npmCommand__"] = npmCommand
|
||||
node.properties["__yarnCommand__"] = npmCommand.replace(
|
||||
"npm create",
|
||||
"yarn create"
|
||||
)
|
||||
node.properties["__pnpmCommand__"] = npmCommand.replace(
|
||||
"npm create",
|
||||
"pnpm create"
|
||||
)
|
||||
node.properties["__bunCommand__"] = npmCommand.replace(
|
||||
"npm create",
|
||||
"bun create"
|
||||
)
|
||||
}
|
||||
|
||||
// npx.
|
||||
if (
|
||||
node.properties?.["__rawString__"]?.startsWith("npx") &&
|
||||
|
||||
Reference in New Issue
Block a user