diff --git a/apps/docs/package.json b/apps/docs/package.json index 05f7e257..e5946095 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -25,7 +25,8 @@ "chart.js": "^4.4.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", - "corvu": "^0.4.8", + "cmdk-solid": "^1.0.1", + "corvu": "^0.5.0", "embla-carousel-autoplay": "^8.0.0", "embla-carousel-solid": "^8.0.0", "shosho": "^1.4.3", diff --git a/apps/docs/public/registry/index.json b/apps/docs/public/registry/index.json index 7971eda4..1e3e4298 100644 --- a/apps/docs/public/registry/index.json +++ b/apps/docs/public/registry/index.json @@ -283,6 +283,16 @@ ], "type": "ui" }, + { + "name": "progress-circle", + "dependencies": [ + "@kobalte/core" + ], + "files": [ + "ui/progress-circle.tsx" + ], + "type": "ui" + }, { "name": "radio-group", "dependencies": [ @@ -293,6 +303,16 @@ ], "type": "ui" }, + { + "name": "resizable", + "dependencies": [ + "corvu" + ], + "files": [ + "ui/resizable.tsx" + ], + "type": "ui" + }, { "name": "select", "dependencies": [ @@ -401,6 +421,16 @@ ], "type": "ui" }, + { + "name": "toggle-group", + "dependencies": [ + "@kobalte/core" + ], + "files": [ + "ui/toggle-group.tsx" + ], + "type": "ui" + }, { "name": "tooltip", "dependencies": [ diff --git a/apps/docs/public/registry/ui/carousel.json b/apps/docs/public/registry/ui/carousel.json index cc9b6373..49d69c53 100644 --- a/apps/docs/public/registry/ui/carousel.json +++ b/apps/docs/public/registry/ui/carousel.json @@ -6,7 +6,7 @@ "files": [ { "name": "carousel.tsx", - "content": "import type { Accessor, Component, ComponentProps, VoidProps } from \"solid-js\"\nimport {\n createContext,\n createEffect,\n createMemo,\n createSignal,\n mergeProps,\n splitProps,\n useContext\n} from \"solid-js\"\n\nimport type { CreateEmblaCarouselType } from \"embla-carousel-solid\"\nimport createEmblaCarousel from \"embla-carousel-solid\"\n\nimport { cn } from \"~/lib/utils\"\nimport { Button, ButtonProps } from \"~/registry/ui/button\"\n\nexport type CarouselApi = CreateEmblaCarouselType[1]\n\ntype UseCarouselParameters = Parameters\ntype CarouselOptions = NonNullable\ntype CarouselPlugin = NonNullable\n\ntype CarouselProps = {\n opts?: ReturnType\n plugins?: ReturnType\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType[0]\n api: ReturnType[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: Accessor\n canScrollNext: Accessor\n} & CarouselProps\n\nconst CarouselContext = createContext | null>(null)\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a \")\n }\n\n return context()\n}\n\nconst Carousel: Component> = (rawProps) => {\n const props = mergeProps<(CarouselProps & ComponentProps<\"div\">)[]>(\n { orientation: \"horizontal\" },\n rawProps\n )\n\n const [, rest] = splitProps(props, [\n \"orientation\",\n \"opts\",\n \"setApi\",\n \"plugins\",\n \"class\",\n \"children\"\n ])\n\n const [carouselRef, api] = createEmblaCarousel(\n () => ({\n ...props.opts,\n axis: props.orientation === \"horizontal\" ? \"x\" : \"y\"\n }),\n () => (props.plugins === undefined ? [] : props.plugins)\n )\n const [canScrollPrev, setCanScrollPrev] = createSignal(false)\n const [canScrollNext, setCanScrollNext] = createSignal(false)\n\n const onSelect = (api: NonNullable>) => {\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n }\n\n const scrollPrev = () => {\n api()?.scrollPrev()\n }\n\n const scrollNext = () => {\n api()?.scrollNext()\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n }\n\n createEffect(() => {\n if (!api() || !props.setApi) {\n return\n }\n\n props.setApi(api)\n })\n\n createEffect(() => {\n if (!api()) {\n return\n }\n\n onSelect(api()!)\n api()!.on(\"reInit\", onSelect)\n api()!.on(\"select\", onSelect)\n\n return () => {\n api()?.off(\"select\", onSelect)\n }\n })\n\n const value = createMemo(\n () =>\n ({\n carouselRef,\n api,\n opts: props.opts,\n orientation: props.orientation || (props.opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext\n }) satisfies CarouselContextProps\n )\n\n return (\n \n \n {props.children}\n \n \n )\n}\n\nconst CarouselContent: Component> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { carouselRef, orientation } = useCarousel()\n\n return (\n
\n \n
\n )\n}\n\nconst CarouselItem: Component> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { orientation } = useCarousel()\n\n return (\n \n )\n}\n\ntype CarouselButtonProps = VoidProps\n\nconst CarouselPrevious: Component = (rawProps) => {\n const props = mergeProps({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n \n \n \n \n \n \n Previous slide\n \n )\n}\n\nconst CarouselNext: Component = (rawProps) => {\n const props = mergeProps({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n \n \n \n \n \n \n Next slide\n \n )\n}\n\nexport { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }\n" + "content": "import type { Accessor, Component, ComponentProps, VoidProps } from \"solid-js\"\nimport {\n createContext,\n createEffect,\n createMemo,\n createSignal,\n mergeProps,\n splitProps,\n useContext\n} from \"solid-js\"\n\nimport type { CreateEmblaCarouselType } from \"embla-carousel-solid\"\nimport createEmblaCarousel from \"embla-carousel-solid\"\n\nimport { cn } from \"~/lib/utils\"\nimport { Button, ButtonProps } from \"~/registry/ui/button\"\n\nexport type CarouselApi = CreateEmblaCarouselType[1]\n\ntype UseCarouselParameters = Parameters\ntype CarouselOptions = NonNullable\ntype CarouselPlugin = NonNullable\n\ntype CarouselProps = {\n opts?: ReturnType\n plugins?: ReturnType\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType[0]\n api: ReturnType[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: Accessor\n canScrollNext: Accessor\n} & CarouselProps\n\nconst CarouselContext = createContext | null>(null)\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a \")\n }\n\n return context()\n}\n\nconst Carousel: Component> = (rawProps) => {\n const props = mergeProps<(CarouselProps & ComponentProps<\"div\">)[]>(\n { orientation: \"horizontal\" },\n rawProps\n )\n\n const [, rest] = splitProps(props, [\n \"orientation\",\n \"opts\",\n \"setApi\",\n \"plugins\",\n \"class\",\n \"children\"\n ])\n\n const [carouselRef, api] = createEmblaCarousel(\n () => ({\n ...props.opts,\n axis: props.orientation === \"horizontal\" ? \"x\" : \"y\"\n }),\n () => (props.plugins === undefined ? [] : props.plugins)\n )\n const [canScrollPrev, setCanScrollPrev] = createSignal(false)\n const [canScrollNext, setCanScrollNext] = createSignal(false)\n\n const onSelect = (api: NonNullable>) => {\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n }\n\n const scrollPrev = () => {\n api()?.scrollPrev()\n }\n\n const scrollNext = () => {\n api()?.scrollNext()\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n }\n\n createEffect(() => {\n if (!api() || !props.setApi) {\n return\n }\n\n props.setApi(api)\n })\n\n createEffect(() => {\n if (!api()) {\n return\n }\n\n onSelect(api()!)\n api()!.on(\"reInit\", onSelect)\n api()!.on(\"select\", onSelect)\n\n return () => {\n api()?.off(\"select\", onSelect)\n }\n })\n\n const value = createMemo(\n () =>\n ({\n carouselRef,\n api,\n opts: props.opts,\n orientation: props.orientation || (props.opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext\n }) satisfies CarouselContextProps\n )\n\n return (\n \n \n {props.children}\n \n \n )\n}\n\nconst CarouselContent: Component> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { carouselRef, orientation } = useCarousel()\n\n return (\n
\n \n
\n )\n}\n\nconst CarouselItem: Component> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { orientation } = useCarousel()\n\n return (\n \n )\n}\n\ntype CarouselButtonProps = VoidProps\n\nconst CarouselPrevious: Component = (rawProps) => {\n const props = mergeProps({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n \n \n \n \n \n \n Previous slide\n \n )\n}\n\nconst CarouselNext: Component = (rawProps) => {\n const props = mergeProps({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n \n \n \n \n \n \n Next slide\n \n )\n}\n\nexport { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }\n" } ], "type": "ui" diff --git a/apps/docs/public/registry/ui/progress-circle.json b/apps/docs/public/registry/ui/progress-circle.json new file mode 100644 index 00000000..92d4ff66 --- /dev/null +++ b/apps/docs/public/registry/ui/progress-circle.json @@ -0,0 +1,13 @@ +{ + "name": "progress-circle", + "dependencies": [ + "@kobalte/core" + ], + "files": [ + { + "name": "progress-circle.tsx", + "content": "import { Component, ComponentProps, mergeProps, splitProps } from \"solid-js\"\n\nimport { cn } from \"~/lib/utils\"\n\ntype Size = \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst sizes: Record = {\n xs: { radius: 15, strokeWidth: 3 },\n sm: { radius: 19, strokeWidth: 4 },\n md: { radius: 32, strokeWidth: 6 },\n lg: { radius: 52, strokeWidth: 8 },\n xl: { radius: 80, strokeWidth: 10 }\n}\n\nexport interface ProgressCircleProps extends ComponentProps<\"div\"> {\n value?: number\n size?: Size\n radius?: number\n strokeWidth?: number\n showAnimation?: boolean\n}\n\nconst ProgressCircle: Component = (rawProps) => {\n const props = mergeProps({ size: \"md\" as Size, showAnimation: true }, rawProps)\n const [, rest] = splitProps(props, [\n \"class\",\n \"children\",\n \"value\",\n \"size\",\n \"radius\",\n \"strokeWidth\",\n \"showAnimation\"\n ])\n\n const value = () => getLimitedValue(props.value)\n const radius = () => props.radius ?? sizes[props.size].radius\n const strokeWidth = () => props.strokeWidth ?? sizes[props.size].strokeWidth\n const normalizedRadius = () => radius() - strokeWidth() / 2\n const circumference = () => normalizedRadius() * 2 * Math.PI\n const strokeDashoffset = () => (value() / 100) * circumference()\n const offset = () => circumference() - strokeDashoffset()\n\n return (\n
\n \n \n {value() >= 0 ? (\n \n ) : null}\n \n
{props.children}
\n
\n )\n}\n\nfunction getLimitedValue(input: number | undefined) {\n if (input === undefined) {\n return 0\n } else if (input > 100) {\n return 100\n }\n return input\n}\n\nexport { ProgressCircle }\n" + } + ], + "type": "ui" +} \ No newline at end of file diff --git a/apps/docs/public/registry/ui/resizable.json b/apps/docs/public/registry/ui/resizable.json new file mode 100644 index 00000000..944581da --- /dev/null +++ b/apps/docs/public/registry/ui/resizable.json @@ -0,0 +1,13 @@ +{ + "name": "resizable", + "dependencies": [ + "corvu" + ], + "files": [ + { + "name": "resizable.tsx", + "content": "import { Component, Show, splitProps } from \"solid-js\"\n\nimport { HandleProps, Resizable as ResizablePrimitive, RootProps } from \"corvu/resizable\"\n\nimport { cn } from \"~/lib/utils\"\n\nconst Resizable: Component = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n return (\n \n )\n}\n\nconst ResizablePanel = ResizablePrimitive.Panel\n\nconst ResizableHandle: Component = (props) => {\n const [, rest] = splitProps(props, [\"class\", \"withHandle\"])\n return (\n div]:rotate-90\",\n props.class\n )}\n {...rest}\n >\n \n
\n \n \n \n \n \n \n \n \n
\n
\n \n )\n}\n\nexport { Resizable, ResizablePanel, ResizableHandle }\n" + } + ], + "type": "ui" +} \ No newline at end of file diff --git a/apps/docs/public/registry/ui/toggle-group.json b/apps/docs/public/registry/ui/toggle-group.json new file mode 100644 index 00000000..3593be03 --- /dev/null +++ b/apps/docs/public/registry/ui/toggle-group.json @@ -0,0 +1,13 @@ +{ + "name": "toggle-group", + "dependencies": [ + "@kobalte/core" + ], + "files": [ + { + "name": "toggle-group.tsx", + "content": "import { Component, createContext, splitProps } from \"solid-js\"\n\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@kobalte/core\"\nimport { VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"~/lib/utils\"\nimport { toggleVariants } from \"~/registry/ui/toggle\"\n\nconst ToggleGroupContext = createContext>({\n size: \"default\",\n variant: \"default\"\n})\n\ntype ToggleGroupProps = ToggleGroupPrimitive.Root & VariantProps\n\nconst ToggleGroup: Component = (props) => {\n const [, rest] = splitProps(props, [\"class\", \"children\", \"variant\", \"size\"])\n return (\n \n \n {props.children}\n \n \n )\n}\n\nexport { ToggleGroup }\n" + } + ], + "type": "ui" +} \ No newline at end of file diff --git a/apps/docs/src/__registry__/index.tsx b/apps/docs/src/__registry__/index.tsx index b15c8248..f29d4304 100644 --- a/apps/docs/src/__registry__/index.tsx +++ b/apps/docs/src/__registry__/index.tsx @@ -216,6 +216,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/ui/progress")), files: ["registry/ui/progress.tsx"], }, + "progress-circle": { + name: "progress-circle", + type: "ui", + registryDependencies: undefined, + component: lazy(() => import("~/registry/ui/progress-circle")), + files: ["registry/ui/progress-circle.tsx"], + }, "radio-group": { name: "radio-group", type: "ui", @@ -223,6 +230,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/ui/radio-group")), files: ["registry/ui/radio-group.tsx"], }, + "resizable": { + name: "resizable", + type: "ui", + registryDependencies: undefined, + component: lazy(() => import("~/registry/ui/resizable")), + files: ["registry/ui/resizable.tsx"], + }, "select": { name: "select", type: "ui", @@ -307,6 +321,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/ui/toggle")), files: ["registry/ui/toggle.tsx"], }, + "toggle-group": { + name: "toggle-group", + type: "ui", + registryDependencies: undefined, + component: lazy(() => import("~/registry/ui/toggle-group")), + files: ["registry/ui/toggle-group.tsx"], + }, "tooltip": { name: "tooltip", type: "ui", @@ -587,6 +608,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/example/progress-demo")), files: ["registry/example/progress-demo.tsx"], }, + "progress-circle-demo": { + name: "progress-circle-demo", + type: "example", + registryDependencies: undefined, + component: lazy(() => import("~/registry/example/progress-circle-demo")), + files: ["registry/example/progress-circle-demo.tsx"], + }, "radio-group-demo": { name: "radio-group-demo", type: "example", @@ -594,6 +622,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/example/radio-group-demo")), files: ["registry/example/radio-group-demo.tsx"], }, + "resizable-demo": { + name: "resizable-demo", + type: "example", + registryDependencies: undefined, + component: lazy(() => import("~/registry/example/resizable-demo")), + files: ["registry/example/resizable-demo.tsx"], + }, "select-demo": { name: "select-demo", type: "example", @@ -678,6 +713,13 @@ export const Index: Record = { component: lazy(() => import("~/registry/example/toggle-demo")), files: ["registry/example/toggle-demo.tsx"], }, + "toggle-group-demo": { + name: "toggle-group-demo", + type: "example", + registryDependencies: undefined, + component: lazy(() => import("~/registry/example/toggle-group-demo")), + files: ["registry/example/toggle-group-demo.tsx"], + }, "tooltip-demo": { name: "tooltip-demo", type: "example", diff --git a/apps/docs/src/config/docs.ts b/apps/docs/src/config/docs.ts index 903c0c3c..f0cac17e 100644 --- a/apps/docs/src/config/docs.ts +++ b/apps/docs/src/config/docs.ts @@ -2,7 +2,7 @@ type NavElement = { title: string href: string external?: boolean - status?: string + status?: "new" | "updated" } type NavCategory = { @@ -101,6 +101,10 @@ export const docsConfig: Config = { title: "Badge", href: "/docs/components/badge" }, + { + title: "Badge Delta", + href: "/docs/components/badge-delta" + }, { title: "Button", href: "/docs/components/button" @@ -190,10 +194,20 @@ export const docsConfig: Config = { title: "Progress", href: "/docs/components/progress" }, + { + title: "Progress Circle", + href: "/docs/components/progress-circle", + status: "new" + }, { title: "Radio Group", href: "/docs/components/radio-group" }, + { + title: "Resizable", + href: "/docs/components/resizable", + status: "new" + }, { title: "Select", href: "/docs/components/select" @@ -242,6 +256,11 @@ export const docsConfig: Config = { title: "Toggle", href: "/docs/components/toggle" }, + // { + // title: "Toggle Group", + // href: "/docs/components/toggle-group", + // status: "new" + // }, { title: "Tooltip", href: "/docs/components/tooltip" diff --git a/apps/docs/src/registry/example/progress-circle-demo.tsx b/apps/docs/src/registry/example/progress-circle-demo.tsx new file mode 100644 index 00000000..60197bf1 --- /dev/null +++ b/apps/docs/src/registry/example/progress-circle-demo.tsx @@ -0,0 +1,59 @@ +import { Avatar, AvatarFallback, AvatarImage } from "~/registry/ui/avatar" +import { Card } from "~/registry/ui/card" +import { ProgressCircle } from "~/registry/ui/progress-circle" + +export default function ProgressCircleDemo() { + return ( +
+

Without children

+ +
+ +
+

+ $340/$450 (75%) +

+

+ Spend management control +

+
+
+
+

Progress value as children

+ +
+ + 75% + +
+

+ $340/$450 (75%) +

+

+ Spend management control +

+
+
+
+

Avatar as children

+ +
+ + + + EK + + +
+

+ $340/$450 (75%) +

+

+ Spend management control +

+
+
+
+
+ ) +} diff --git a/apps/docs/src/registry/example/resizable-demo.tsx b/apps/docs/src/registry/example/resizable-demo.tsx new file mode 100644 index 00000000..3d50e6d4 --- /dev/null +++ b/apps/docs/src/registry/example/resizable-demo.tsx @@ -0,0 +1,29 @@ +import { Resizable, ResizableHandle, ResizablePanel } from "../ui/resizable" + +export default function ResizableDemo() { + return ( + + +
+ One +
+
+ + + + +
+ Two +
+
+ + +
+ Three +
+
+
+
+
+ ) +} diff --git a/apps/docs/src/registry/example/toggle-group-demo.tsx b/apps/docs/src/registry/example/toggle-group-demo.tsx new file mode 100644 index 00000000..2943498e --- /dev/null +++ b/apps/docs/src/registry/example/toggle-group-demo.tsx @@ -0,0 +1,3 @@ +export default function ToggleGroupDemo() { + return <> +} diff --git a/apps/docs/src/registry/registry.ts b/apps/docs/src/registry/registry.ts index 594a70cb..b08e90f4 100644 --- a/apps/docs/src/registry/registry.ts +++ b/apps/docs/src/registry/registry.ts @@ -175,12 +175,24 @@ const ui: Registry = [ registryDependencies: ["label"], files: ["ui/progress.tsx"] }, + { + name: "progress-circle", + type: "ui", + dependencies: ["@kobalte/core"], + files: ["ui/progress-circle.tsx"] + }, { name: "radio-group", type: "ui", dependencies: ["@kobalte/core"], files: ["ui/radio-group.tsx"] }, + { + name: "resizable", + type: "ui", + dependencies: ["corvu"], + files: ["ui/resizable.tsx"] + }, { name: "select", type: "ui", @@ -249,6 +261,12 @@ const ui: Registry = [ dependencies: ["@kobalte/core"], files: ["ui/toggle.tsx"] }, + { + name: "toggle-group", + type: "ui", + dependencies: ["@kobalte/core"], + files: ["ui/toggle-group.tsx"] + }, { name: "tooltip", type: "ui", @@ -464,11 +482,21 @@ const examples: Registry = [ type: "example", files: ["example/progress-demo.tsx"] }, + { + name: "progress-circle-demo", + type: "example", + files: ["example/progress-circle-demo.tsx"] + }, { name: "radio-group-demo", type: "example", files: ["example/radio-group-demo.tsx"] }, + { + name: "resizable-demo", + type: "example", + files: ["example/resizable-demo.tsx"] + }, { name: "select-demo", type: "example", @@ -529,6 +557,11 @@ const examples: Registry = [ type: "example", files: ["example/toggle-demo.tsx"] }, + { + name: "toggle-group-demo", + type: "example", + files: ["example/toggle-group-demo.tsx"] + }, { name: "tooltip-demo", type: "example", diff --git a/apps/docs/src/registry/ui/progress-circle.tsx b/apps/docs/src/registry/ui/progress-circle.tsx new file mode 100644 index 00000000..829d9321 --- /dev/null +++ b/apps/docs/src/registry/ui/progress-circle.tsx @@ -0,0 +1,93 @@ +import { Component, ComponentProps, mergeProps, splitProps } from "solid-js" + +import { cn } from "~/lib/utils" + +type Size = "xs" | "sm" | "md" | "lg" | "xl" + +const sizes: Record = { + xs: { radius: 15, strokeWidth: 3 }, + sm: { radius: 19, strokeWidth: 4 }, + md: { radius: 32, strokeWidth: 6 }, + lg: { radius: 52, strokeWidth: 8 }, + xl: { radius: 80, strokeWidth: 10 } +} + +export interface ProgressCircleProps extends ComponentProps<"div"> { + value?: number + size?: Size + radius?: number + strokeWidth?: number + showAnimation?: boolean +} + +const ProgressCircle: Component = (rawProps) => { + const props = mergeProps({ size: "md" as Size, showAnimation: true }, rawProps) + const [, rest] = splitProps(props, [ + "class", + "children", + "value", + "size", + "radius", + "strokeWidth", + "showAnimation" + ]) + + const value = () => getLimitedValue(props.value) + const radius = () => props.radius ?? sizes[props.size].radius + const strokeWidth = () => props.strokeWidth ?? sizes[props.size].strokeWidth + const normalizedRadius = () => radius() - strokeWidth() / 2 + const circumference = () => normalizedRadius() * 2 * Math.PI + const strokeDashoffset = () => (value() / 100) * circumference() + const offset = () => circumference() - strokeDashoffset() + + return ( +
+ + + {value() >= 0 ? ( + + ) : null} + +
{props.children}
+
+ ) +} + +function getLimitedValue(input: number | undefined) { + if (input === undefined) { + return 0 + } else if (input > 100) { + return 100 + } + return input +} + +export { ProgressCircle } diff --git a/apps/docs/src/registry/ui/resizable.tsx b/apps/docs/src/registry/ui/resizable.tsx new file mode 100644 index 00000000..32f464f0 --- /dev/null +++ b/apps/docs/src/registry/ui/resizable.tsx @@ -0,0 +1,54 @@ +import { Component, Show, splitProps } from "solid-js" + +import { HandleProps, Resizable as ResizablePrimitive, RootProps } from "corvu/resizable" + +import { cn } from "~/lib/utils" + +const Resizable: Component = (props) => { + const [, rest] = splitProps(props, ["class"]) + return ( + + ) +} + +const ResizablePanel = ResizablePrimitive.Panel + +const ResizableHandle: Component = (props) => { + const [, rest] = splitProps(props, ["class", "withHandle"]) + return ( + div]:rotate-90", + props.class + )} + {...rest} + > + +
+ + + + + + + + +
+
+
+ ) +} + +export { Resizable, ResizablePanel, ResizableHandle } diff --git a/apps/docs/src/registry/ui/toggle-group.tsx b/apps/docs/src/registry/ui/toggle-group.tsx new file mode 100644 index 00000000..8949a48b --- /dev/null +++ b/apps/docs/src/registry/ui/toggle-group.tsx @@ -0,0 +1,30 @@ +import { Component, createContext, splitProps } from "solid-js" + +import { ToggleGroup as ToggleGroupPrimitive } from "@kobalte/core" +import { VariantProps } from "class-variance-authority" + +import { cn } from "~/lib/utils" +import { toggleVariants } from "~/registry/ui/toggle" + +const ToggleGroupContext = createContext>({ + size: "default", + variant: "default" +}) + +type ToggleGroupProps = ToggleGroupPrimitive.Root & VariantProps + +const ToggleGroup: Component = (props) => { + const [, rest] = splitProps(props, ["class", "children", "variant", "size"]) + return ( + + + {props.children} + + + ) +} + +export { ToggleGroup } diff --git a/apps/docs/src/routes/docs/about.mdx b/apps/docs/src/routes/docs/about.mdx index 4ce31bb6..1a75254d 100644 --- a/apps/docs/src/routes/docs/about.mdx +++ b/apps/docs/src/routes/docs/about.mdx @@ -2,9 +2,10 @@ ## Credits -- [shadcn/ui](https://github.com/shadcn/ui) - Without @shadcn this project wouldn't be possible! ♥ -- [tremor](https://github.com/tremorlabs/tremor) - For the awesome components that perfectly fit with shadcn/ui. +- [shadcn/ui](https://github.com/shadcn/ui) - Without @shadcn, this project wouldn't have been possible! +- [tremor](https://github.com/tremorlabs/tremor) - For the components that perfectly fit with shadcn/ui. - [Kobalte.](https://github.com/kobaltedev/kobalte) - For the primitives. +- [corvu](https://corvu.dev/) - For the primitives. ## License diff --git a/apps/docs/src/routes/docs/components/badge-delta.mdx b/apps/docs/src/routes/docs/components/badge-delta.mdx new file mode 100644 index 00000000..15667526 --- /dev/null +++ b/apps/docs/src/routes/docs/components/badge-delta.mdx @@ -0,0 +1,45 @@ + + + + +## Installation + + + + + CLI + Manual + + + + +```bash +npx solidui-cli@latest add badge-delta +``` + + + + + + +Copy and paste the following code into your project: + + + + + + + + +## Usage + +```tsx +import { BadgeDelta } from "~/components/ui/badge-delta" +``` + +```tsx ++ 420.69% +``` diff --git a/apps/docs/src/routes/docs/components/badge.mdx b/apps/docs/src/routes/docs/components/badge.mdx index 7d3c57c6..91632512 100644 --- a/apps/docs/src/routes/docs/components/badge.mdx +++ b/apps/docs/src/routes/docs/components/badge.mdx @@ -38,46 +38,3 @@ import { Badge } from "~/components/ui/badge" ```tsx Badge ``` - -## Badge Delta - - - -## Installation - - - - - CLI - Manual - - - - -```bash -npx solidui-cli@latest add badge-delta -``` - - - - - - -Copy and paste the following code into your project: - - - - - - - - -## Usage - -```tsx -import { BadgeDelta } from "~/components/ui/badge-delta" -``` - -```tsx -+ 420.69% -``` diff --git a/apps/docs/src/routes/docs/components/drawer.mdx b/apps/docs/src/routes/docs/components/drawer.mdx index cab9d8f7..fa1bcf31 100644 --- a/apps/docs/src/routes/docs/components/drawer.mdx +++ b/apps/docs/src/routes/docs/components/drawer.mdx @@ -6,10 +6,6 @@ -## About - -Drawer is built on top of [corvu](https://corvu.dev/) by [GiyoMoon](https://github.com/GiyoMoon). - ## Installation diff --git a/apps/docs/src/routes/docs/components/progress-circle.mdx b/apps/docs/src/routes/docs/components/progress-circle.mdx new file mode 100644 index 00000000..16021806 --- /dev/null +++ b/apps/docs/src/routes/docs/components/progress-circle.mdx @@ -0,0 +1,45 @@ + + + + +## Installation + + + + + CLI + Manual + + + + +```bash +npx solidui-cli@latest add progress-circle +``` + + + + + + +Copy and paste the following code into your project: + + + + + + + + +## Usage + +```tsx +import { ProgressCircle } from "~/components/ui/progress-circle" +``` + +```tsx + +``` diff --git a/apps/docs/src/routes/docs/components/resizable.mdx b/apps/docs/src/routes/docs/components/resizable.mdx new file mode 100644 index 00000000..2fdd43b4 --- /dev/null +++ b/apps/docs/src/routes/docs/components/resizable.mdx @@ -0,0 +1,60 @@ + + + + +## About + +Drawer is built on top of [corvu](https://corvu.dev/) by [GiyoMoon](https://github.com/GiyoMoon). + +## Installation + + + + + CLI + Manual + + + + +```bash +npx solidui-cli@latest add resizable +``` + + + + + + +Install the following dependencies: + +```bash +npm install corvu +``` + +Copy and paste the following code into your project: + + + + + + + + +## Usage + +```tsx +import { Resizable, ResizableHandle, ResizablePanel } from "~/components/ui/resizable" +``` + +```tsx + + One + + Two + +``` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0fadc87..82452af6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,9 +96,12 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.0 + cmdk-solid: + specifier: ^1.0.1 + version: 1.0.1(solid-js@1.8.16) corvu: - specifier: ^0.4.8 - version: 0.4.8(solid-js@1.8.16) + specifier: ^0.5.0 + version: 0.5.0(solid-js@1.8.16) embla-carousel-autoplay: specifier: ^8.0.0 version: 8.0.0(embla-carousel@8.0.0) @@ -1791,13 +1794,6 @@ packages: '@floating-ui/utils': 0.2.1 dev: false - /@floating-ui/dom@1.6.1: - resolution: {integrity: sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==} - dependencies: - '@floating-ui/core': 1.6.0 - '@floating-ui/utils': 0.2.1 - dev: false - /@floating-ui/dom@1.6.3: resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} dependencies: @@ -1936,7 +1932,7 @@ packages: peerDependencies: solid-js: ^1.8.15 dependencies: - '@floating-ui/dom': 1.6.1 + '@floating-ui/dom': 1.6.3 '@internationalized/date': 3.4.0 '@internationalized/number': 3.2.1 '@kobalte/utils': 0.9.0(solid-js@1.8.16) @@ -2473,6 +2469,15 @@ packages: engines: {node: '>=18'} dev: false + /@solid-primitives/deep@0.2.8(solid-js@1.8.16): + resolution: {integrity: sha512-r+enqrL2oiDkJt7lYUYMVvKzcqVDAd4G/1OkLnXEibk5nIA635qNIuOxIAaCCP3GDAHKznaW3yc/vMG68lo8bQ==} + peerDependencies: + solid-js: ^1.6.12 + dependencies: + '@solid-primitives/memo': 1.3.8(solid-js@1.8.16) + solid-js: 1.8.16 + dev: false + /@solid-primitives/event-listener@2.3.0(solid-js@1.8.16): resolution: {integrity: sha512-0DS7DQZvCExWSpurVZC9/wjI8RmkhuOtWOy6Pp1Woq9ElMT9/bfjNpkwXsOwisLpcTqh9eUs17kp7jtpWcC20w==} peerDependencies: @@ -2541,6 +2546,15 @@ packages: solid-js: 1.8.16 dev: false + /@solid-primitives/mutation-observer@1.1.17(solid-js@1.8.16): + resolution: {integrity: sha512-01skkiHtNWl2PQ0ugFzZHBMsHk870+bZSNK8Gvjcj3mZwBzAJPaUPzFswfM/V8Io1Cko86vfXl73vl9ddRYb5A==} + peerDependencies: + solid-js: ^1.6.12 + dependencies: + '@solid-primitives/utils': 6.2.3(solid-js@1.8.16) + solid-js: 1.8.16 + dev: false + /@solid-primitives/props@3.1.9(solid-js@1.8.16): resolution: {integrity: sha512-2MvjgxF0rwMR8BGq2onN9KhxF66bQhtFWdrEkO/qjy8I4wr/OQrhwgM9RP89/gtzOVPXUYKSxt2lkCk+JQpNJA==} peerDependencies: @@ -2666,7 +2680,7 @@ packages: /@swc/helpers@0.5.1: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: - tslib: 2.6.1 + tslib: 2.6.2 dev: false /@ts-morph/common@0.21.0: @@ -3762,6 +3776,18 @@ packages: engines: {node: '>=0.10.0'} dev: false + /cmdk-solid@1.0.1(solid-js@1.8.16): + resolution: {integrity: sha512-0BiQaIc4OUTssu5+q/7FwV+fN9EEWPt9Mc0LQYGSB6b+g26br8evCzgIEA0skjVmmlhSWdsEjFAo2/gbCGAc8Q==} + peerDependencies: + solid-js: ^1.8.0 + dependencies: + '@kobalte/core': 0.12.6(solid-js@1.8.16) + '@kobalte/utils': 0.9.0(solid-js@1.8.16) + '@solid-primitives/deep': 0.2.8(solid-js@1.8.16) + '@solid-primitives/mutation-observer': 1.1.17(solid-js@1.8.16) + solid-js: 1.8.16 + dev: false + /code-block-writer@12.0.0: resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} dev: false @@ -3847,8 +3873,8 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: false - /corvu@0.4.8(solid-js@1.8.16): - resolution: {integrity: sha512-6oway9VuOiLUDYYo7jtd4OVdOxiIEhCd6HaswKQLX0q8+dc5Ul2tMhrVr8YRFbf0OE4TBiveJ+px+afSH30MJg==} + /corvu@0.5.0(solid-js@1.8.16): + resolution: {integrity: sha512-lgnXyDJZ0Ed/3nEulZlXp48DyO13Z9YwZA36JlCpPAFFR6WFoK6NufPaCQeKiz7yll8v14P5IIUFtp6ZeEOPJg==} peerDependencies: solid-js: ^1.8 dependencies: