From f68e2402939d541bad3f49ee14370fefe72b0457 Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 2 Mar 2026 20:55:27 +0400 Subject: [PATCH] fix --- .../app/(create)/components/project-form.tsx | 295 ++++++++---------- .../(create)/components/template-picker.tsx | 142 --------- .../app/(create)/components/theme-picker.tsx | 2 +- apps/v4/app/(create)/lib/templates.ts | 55 ++++ .../bases/base/blocks/preview/index.tsx | 12 +- .../bases/radix/blocks/preview/index.tsx | 12 +- 6 files changed, 196 insertions(+), 322 deletions(-) delete mode 100644 apps/v4/app/(create)/components/template-picker.tsx create mode 100644 apps/v4/app/(create)/lib/templates.ts diff --git a/apps/v4/app/(create)/components/project-form.tsx b/apps/v4/app/(create)/components/project-form.tsx index ab20f202ed..adc41f08c1 100644 --- a/apps/v4/app/(create)/components/project-form.tsx +++ b/apps/v4/app/(create)/components/project-form.tsx @@ -14,11 +14,14 @@ import { import { Field, FieldContent, - FieldDescription, FieldGroup, FieldLabel, + FieldLegend, + FieldSeparator, + FieldSet, FieldTitle, } from "@/examples/base/ui/field" +import { RadioGroup, RadioGroupItem } from "@/examples/base/ui/radio-group" import { Switch } from "@/examples/base/ui/switch" import { Tabs, @@ -26,24 +29,29 @@ import { TabsList, TabsTrigger, } from "@/examples/base/ui/tabs" -import { Copy01Icon, Tick02Icon } from "@hugeicons/core-free-icons" +import { Copy01Icon, Globe02Icon, Tick02Icon } from "@hugeicons/core-free-icons" import { HugeiconsIcon } from "@hugeicons/react" -import { cn } from "@/lib/utils" import { useConfig } from "@/hooks/use-config" import { copyToClipboardWithMeta } from "@/components/copy-button" -import { - ALL_TEMPLATES, - getFramework, - getTemplateValue, - NO_MONOREPO_FRAMEWORKS, - TEMPLATES, -} from "@/app/(create)/components/template-picker" import { usePresetCode } from "@/app/(create)/hooks/use-design-system" import { useDesignSystemSearchParams, type DesignSystemSearchParams, } from "@/app/(create)/lib/search-params" +import { + getFramework, + getTemplateValue, + NO_MONOREPO_FRAMEWORKS, + TEMPLATES, +} from "@/app/(create)/lib/templates" + +const TURBOREPO_LOGO = + 'Turborepo' +const ORIGIN = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:4000" +const IS_LOCAL_DEV = ORIGIN.includes("localhost") +const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"] as const +type PackageManager = (typeof PACKAGE_MANAGERS)[number] export function ProjectForm() { const [open, setOpen] = React.useState(false) @@ -51,26 +59,29 @@ export function ProjectForm() { const presetCode = usePresetCode() const [config, setConfig] = useConfig() const [hasCopied, setHasCopied] = React.useState(false) - const [hasCopiedLaravel, setHasCopiedLaravel] = React.useState(false) - const packageManager = config.packageManager || "pnpm" + const packageManager = (config.packageManager || "pnpm") as PackageManager + const framework = React.useMemo( + () => getFramework(params.template ?? "next"), + [params.template] + ) + const isMonorepo = React.useMemo( + () => params.template?.endsWith("-monorepo") ?? false, + [params.template] + ) - const isLaravel = getFramework(params.template ?? "next") === "laravel" + const hasMonorepo = !NO_MONOREPO_FRAMEWORKS.includes( + framework as (typeof NO_MONOREPO_FRAMEWORKS)[number] + ) const commands = React.useMemo(() => { - const origin = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:4000" - const isLocalDev = origin.includes("localhost") const presetFlag = ` --preset ${presetCode}` - const framework = getFramework(params.template ?? "next") - const isMonorepo = params.template?.endsWith("-monorepo") ?? false - // Laravel doesn't use --template since it has its own scaffolding. - const templateFlag = - params.template && !isLaravel ? ` --template ${framework}` : "" + const templateFlag = ` --template ${framework}` const monorepoFlag = isMonorepo ? " --monorepo" : "" const rtlFlag = params.rtl ? " --rtl" : "" const flags = `${presetFlag}${templateFlag}${monorepoFlag}${rtlFlag}` - return isLocalDev + return IS_LOCAL_DEV ? { pnpm: `shadcn init${flags}`, npm: `shadcn init${flags}`, @@ -83,7 +94,7 @@ export function ProjectForm() { yarn: `yarn dlx shadcn@latest init${flags}`, bun: `bunx --bun shadcn@latest init${flags}`, } - }, [presetCode, params.rtl, params.template, isLaravel]) + }, [framework, isMonorepo, params.rtl, presetCode]) const command = commands[packageManager] @@ -94,13 +105,6 @@ export function ProjectForm() { } }, [hasCopied]) - React.useEffect(() => { - if (hasCopiedLaravel) { - const timer = setTimeout(() => setHasCopiedLaravel(false), 2000) - return () => clearTimeout(timer) - } - }, [hasCopiedLaravel]) - const handleCopy = React.useCallback(() => { const properties: Record = { command, @@ -120,63 +124,58 @@ export function ProjectForm() { }> Create Project - + Create Project - Configure your project to use shadcn/ui. + Pick a template and configure your project. Available for all major + React frameworks. - Template - - - See the{" "} - - installation guides - {" "} - for more templates and frameworks. - + Template + - {!isLaravel && ( - - - - Monorepo - - Use a Turborepo monorepo with a shared UI package. - - - { - const framework = getFramework(params.template ?? "next") - setParams({ - template: getTemplateValue( - framework, - checked === true - ) as typeof params.template, - }) + +
+ + Options + + + + - - - )} - + Create a monorepo + + { + const framework = getFramework(params.template ?? "next") + setParams({ + template: getTemplateValue( + framework, + checked === true + ) as typeof params.template, + }) + }} + /> + + - - Enable RTL - - Add right-to-left support for your project. - - + + + Enable RTL support + - +
- {isLaravel && ( -
-

- - Create a new Laravel project - - , then run the following command. -

-
-
-
- - laravel new example-app - -
- -
-
-
- )} { - setConfig({ - ...config, - packageManager: value as "pnpm" | "npm" | "yarn" | "bun", - }) + setConfig((prev) => ({ + ...prev, + packageManager: value as PackageManager, + })) }} className="min-w-0 gap-0 overflow-hidden rounded-lg border bg-surface" >
- pnpm - npm - yarn - bun + {PACKAGE_MANAGERS.map((manager) => { + return ( + + {manager} + + ) + })} - ))} -
+ > + {template.title} + + + + ))} - + ) -} +}) diff --git a/apps/v4/app/(create)/components/template-picker.tsx b/apps/v4/app/(create)/components/template-picker.tsx deleted file mode 100644 index c51bf6b093..0000000000 --- a/apps/v4/app/(create)/components/template-picker.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client" - -import { - Picker, - PickerContent, - PickerGroup, - PickerRadioGroup, - PickerRadioItem, - PickerTrigger, -} from "@/app/(create)/components/picker" -import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params" - -const NEXT_LOGO = - 'Next.js' - -const START_LOGO = - 'TanStack' - -const REACT_ROUTER_LOGO = - '' - -const VITE_LOGO = - '' - -const LARAVEL_LOGO = - 'Laravel' - -const ASTRO_LOGO = - 'Astro' - -export const TEMPLATES = [ - [ - { value: "next", title: "Next.js", logo: NEXT_LOGO }, - { value: "vite", title: "Vite", logo: VITE_LOGO }, - { value: "astro", title: "Astro", logo: ASTRO_LOGO }, - ], - [ - { value: "start", title: "TanStack Start", logo: START_LOGO }, - { value: "laravel", title: "Laravel", logo: LARAVEL_LOGO }, - { value: "react-router", title: "React Router", logo: REACT_ROUTER_LOGO }, - ], -] as const - -export const ALL_TEMPLATES = TEMPLATES.flat() - -// Extract the base framework from a template value (e.g. "next-monorepo" -> "next"). -export function getFramework(template: string) { - return template.replace( - "-monorepo", - "" - ) as (typeof ALL_TEMPLATES)[number]["value"] -} - -// Frameworks that don't support the monorepo template. -export const NO_MONOREPO_FRAMEWORKS = ["laravel"] as const - -// Build the full template value from a framework and monorepo flag. -export function getTemplateValue(framework: string, monorepo: boolean) { - if ( - NO_MONOREPO_FRAMEWORKS.includes( - framework as (typeof NO_MONOREPO_FRAMEWORKS)[number] - ) - ) { - return framework - } - return monorepo ? `${framework}-monorepo` : framework -} - -export function TemplatePicker({ - isMobile, - anchorRef, -}: { - isMobile: boolean - anchorRef: React.RefObject -}) { - const [params, setParams] = useDesignSystemSearchParams() - - const isMonorepo = params.template?.endsWith("-monorepo") ?? false - const framework = getFramework(params.template ?? "next") - - const currentTemplate = ALL_TEMPLATES.find( - (template) => template.value === framework - ) - - return ( - - -
-
Template
-
- {currentTemplate?.title} - {isMonorepo ? " (Monorepo)" : ""} -
-
- {currentTemplate?.logo && ( -
- )} - - - { - const canMonorepo = !NO_MONOREPO_FRAMEWORKS.includes( - value as (typeof NO_MONOREPO_FRAMEWORKS)[number] - ) - setParams({ - template: getTemplateValue( - value, - canMonorepo && isMonorepo - ) as typeof params.template, - }) - }} - > - - {ALL_TEMPLATES.map((template) => ( - - {template.logo && ( -
- )} - {template.title} - - ))} - - - - - ) -} diff --git a/apps/v4/app/(create)/components/theme-picker.tsx b/apps/v4/app/(create)/components/theme-picker.tsx index ef6ceadf5d..622c1ee069 100644 --- a/apps/v4/app/(create)/components/theme-picker.tsx +++ b/apps/v4/app/(create)/components/theme-picker.tsx @@ -76,7 +76,7 @@ export function ThemePicker({ anchor={isMobile ? anchorRef : undefined} side={isMobile ? "top" : "right"} align={isMobile ? "center" : "start"} - className="max-h-[23rem]" + className="max-h-92" > Next.js', + }, + { + value: "vite", + title: "Vite", + logo: '', + }, + { + value: "start", + title: "TanStack Start", + logo: 'TanStack', + }, + { + value: "laravel", + title: "Laravel", + logo: 'Laravel', + }, + { + value: "react-router", + title: "React Router", + logo: '', + }, + { + value: "astro", + title: "Astro", + logo: 'Astro', + }, +] as const + +export type TemplateValue = (typeof TEMPLATES)[number]["value"] + +// Extract the base framework from a template value (e.g. "next-monorepo" -> "next"). +export function getFramework(template: string) { + return template.replace("-monorepo", "") as TemplateValue +} + +// Frameworks that don't support the monorepo template. +export const NO_MONOREPO_FRAMEWORKS = ["laravel"] as const + +// Build the full template value from a framework and monorepo flag. +export function getTemplateValue(framework: string, monorepo: boolean) { + if ( + NO_MONOREPO_FRAMEWORKS.includes( + framework as (typeof NO_MONOREPO_FRAMEWORKS)[number] + ) + ) { + return framework + } + + return monorepo ? `${framework}-monorepo` : framework +} diff --git a/apps/v4/registry/bases/base/blocks/preview/index.tsx b/apps/v4/registry/bases/base/blocks/preview/index.tsx index 2fd14b471d..39d1a116aa 100644 --- a/apps/v4/registry/bases/base/blocks/preview/index.tsx +++ b/apps/v4/registry/bases/base/blocks/preview/index.tsx @@ -40,13 +40,13 @@ export default function PreviewExample() { className="grid w-[3000px] grid-cols-7 items-start gap-(--gap) bg-muted p-(--gap) dark:bg-background *:[div]:gap-(--gap)" data-slot="capture-target" > -
+
-
+
@@ -59,20 +59,20 @@ export default function PreviewExample() {
-
+
-
+
-
+
@@ -80,7 +80,7 @@ export default function PreviewExample() {
-
+
diff --git a/apps/v4/registry/bases/radix/blocks/preview/index.tsx b/apps/v4/registry/bases/radix/blocks/preview/index.tsx index a1bf2999a1..42d7de37ef 100644 --- a/apps/v4/registry/bases/radix/blocks/preview/index.tsx +++ b/apps/v4/registry/bases/radix/blocks/preview/index.tsx @@ -40,13 +40,13 @@ export default function PreviewExample() { className="grid w-[3000px] grid-cols-7 items-start gap-(--gap) bg-muted p-(--gap) dark:bg-background *:[div]:gap-(--gap)" data-slot="capture-target" > -
+
-
+
@@ -59,20 +59,20 @@ export default function PreviewExample() {
-
+
-
+
-
+
@@ -80,7 +80,7 @@ export default function PreviewExample() {
-
+