snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
213
apps/v4/components/directory-add-button.tsx
Normal file
213
apps/v4/components/directory-add-button.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { IconCheck, IconCopy, IconPlus } from "@tabler/icons-react"
|
||||
|
||||
import { useConfig } from "@/hooks/use-config"
|
||||
import { useIsMobile } from "@/hooks/use-mobile"
|
||||
import { copyToClipboardWithMeta } from "@/components/copy-button"
|
||||
import { Button } from "@/styles/base-nova/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/styles/base-nova/ui/dialog"
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from "@/styles/base-nova/ui/drawer"
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@/styles/base-nova/ui/tabs"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/styles/base-nova/ui/tooltip"
|
||||
|
||||
const DirectoryAddContext = React.createContext<{
|
||||
open: (registry: { name: string }) => void
|
||||
}>({
|
||||
open: () => {},
|
||||
})
|
||||
|
||||
export function useDirectoryAdd() {
|
||||
return React.useContext(DirectoryAddContext)
|
||||
}
|
||||
|
||||
export function DirectoryAddButton({
|
||||
registry,
|
||||
}: {
|
||||
registry: { name: string }
|
||||
}) {
|
||||
const { open } = useDirectoryAdd()
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="relative z-10"
|
||||
onClick={() => open(registry)}
|
||||
>
|
||||
Add <IconPlus />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function DirectoryAddProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [config, setConfig] = useConfig()
|
||||
const [hasCopied, setHasCopied] = React.useState(false)
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const [selectedRegistry, setSelectedRegistry] = React.useState<{
|
||||
name: string
|
||||
} | null>(null)
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const packageManager = config.packageManager || "pnpm"
|
||||
|
||||
const commands = React.useMemo(() => {
|
||||
if (!selectedRegistry) return null
|
||||
return {
|
||||
pnpm: `pnpm dlx shadcn@latest registry add ${selectedRegistry.name}`,
|
||||
npm: `npx shadcn@latest registry add ${selectedRegistry.name}`,
|
||||
yarn: `yarn dlx shadcn@latest registry add ${selectedRegistry.name}`,
|
||||
bun: `bunx --bun shadcn@latest registry add ${selectedRegistry.name}`,
|
||||
}
|
||||
}, [selectedRegistry])
|
||||
|
||||
const command = commands?.[packageManager] ?? ""
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasCopied) {
|
||||
const timer = setTimeout(() => setHasCopied(false), 2000)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [hasCopied])
|
||||
|
||||
const handleCopy = React.useCallback(() => {
|
||||
copyToClipboardWithMeta(command, {
|
||||
name: "copy_registry_add_command",
|
||||
properties: {
|
||||
command,
|
||||
registry: selectedRegistry?.name ?? "",
|
||||
},
|
||||
})
|
||||
setHasCopied(true)
|
||||
}, [command, selectedRegistry?.name])
|
||||
|
||||
const contextValue = React.useMemo(
|
||||
() => ({
|
||||
open: (registry: { name: string }) => {
|
||||
setSelectedRegistry(registry)
|
||||
setIsOpen(true)
|
||||
},
|
||||
}),
|
||||
[]
|
||||
)
|
||||
|
||||
const Content = commands ? (
|
||||
<Tabs
|
||||
value={packageManager}
|
||||
onValueChange={(value) => {
|
||||
setConfig({
|
||||
...config,
|
||||
packageManager: value as "pnpm" | "npm" | "yarn" | "bun",
|
||||
})
|
||||
}}
|
||||
className="gap-0 overflow-hidden rounded-xl border"
|
||||
>
|
||||
<div className="flex items-center gap-2 border-b p-1.5">
|
||||
<TabsList className="h-auto *:data-[slot=tabs-trigger]:pt-0">
|
||||
<TabsTrigger value="pnpm">pnpm</TabsTrigger>
|
||||
<TabsTrigger value="npm">npm</TabsTrigger>
|
||||
<TabsTrigger value="yarn">yarn</TabsTrigger>
|
||||
<TabsTrigger value="bun">bun</TabsTrigger>
|
||||
</TabsList>
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<Button
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
className="ml-auto"
|
||||
onClick={handleCopy}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{hasCopied ? <IconCheck /> : <IconCopy />}
|
||||
<span className="sr-only">Copy command</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{hasCopied ? "Copied!" : "Copy command"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{Object.entries(commands).map(([key, cmd]) => (
|
||||
<TabsContent key={key} value={key} className="mt-0">
|
||||
<div className="bg-surface px-3 py-3 text-surface-foreground">
|
||||
<div className="no-scrollbar overflow-x-auto">
|
||||
<code className="font-mono text-sm whitespace-nowrap">{cmd}</code>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<DirectoryAddContext value={contextValue}>
|
||||
{children}
|
||||
{isMobile ? (
|
||||
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Add Registry</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Run this command to add {selectedRegistry?.name} to your
|
||||
project.
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<div className="px-4">{Content}</div>
|
||||
<DrawerFooter>
|
||||
<DrawerClose render={<Button size="sm" />}>Done</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
) : (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent className="animate-none! sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add Registry</DialogTitle>
|
||||
<DialogDescription>
|
||||
Run this command to add {selectedRegistry?.name} to your
|
||||
project.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{Content}
|
||||
<DialogFooter>
|
||||
<DialogClose render={<Button size="sm" variant="outline" />}>
|
||||
Done
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</DirectoryAddContext>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user