mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-07 14:08:47 +00:00
feat: add docs for base-ui components (#9304)
* feat: add base and radix docs * feat: transform code for display * fix * fix * fix * fix * fix * chore: remove claude files * fix * fix * fix * chore: run format:write * fix * feat: add more examples * fix * feat: add aspect-ratio * feat: add avatar * feat: add badge * feat: add breadcrumb * fix * feat: add button * fix * fix * fix * feat: add calendar and card * feat: add carousel * fix: chart * feat: add checkbox * feat: add collapsible * feat: add combobox * feat: add command * feat: add context menu * feat: add data-table dialog and drawer * feat: dropdown-menu * feat: add date-picker * feat: add empty * feat: add field and hover-card * fix: input * feat: add input * feat: add input-group * feat: add input-otp * feat: add item * feat: add kbd and label * feat: add menubar * feat: add native-select * feat: add more components * feat: more components * feat: more components * feat: add skeleton, slider and sonner * feat: add spinner and switch * feat: add more components * fix: tabs * fix: tabs * feat: add docs for sidebar * fix * fix * fi * docs: update * fix: create page * fix * fix * chore: add changelog * fix
This commit is contained in:
72
apps/v4/scripts/build-examples.ts
Normal file
72
apps/v4/scripts/build-examples.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { rimraf } from "rimraf"
|
||||
|
||||
import { BASES } from "@/registry/bases"
|
||||
|
||||
async function buildExamplesIndex() {
|
||||
const cwd = process.cwd()
|
||||
const examplesDir = path.join(cwd, "examples")
|
||||
|
||||
console.log("📋 Generating examples/__index__.tsx...")
|
||||
|
||||
let index = `// @ts-nocheck
|
||||
// This file is autogenerated by scripts/build-examples-index.mts
|
||||
// Do not edit this file directly.
|
||||
import * as React from "react"
|
||||
|
||||
export const ExamplesIndex: Record<string, Record<string, any>> = {`
|
||||
|
||||
for (const base of BASES) {
|
||||
const baseDir = path.join(examplesDir, base.name)
|
||||
|
||||
try {
|
||||
await fs.access(baseDir)
|
||||
} catch {
|
||||
console.log(` Skipping ${base.name} - directory does not exist`)
|
||||
continue
|
||||
}
|
||||
|
||||
const allEntries = await fs.readdir(baseDir, { withFileTypes: true })
|
||||
const files = allEntries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".tsx"))
|
||||
.map((entry) => entry.name)
|
||||
.sort()
|
||||
|
||||
console.log(` Found ${files.length} demos for ${base.name}`)
|
||||
|
||||
index += `
|
||||
"${base.name}": {`
|
||||
|
||||
for (const file of files) {
|
||||
const name = file.replace(/\.tsx$/, "")
|
||||
const filePath = `examples/${base.name}/${file}`
|
||||
|
||||
index += `
|
||||
"${name}": {
|
||||
name: "${name}",
|
||||
filePath: "${filePath}",
|
||||
component: React.lazy(async () => {
|
||||
const mod = await import("./${base.name}/${name}")
|
||||
const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || "${name}"
|
||||
return { default: mod.default || mod[exportName] }
|
||||
}),
|
||||
},`
|
||||
}
|
||||
|
||||
index += `
|
||||
},`
|
||||
}
|
||||
|
||||
index += `
|
||||
}
|
||||
`
|
||||
|
||||
const indexPath = path.join(examplesDir, "__index__.tsx")
|
||||
rimraf.sync(indexPath)
|
||||
await fs.writeFile(indexPath, index)
|
||||
|
||||
console.log(`\n✅ Generated examples/__index__.tsx`)
|
||||
}
|
||||
|
||||
buildExamplesIndex().catch(console.error)
|
||||
@@ -37,9 +37,13 @@ try {
|
||||
|
||||
const stylesToBuild = getStylesToBuild()
|
||||
|
||||
// We only need the legacy styles here for backward compatibility.
|
||||
// Build index for legacy styles and whitelisted base-style combinations.
|
||||
console.log(`📦 Building registry/__index__.tsx...`)
|
||||
await buildRegistryIndex([...legacyStyles])
|
||||
const stylesForIndex = WHITELISTED_STYLES.map((name) => ({
|
||||
name,
|
||||
title: name,
|
||||
}))
|
||||
await buildRegistryIndex(stylesForIndex)
|
||||
|
||||
console.log("💅 Building styles...")
|
||||
for (const style of stylesToBuild) {
|
||||
@@ -54,6 +58,10 @@ try {
|
||||
console.log("\n⚙️ Building public/r/config.json...")
|
||||
await buildConfig()
|
||||
|
||||
// Copy UI to examples before cleanup.
|
||||
console.log("\n📋 Copying UI to examples...")
|
||||
await copyUIToExamples()
|
||||
|
||||
console.log("\n📋 Building public/r/registries.json...")
|
||||
await buildRegistriesJson()
|
||||
|
||||
@@ -96,6 +104,11 @@ export const Index: Record<string, Record<string, any>> = {`
|
||||
"${base.name}": {`
|
||||
|
||||
for (const item of registry.items) {
|
||||
// Skip demos - they're handled by the examples index.
|
||||
if (item.type === "registry:internal") {
|
||||
continue
|
||||
}
|
||||
|
||||
const files =
|
||||
item.files?.map((file) => ({
|
||||
path: typeof file === "string" ? file : file.path,
|
||||
@@ -169,7 +182,10 @@ async function buildBases(bases: Base[]) {
|
||||
throw new Error(`Invalid registry schema for ${base.name}`)
|
||||
}
|
||||
|
||||
const registryItems = result.data.items
|
||||
// Filter out demos - they're handled by the examples index.
|
||||
const registryItems = result.data.items.filter(
|
||||
(item) => item.type !== "registry:internal"
|
||||
)
|
||||
|
||||
for (const style of STYLES) {
|
||||
console.log(` ✅ ${base.name}-${style.name}...`)
|
||||
@@ -217,11 +233,25 @@ async function buildBases(bases: Base[]) {
|
||||
const fileExtension = path.extname(file.path)
|
||||
const shouldTransform =
|
||||
fileExtension === ".tsx" || fileExtension === ".ts"
|
||||
const transformedContent = shouldTransform
|
||||
? await transformStyle(source, {
|
||||
styleMap: styleMap,
|
||||
})
|
||||
: source
|
||||
|
||||
let transformedContent = source
|
||||
|
||||
if (shouldTransform) {
|
||||
// Transform style classes (cn-* -> Tailwind).
|
||||
transformedContent = await transformStyle(source, {
|
||||
styleMap: styleMap,
|
||||
})
|
||||
|
||||
// Transform import paths from base to style-specific paths.
|
||||
// e.g., @/registry/bases/radix/ui/button -> @/registry/radix-nova/ui/button
|
||||
transformedContent = transformedContent.replace(
|
||||
new RegExp(
|
||||
`@/registry/bases/${base.name}/`,
|
||||
"g"
|
||||
),
|
||||
`@/registry/${base.name}-${style.name}/`
|
||||
)
|
||||
}
|
||||
|
||||
const outputPath = path.join(
|
||||
process.cwd(),
|
||||
@@ -264,6 +294,11 @@ export const Index: Record<string, Record<string, any>> = {`
|
||||
"${style.name}": {`
|
||||
|
||||
for (const item of registry.items) {
|
||||
// Skip demos - they're handled by the examples index.
|
||||
if (item.type === "registry:internal") {
|
||||
continue
|
||||
}
|
||||
|
||||
const files =
|
||||
item.files?.map((file) => ({
|
||||
path: typeof file === "string" ? file : file.path,
|
||||
@@ -455,6 +490,46 @@ async function buildConfig() {
|
||||
})
|
||||
}
|
||||
|
||||
async function copyUIToExamples() {
|
||||
const defaultStyle = "nova"
|
||||
const directories = ["ui", "lib", "hooks"]
|
||||
|
||||
for (const base of BASES) {
|
||||
const sourceStyle = `${base.name}-${defaultStyle}`
|
||||
|
||||
for (const dir of directories) {
|
||||
const fromDir = path.join(process.cwd(), `registry/${sourceStyle}/${dir}`)
|
||||
const toDir = path.join(process.cwd(), `examples/${base.name}/${dir}`)
|
||||
|
||||
try {
|
||||
await fs.access(fromDir)
|
||||
} catch {
|
||||
console.log(` ⚠️ registry/${sourceStyle}/${dir} not found, skipping`)
|
||||
continue
|
||||
}
|
||||
|
||||
rimraf.sync(toDir)
|
||||
await fs.mkdir(toDir, { recursive: true })
|
||||
|
||||
const files = await fs.readdir(fromDir)
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(fromDir, file)
|
||||
const targetPath = path.join(toDir, file)
|
||||
|
||||
let content = await fs.readFile(sourcePath, "utf-8")
|
||||
content = content.replace(
|
||||
new RegExp(`@/registry/${sourceStyle}/`, "g"),
|
||||
`@/examples/${base.name}/`
|
||||
)
|
||||
await fs.writeFile(targetPath, content)
|
||||
}
|
||||
console.log(
|
||||
` ✅ registry/${sourceStyle}/${dir} → examples/${base.name}/${dir}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build public/r/registries.json from registry/directory.json.
|
||||
// This generates a slim version without logos for CLI consumption.
|
||||
async function buildRegistriesJson() {
|
||||
|
||||
Reference in New Issue
Block a user