mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
Compare commits
4 Commits
shadcn-ui@
...
shadcn/cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8392c7fa0c | ||
|
|
a465432a66 | ||
|
|
7822e06904 | ||
|
|
578d2c1823 |
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
"files": [
|
||||
{
|
||||
"name": "pagination.tsx",
|
||||
"content": "import * as React from \"react\"\nimport { ChevronLeft, ChevronRight, MoreHorizontal } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/registry/default/ui/button\"\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n)\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n))\nPaginationContent.displayName = \"PaginationContent\"\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n))\nPaginationItem.displayName = \"PaginationItem\"\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <PaginationItem>\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n </PaginationItem>\n)\nPaginationLink.displayName = \"PaginationLink\"\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span>Previous</span>\n </PaginationLink>\n)\nPaginationPrevious.displayName = \"PaginationPrevious\"\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </PaginationLink>\n)\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n <span className=\"sr-only\">More pages</span>\n </span>\n)\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n}\n"
|
||||
"content": "import * as React from \"react\"\nimport { ChevronLeft, ChevronRight, MoreHorizontal } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/registry/default/ui/button\"\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n)\nPagination.displayName = \"Pagination\"\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n))\nPaginationContent.displayName = \"PaginationContent\"\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n))\nPaginationItem.displayName = \"PaginationItem\"\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n)\nPaginationLink.displayName = \"PaginationLink\"\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span>Previous</span>\n </PaginationLink>\n)\nPaginationPrevious.displayName = \"PaginationPrevious\"\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </PaginationLink>\n)\nPaginationNext.displayName = \"PaginationNext\"\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n <span className=\"sr-only\">More pages</span>\n </span>\n)\nPaginationEllipsis.displayName = \"PaginationEllipsis\"\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n}\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
"files": [
|
||||
{
|
||||
"name": "pagination.tsx",
|
||||
"content": "import * as React from \"react\"\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n DotsHorizontalIcon,\n} from \"@radix-ui/react-icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/registry/new-york/ui/button\"\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n)\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n))\nPaginationContent.displayName = \"PaginationContent\"\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n))\nPaginationItem.displayName = \"PaginationItem\"\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <PaginationItem>\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n </PaginationItem>\n)\nPaginationLink.displayName = \"PaginationLink\"\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeftIcon className=\"h-4 w-4\" />\n <span>Previous</span>\n </PaginationLink>\n)\nPaginationPrevious.displayName = \"PaginationPrevious\"\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>Next</span>\n <ChevronRightIcon className=\"h-4 w-4\" />\n </PaginationLink>\n)\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <DotsHorizontalIcon className=\"h-4 w-4\" />\n <span className=\"sr-only\">More pages</span>\n </span>\n)\n\nexport {\n Pagination,\n PaginationContent,\n PaginationLink,\n PaginationItem,\n PaginationPrevious,\n PaginationNext,\n PaginationEllipsis,\n}\n"
|
||||
"content": "import * as React from \"react\"\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n DotsHorizontalIcon,\n} from \"@radix-ui/react-icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/registry/new-york/ui/button\"\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n)\nPagination.displayName = \"Pagination\"\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n))\nPaginationContent.displayName = \"PaginationContent\"\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n))\nPaginationItem.displayName = \"PaginationItem\"\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n)\nPaginationLink.displayName = \"PaginationLink\"\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeftIcon className=\"h-4 w-4\" />\n <span>Previous</span>\n </PaginationLink>\n)\nPaginationPrevious.displayName = \"PaginationPrevious\"\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>Next</span>\n <ChevronRightIcon className=\"h-4 w-4\" />\n </PaginationLink>\n)\nPaginationNext.displayName = \"PaginationNext\"\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <DotsHorizontalIcon className=\"h-4 w-4\" />\n <span className=\"sr-only\">More pages</span>\n </span>\n)\nPaginationEllipsis.displayName = \"PaginationEllipsis\"\n\nexport {\n Pagination,\n PaginationContent,\n PaginationLink,\n PaginationItem,\n PaginationPrevious,\n PaginationNext,\n PaginationEllipsis,\n}\n"
|
||||
}
|
||||
],
|
||||
"type": "components:ui"
|
||||
|
||||
@@ -2,18 +2,21 @@
|
||||
|
||||
import * as React from "react"
|
||||
import useEmblaCarousel, {
|
||||
type EmblaCarouselType as CarouselApi,
|
||||
type EmblaOptionsType as CarouselOptions,
|
||||
type EmblaPluginType as CarouselPlugin,
|
||||
type UseEmblaCarouselType,
|
||||
} from "embla-carousel-react"
|
||||
import { ArrowLeft, ArrowRight } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/registry/default/ui/button"
|
||||
|
||||
type CarouselApi = UseEmblaCarouselType[1]
|
||||
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
|
||||
type CarouselOptions = UseCarouselParameters[0]
|
||||
type CarouselPlugin = UseCarouselParameters[1]
|
||||
|
||||
type CarouselProps = {
|
||||
opts?: CarouselOptions
|
||||
plugins?: CarouselPlugin[]
|
||||
plugins?: CarouselPlugin
|
||||
orientation?: "horizontal" | "vertical"
|
||||
setApi?: (api: CarouselApi) => void
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
Pagination.displayName = "Pagination"
|
||||
|
||||
const PaginationContent = React.forwardRef<
|
||||
HTMLUListElement,
|
||||
@@ -44,19 +45,17 @@ const PaginationLink = ({
|
||||
size = "icon",
|
||||
...props
|
||||
}: PaginationLinkProps) => (
|
||||
<PaginationItem>
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? "outline" : "ghost",
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? "outline" : "ghost",
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
PaginationLink.displayName = "PaginationLink"
|
||||
|
||||
@@ -90,6 +89,7 @@ const PaginationNext = ({
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</PaginationLink>
|
||||
)
|
||||
PaginationNext.displayName = "PaginationNext"
|
||||
|
||||
const PaginationEllipsis = ({
|
||||
className,
|
||||
@@ -104,6 +104,7 @@ const PaginationEllipsis = ({
|
||||
<span className="sr-only">More pages</span>
|
||||
</span>
|
||||
)
|
||||
PaginationEllipsis.displayName = "PaginationEllipsis"
|
||||
|
||||
export {
|
||||
Pagination,
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
import * as React from "react"
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"
|
||||
import useEmblaCarousel, {
|
||||
type EmblaCarouselType as CarouselApi,
|
||||
type EmblaOptionsType as CarouselOptions,
|
||||
type EmblaPluginType as CarouselPlugin,
|
||||
type UseEmblaCarouselType,
|
||||
} from "embla-carousel-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/registry/new-york/ui/button"
|
||||
|
||||
type CarouselApi = UseEmblaCarouselType[1]
|
||||
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
|
||||
type CarouselOptions = UseCarouselParameters[0]
|
||||
type CarouselPlugin = UseCarouselParameters[1]
|
||||
|
||||
type CarouselProps = {
|
||||
opts?: CarouselOptions
|
||||
plugins?: CarouselPlugin[]
|
||||
plugins?: CarouselPlugin
|
||||
orientation?: "horizontal" | "vertical"
|
||||
setApi?: (api: CarouselApi) => void
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
Pagination.displayName = "Pagination"
|
||||
|
||||
const PaginationContent = React.forwardRef<
|
||||
HTMLUListElement,
|
||||
@@ -48,19 +49,17 @@ const PaginationLink = ({
|
||||
size = "icon",
|
||||
...props
|
||||
}: PaginationLinkProps) => (
|
||||
<PaginationItem>
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? "outline" : "ghost",
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? "outline" : "ghost",
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
PaginationLink.displayName = "PaginationLink"
|
||||
|
||||
@@ -94,6 +93,7 @@ const PaginationNext = ({
|
||||
<ChevronRightIcon className="h-4 w-4" />
|
||||
</PaginationLink>
|
||||
)
|
||||
PaginationNext.displayName = "PaginationNext"
|
||||
|
||||
const PaginationEllipsis = ({
|
||||
className,
|
||||
@@ -108,6 +108,7 @@ const PaginationEllipsis = ({
|
||||
<span className="sr-only">More pages</span>
|
||||
</span>
|
||||
)
|
||||
PaginationEllipsis.displayName = "PaginationEllipsis"
|
||||
|
||||
export {
|
||||
Pagination,
|
||||
|
||||
@@ -60,7 +60,6 @@ const ui: Registry = [
|
||||
files: ["ui/carousel.tsx"],
|
||||
registryDependencies: ["button"],
|
||||
dependencies: ["embla-carousel-react"],
|
||||
devDependencies: ["embla-carousel"],
|
||||
},
|
||||
{
|
||||
name: "checkbox",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { existsSync, promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { applyCSSUpdates } from "@/src/utils/css"
|
||||
import {
|
||||
DEFAULT_COMPONENTS,
|
||||
DEFAULT_TAILWIND_CONFIG,
|
||||
@@ -357,16 +358,17 @@ export async function runInit(cwd: string, config: Config) {
|
||||
|
||||
// Write css file.
|
||||
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
|
||||
|
||||
if (baseColor) {
|
||||
await fs.writeFile(
|
||||
const cssFileInput = await fs.readFile(
|
||||
config.resolvedPaths.tailwindCss,
|
||||
config.tailwind.cssVariables
|
||||
? config.tailwind.prefix
|
||||
? applyPrefixesCss(baseColor.cssVarsTemplate, config.tailwind.prefix)
|
||||
: baseColor.cssVarsTemplate
|
||||
: baseColor.inlineColorsTemplate,
|
||||
"utf8"
|
||||
)
|
||||
// const cssFileTemplate = config.tailwind.cssVariables
|
||||
// ? baseColor.cssVarsTemplate
|
||||
// : baseColor.inlineColorsTemplate
|
||||
const cssFileContent = applyCSSUpdates(cssFileInput, baseColor, config)
|
||||
await fs.writeFile(config.resolvedPaths.tailwindCss, cssFileContent, "utf8")
|
||||
}
|
||||
|
||||
// Write cn file.
|
||||
|
||||
86
packages/cli/src/utils/css.ts
Normal file
86
packages/cli/src/utils/css.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Config } from "@/src/utils/get-config"
|
||||
import { registryBaseColorSchema } from "@/src/utils/registry/schema"
|
||||
import { applyPrefixesCss } from "@/src/utils/transformers/transform-tw-prefix"
|
||||
import * as z from "zod"
|
||||
|
||||
// This does a simple string replacement.
|
||||
// TODO: Do we want to use a proper CSS transformer here? Probably not right now.
|
||||
export function applyCSSUpdates(
|
||||
input: string,
|
||||
baseColor: Pick<z.infer<typeof registryBaseColorSchema>, "cssVars">,
|
||||
config: Partial<Config>
|
||||
) {
|
||||
if (!isCSSUpdateRequired(input, config)) {
|
||||
return input
|
||||
}
|
||||
|
||||
let output = input
|
||||
// Check if the file contains the `@tailwind` directives.
|
||||
// We assume it does since Tailwind CSS is checked in preflight.
|
||||
if (!input.includes("@tailwind base")) {
|
||||
// Prepend the `@tailwind` directives.
|
||||
output = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n${input}`
|
||||
}
|
||||
|
||||
// No additional changes if not using CSS variables.
|
||||
if (!config.tailwind?.cssVariables) {
|
||||
return output
|
||||
}
|
||||
|
||||
const lightColors = Object.entries(baseColor.cssVars.light).map(
|
||||
([name, value]) => ` --${name}: ${value};`
|
||||
)
|
||||
const darkColors = Object.entries(baseColor.cssVars.dark).map(
|
||||
([name, value]) => ` --${name}: ${value};`
|
||||
)
|
||||
|
||||
output = output.replace(
|
||||
/@tailwind utilities;/,
|
||||
`@tailwind utilities;\n
|
||||
@layer base {
|
||||
:root {
|
||||
${lightColors.join("\n")}
|
||||
}
|
||||
|
||||
.dark {
|
||||
${darkColors.join("\n")}
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
if (config.tailwind.prefix) {
|
||||
output = applyPrefixesCss(output, config.tailwind.prefix)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
function isCSSUpdateRequired(input: string, config: Partial<Config>) {
|
||||
// Check for tailwind directives.
|
||||
const hasTailwindDirectives =
|
||||
input.includes("@tailwind base") &&
|
||||
input.includes("@tailwind utilities") &&
|
||||
input.includes("@tailwind components")
|
||||
|
||||
// If we're not using CSS variables, we only check for the `@tailwind` directives.
|
||||
if (!config.tailwind?.cssVariables) {
|
||||
return !hasTailwindDirectives
|
||||
}
|
||||
|
||||
// If we are using CSS variables, we check for the `@layer base` directives.
|
||||
// And we check for the `--background` and `--foreground` variables.
|
||||
return !(
|
||||
input.includes("@layer base") &&
|
||||
input.includes("--background") &&
|
||||
input.includes("--foreground")
|
||||
)
|
||||
}
|
||||
27
packages/cli/test/fixtures/css/app-globals.css
vendored
Normal file
27
packages/cli/test/fixtures/css/app-globals.css
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
38
packages/cli/test/fixtures/css/applied-css-vars.css
vendored
Normal file
38
packages/cli/test/fixtures/css/applied-css-vars.css
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
7
packages/cli/test/fixtures/css/applied.css
vendored
Normal file
7
packages/cli/test/fixtures/css/applied.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
3
packages/cli/test/fixtures/css/no-tailwind.css
vendored
Normal file
3
packages/cli/test/fixtures/css/no-tailwind.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
304
packages/cli/test/utils/__snapshots__/css.test.ts.snap
Normal file
304
packages/cli/test/utils/__snapshots__/css.test.ts.snap
Normal file
@@ -0,0 +1,304 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`apply css updates 1`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 2`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 3`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 4`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 5`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply tw-border-border;
|
||||
}
|
||||
body {
|
||||
@apply tw-bg-background tw-text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 6`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply cn-border-border;
|
||||
}
|
||||
body {
|
||||
@apply cn-bg-background cn-text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 7`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 8`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
154
packages/cli/test/utils/css.test.ts
Normal file
154
packages/cli/test/utils/css.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { expect, test } from "vitest"
|
||||
|
||||
import { applyCSSUpdates } from "../../src/utils/css"
|
||||
|
||||
const baseColor = {
|
||||
cssVars: {
|
||||
light: {
|
||||
background: "0 0% 100%",
|
||||
foreground: "224 71.4% 4.1%",
|
||||
primary: "220.9 39.3% 11%",
|
||||
"primary-foreground": "210 20% 98%",
|
||||
border: "220 13% 91%",
|
||||
input: "220 13% 91%",
|
||||
ring: "224 71.4% 4.1%",
|
||||
},
|
||||
dark: {
|
||||
background: "224 71.4% 4.1%",
|
||||
foreground: "210 20% 98%",
|
||||
primary: "210 20% 98%",
|
||||
"primary-foreground": "220.9 39.3% 11%",
|
||||
border: "215 27.9% 16.9%",
|
||||
input: "215 27.9% 16.9%",
|
||||
ring: "216 12.2% 83.9%",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test("apply css updates", async () => {
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
// Prefix.
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
prefix: "tw-",
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
prefix: "cn-",
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
// Applied.
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/applied.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/applied-css-vars.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
Reference in New Issue
Block a user