diff --git a/apps/www/app/global.css b/apps/www/app/global.css
index 7e4c53f..c493916 100644
--- a/apps/www/app/global.css
+++ b/apps/www/app/global.css
@@ -18,6 +18,10 @@
--headless-color: 250 80% 54%;
--mdx-color: 0 0% 9%;
--ui-color: 220deg 91% 54%;
+ --shiki-light: #4c4f69;
+ --shiki-dark: #cdd6f4;
+ --shiki-light-bg: #eff1f5;
+ --shiki-dark-bg: #0f0f0f !important;
}
.dark {
@@ -49,8 +53,12 @@
--card: 230 40% 26.3% !important;
}
+/* #nd-docs-layout {
+ background: var(--popover);
+} */
+
.words_width_animat {
- animation: words_width_animation 4s normal;
+ animation: words_width_animation 2.5s normal;
}
.words_width_lens_body {
@@ -66,11 +74,11 @@
}
.words_interfaces::after {
- animation: words_interfaces_after_animation 3.5s normal;
+ animation: words_interfaces_after_animation 1.5s normal;
}
.words_interfaces::before {
- animation: words_interfaces_before_animation 3.5s normal;
+ animation: words_interfaces_before_animation 1.5s normal;
}
@keyframes words_width_animation {
@@ -82,6 +90,10 @@
top: -28px;
opacity: 0.5;
}
+ 75% {
+ top: -28px;
+ opacity: 1;
+ }
100% {
top: -28px;
opacity: 1;
@@ -126,10 +138,10 @@
width: 0;
opacity: 0;
}
- 65% {
+ /* 65% {
width: 205px;
opacity: 0.5;
- }
+ } */
100% {
width: 205px;
opacity: 1;
@@ -141,10 +153,10 @@
height: 0;
opacity: 0;
}
- 65% {
+ /* 65% {
height: 60px;
opacity: 0.5;
- }
+ } */
100% {
height: 60px;
opacity: 1;
diff --git a/apps/www/app/layout.tsx b/apps/www/app/layout.tsx
index e3b27f6..5656366 100644
--- a/apps/www/app/layout.tsx
+++ b/apps/www/app/layout.tsx
@@ -6,7 +6,9 @@ import { GeistSans } from "geist/font/sans";
import type { ReactNode } from "react";
import { RuruProvider } from "ruru-ui/provider";
import "fumadocs-ui/style.css";
+import "fumadocs-ui/twoslash.css";
import { ScrollArea } from "@/components/scroll-area";
+import { Analytics } from "@vercel/analytics/react";
export const metadata = createMetadata({
title: {
@@ -29,8 +31,9 @@ export default function Layout({ children }: { children: ReactNode }) {
return (
+
-
+
{children}
diff --git a/apps/www/app/playground/page.tsx b/apps/www/app/playground/page.tsx
new file mode 100644
index 0000000..0aed718
--- /dev/null
+++ b/apps/www/app/playground/page.tsx
@@ -0,0 +1,565 @@
+"use client";
+
+import React from "react";
+import Card from "@/components/ui/card";
+import { ModeToggle } from "@/components/ui/ModeToggle";
+import {
+ ArrowLeftIcon,
+ ArrowRightIcon,
+ CubeIcon,
+ PersonIcon,
+} from "@radix-ui/react-icons";
+import { Button } from "ruru-ui/components/button";
+import { Spinner } from "ruru-ui/components/spinner";
+import {
+ Avatar,
+ AvatarGroup,
+ AvatarWithBadge,
+} from "ruru-ui/components/avatar";
+import { Badge } from "ruru-ui/components/badge";
+import { Checkbox } from "ruru-ui/components/checkbox";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "ruru-ui/components/tooltip";
+import { Input, SearchInput } from "ruru-ui/components/input";
+import { Textarea } from "ruru-ui/components/textarea";
+import { Tabs, Tab } from "ruru-ui/components/tabs";
+import { Switch } from "ruru-ui/components/switch";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+ SelectSeparator,
+} from "ruru-ui/components/select";
+import AnimationToggle from "@/components/animationToggle";
+
+const Playground = () => {
+ return (
+
+
+
+
+
+ default
+ default
+ default
+
+
+ {/*
+
+ Types
+ */}
+
+ Upload
+
+ Upload
+
+ Upload
+
+ Upload
+
+
+
+ }>Upload
+ } suffix={ }>
+ Upload
+
+ }>Upload
+
+
+
+
+ default
+
+
+ default
+
+
+ default
+
+
+
+
+
+ default
+
+
+ default
+
+
+ default
+
+
+
+ {/*
+
+ Click here
+
+ */}
+
+ {/*
+ setColor(color === "blue" ? "green" : "blue")}
+ >
+ Click me to change color
+
+ */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }>
+ gray
+
+ }>
+ gray
+
+ }>
+ gray
+
+
+ }>
+ gray-subtle
+
+ }>
+ gray-subtle
+
+ }>
+ gray-subtle
+
+
+
+
+ }>
+ blue
+
+ }>
+ blue
+
+ }>
+ blue
+
+
+ }>
+ blue-subtle
+
+ }>
+ blue-subtle
+
+ }>
+ blue-subtle
+
+
+
+
+ }>
+ purple
+
+ }>
+ purple
+
+ }>
+ purple
+
+
+ }>
+ purple-subtle
+
+ }>
+ purple-subtle
+
+ }>
+ purple-subtle
+
+
+
+
+ }>
+ amber
+
+ }>
+ amber
+
+ }>
+ amber
+
+
+ }>
+ amber-subtle
+
+ }>
+ amber-subtle
+
+ }>
+ amber-subtle
+
+
+
+
+ }>
+ red
+
+ }>
+ red
+
+ }>
+ red
+
+
+ }>
+ red-subtle
+
+ }>
+ red-subtle
+
+ }>
+ red-subtle
+
+
+
+
+ }>
+ pink
+
+ }>
+ pink
+
+ }>
+ pink
+
+
+ }>
+ pink-subtle
+
+ }>
+ pink-subtle
+
+ }>
+ pink-subtle
+
+
+
+
+ }>
+ green
+
+ }>
+ green
+
+ }>
+ green
+
+
+ }>
+ green-subtle
+
+ }>
+ green-subtle
+
+ }>
+ green-subtle
+
+
+
+
+ }>
+ teal
+
+ }>
+ teal
+
+ }>
+ teal
+
+
+ }>
+ teal-subtle
+
+ }>
+ teal-subtle
+
+ }>
+ teal-subtle
+
+
+
+
+ }>
+ inverted
+
+ }>
+ inverted
+
+ }>
+ inverted
+
+
+
+
+
+
+
+ sm
+
+
+ md
+
+
+ lg
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* @ruru */}
+ @ruru
+
+
+ author of ruru UI
+
+
+
+
+
+
+
+
+
+
+ } placeholder="Enter your name" />
+ } placeholder="Enter your name" />
+ }
+ suffix={ }
+ placeholder="Enter your name"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Apple
+ Orange
+ Mango
+
+
+
+
+
+ Apple
+ Orange
+ Mango
+
+
+
+
+
+ Apple
+ Orange
+ Mango
+
+
+
+
+
+
+
+
+ toggle Mode
+
+
+
+
+
+
+
+
+
+
+ Fruits
+
+ Apple
+ Banana
+ Blueberry
+ Grapes
+ Pineapple
+
+
+
+
+
+
+
+
+
+
+ Fruits
+
+ Apple
+ Banana
+ Blueberry
+ Grapes
+ Pineapple
+
+
+
+
+
+
+
+
+
+
+ Fruits
+
+ Apple
+ Banana
+ Blueberry
+ Grapes
+ Pineapple
+
+
+
+
+
+
+
+ );
+};
+
+export default Playground;
diff --git a/apps/www/app/source.ts b/apps/www/app/source.ts
index 76a8855..ef7cbd1 100644
--- a/apps/www/app/source.ts
+++ b/apps/www/app/source.ts
@@ -1,7 +1,6 @@
import { map } from "@/.map";
import { createMDXSource, defaultSchemas } from "fumadocs-mdx";
import { BuildPageTreeOptions, loader } from "fumadocs-core/source";
-import { PageTree } from "fumadocs-core/server";
import { z } from "zod";
// @ts-ignore
diff --git a/apps/www/components/SwitchEvent.tsx b/apps/www/components/SwitchEvent.tsx
new file mode 100644
index 0000000..4b38674
--- /dev/null
+++ b/apps/www/components/SwitchEvent.tsx
@@ -0,0 +1,19 @@
+"use client";
+
+import React, { useState } from "react";
+import { Switch } from "ruru-ui/components/switch";
+
+const SwiychEvent = (): React.ReactNode => {
+ const [value, setValue] = useState(false);
+ return (
+
+
+ setValue(e)} id="toggle-animation" />
+ toggle value
+
+
{String(value)}
+
+ );
+};
+
+export default SwiychEvent;
diff --git a/apps/www/components/animationToggle.tsx b/apps/www/components/animationToggle.tsx
index ef251f3..6aa62bd 100644
--- a/apps/www/components/animationToggle.tsx
+++ b/apps/www/components/animationToggle.tsx
@@ -8,7 +8,11 @@ const AnimationToggle = () => {
const { setAnimation } = useRuru();
return (
- setAnimation(e)} id="toggle-animation" />
+ setAnimation(e)}
+ id="toggle-animation"
+ />
toggle animation
);
diff --git a/apps/www/components/ui/ModeToggle.tsx b/apps/www/components/ui/ModeToggle.tsx
new file mode 100644
index 0000000..87525c2
--- /dev/null
+++ b/apps/www/components/ui/ModeToggle.tsx
@@ -0,0 +1,18 @@
+"use client";
+
+import * as React from "react";
+import { useTheme } from "ruru-ui/theme";
+
+import { Button } from "ruru-ui/components/button";
+
+export function ModeToggle() {
+ const { setTheme } = useTheme();
+
+ return (
+
+ setTheme("light")}>Light
+ setTheme("dark")}>Dark
+ setTheme("system")}>system
+
+ );
+}
diff --git a/apps/www/components/ui/card.tsx b/apps/www/components/ui/card.tsx
new file mode 100644
index 0000000..29fda6e
--- /dev/null
+++ b/apps/www/components/ui/card.tsx
@@ -0,0 +1,21 @@
+import { cn } from "ruru-ui/utils";
+import React from "react";
+
+export default function Card({
+ children,
+ className,
+}: Readonly<{
+ children: React.ReactNode;
+ className?: string;
+}>) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/www/content/docs/animation.mdx b/apps/www/content/docs/animation.mdx
new file mode 100644
index 0000000..2755207
--- /dev/null
+++ b/apps/www/content/docs/animation.mdx
@@ -0,0 +1,5 @@
+---
+title: Animation
+description: The Animation component is used to animate elements.
+---
+
diff --git a/apps/www/content/docs/cli.mdx b/apps/www/content/docs/cli.mdx
index db4caca..7d0b979 100644
--- a/apps/www/content/docs/cli.mdx
+++ b/apps/www/content/docs/cli.mdx
@@ -46,7 +46,7 @@ bunx --bun ruru-ui@latest init
◇ Configure the import alias for components: @/components/ui
◇ Configure the import alias for utils: @/lib/utils
◇ Would you like to use RSC ? Yes
-◇ Write configuration to components.json. Proceed? Yes
+◇ Write configuration to ruru.json. Proceed? Yes
```
diff --git a/apps/www/content/docs/components/avatar.mdx b/apps/www/content/docs/components/avatar.mdx
index 7a0e593..903a52f 100644
--- a/apps/www/content/docs/components/avatar.mdx
+++ b/apps/www/content/docs/components/avatar.mdx
@@ -71,30 +71,31 @@ The `Avatar` component is used to represent a user or entity.
- ```tsx
- import { Avatar } from "ruru-ui/components/avatar";
+```tsx
+// [!code word:Avatar]
+import { Avatar } from "ruru-ui/components/avatar";
- export function AvatarDemo() {
- return (
-
- )
- }
- ```
+export function Demo() {
+ return (
+
+ )
+}
+```
@@ -103,17 +104,18 @@ The `Avatar` component is used to represent a user or entity.
| Prop | Description | Type | Default |
| --------------- | --------------------------------------------------------------------- | ------ | ------- |
-| **src** | The URL of the image to be displayed. | string | - |
-| **placeholder** | The placeholder text to be displayed when the image is not available. | string | - |
+| **src** | The URL of the image to be displayed. | `string` | - |
+| **placeholder** | The placeholder text to be displayed when the image is not available. | `string` | - |
| **size** | The size of the avatar. | number | 30 |
-| **className** | The class name to be applied to the avatar. | string | - |
+| **className** | The class name to be applied to the avatar. | `string` | - |
## AvatarGroup
The `AvatarGroup` component is used to display multiple avatars in a group.
-
+
+
-
- ```tsx
- import { AvatarGroup } from "ruru-ui/components/avatar";
+
+```tsx
+// [!code word:AvatarGroup]
+// [!code word:limit]
+import { AvatarGroup } from "ruru-ui/components/avatar";
- export function AvatarGroupDemo() {
- return (
-
- )
- }
- ```
+export function Demo() {
+ return (
+
+ )
+}
+```
@@ -202,10 +232,10 @@ The `AvatarGroup` component is used to display multiple avatars in a group.
| Prop | Description | Type | Default |
| ------------- | ---------------------------------------------------------------------------- | --------------------------------------- | ------- |
-| **members** | An array of objects containing the `src` and `alt` properties of the avatar. | Array\<\{ src: string; alt: string \}\> | - |
-| **limit** | The maximum number of avatars to be displayed. | number | - |
-| **size** | The size of the avatars. | number | 30 |
-| **className** | The class name to be applied to the avatar group. | string | - |
+| **members** | An array of objects containing the `src` and `alt` properties of the avatar. | `Array<{ src: string; alt: string }>` | - |
+| **limit** | The maximum number of avatars to be displayed. | `number` | - |
+| **size** | The size of the avatars. | `number` | 30 |
+| **className** | The class name to be applied to the avatar group. | `string` | - |
## AvatarWithBadge
@@ -253,30 +283,32 @@ The `AvatarWithBadge` component is used to display an avatar with a badge.
/>
- ```tsx
- import { AvatarWithBadge } from "ruru-ui/components/avatar";
+```tsx
+// [!code word:AvatarWithBadge]
+// [!code word:badgeSrc]
+import { AvatarWithBadge } from "ruru-ui/components/avatar";
- export function AvatarWithBadgeDemo() {
- return (
-
- )
- }
- ```
+export function Demo() {
+ return (
+
+ )
+}
+```
@@ -285,8 +317,8 @@ The `AvatarWithBadge` component is used to display an avatar with a badge.
| Prop | Description | Type | Default |
| --------------- | --------------------------------------------------------------------- | ------ | ------- |
-| **src** | The URL of the image to be displayed. | string | - |
-| **badgeSrc** | The URL of the badge image to be displayed. | string | - |
-| **size** | The size of the avatar. | number | 30 |
-| **className** | The class name to be applied to the avatar. | string | - |
-| **placeholder** | The placeholder text to be displayed when the image is not available. | string | - |
+| **src** | The URL of the image to be displayed. | `string` | - |
+| **badgeSrc** | The URL of the badge image to be displayed. | `string` | - |
+| **size** | The size of the avatar. | `number` | 30 |
+| **className** | The class name to be applied to the avatar. | `string` | - |
+| **placeholder** | The placeholder text to be displayed when the image is not available. | `string` | - |
diff --git a/apps/www/content/docs/components/badge.mdx b/apps/www/content/docs/components/badge.mdx
index b45f31b..cc4bb34 100644
--- a/apps/www/content/docs/components/badge.mdx
+++ b/apps/www/content/docs/components/badge.mdx
@@ -53,15 +53,16 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
green-subtle
- ```tsx
- import { Badge } from "ruru-ui/components/badge";
-
- export function BadgeDemo() {
- return (
- green-subtle
- )
- }
- ```
+```tsx
+// [!code word:Badge]
+import { Badge } from "ruru-ui/components/badge";
+
+export function Demo() {
+ return (
+ green-subtle
+ )
+}
+```
@@ -94,33 +95,34 @@ The variant of the badge (e.g., "gray", "red", etc...).
inverted
- ```tsx
- import { Badge } from "ruru-ui/components/badge";
-
- export function BadgeDemo() {
- return (
-
- gray
- gray-subtle
- blue
- blue-subtle
- purple
- purple-subtle
- amber
- amber-subtle
- red
- red-subtle
- pink
- pink-subtle
- green
- Green-Subtle
- teal
- teal-subtle
- inverted
-
- )
- }
- ```
+```tsx
+// [!code word:variant]
+import { Badge } from "ruru-ui/components/badge";
+
+export function BadgeDemo() {
+ return (
+
+ gray
+ gray-subtle
+ blue
+ blue-subtle
+ purple
+ purple-subtle
+ amber
+ amber-subtle
+ red
+ red-subtle
+ pink
+ pink-subtle
+ green
+ Green-Subtle
+ teal
+ teal-subtle
+ inverted
+
+ )
+}
+```
@@ -130,31 +132,32 @@ The variant of the badge (e.g., "gray", "red", etc...).
The size of the badge (e.g., "sm", "md", "lg").
-
+
gray
gray
gray
- ```tsx
- import { Badge } from "ruru-ui/components/badge";
-
- export function BadgeDemo() {
- return (
-
-
- sm
-
-
- md
-
-
- lg
-
-
- )
- }
- ```
+```tsx
+// [!code word:size]
+import { Badge } from "ruru-ui/components/badge";
+
+export function BadgeDemo() {
+ return (
+
+
+ sm
+
+
+ md
+
+
+ lg
+
+
+ )
+}
+```
@@ -164,31 +167,32 @@ The size of the badge (e.g., "sm", "md", "lg").
An optional icon to display before the badge content.
-
+
} >gray
} >gray
} >gray
- ```tsx
- import { Badge } from "ruru-ui/components/badge";
-
- export function BadgeDemo() {
- return (
-
-
- sm
-
-
- md
-
-
- lg
-
-
- )
- }
- ```
+```tsx
+// [!code word:icon]
+import { Badge } from "ruru-ui/components/badge";
+
+export function BadgeDemo() {
+ return (
+
+ } >
+ gray
+
+ } >
+ gray
+
+ } >
+ gray
+
+
+ )
+}
+```
diff --git a/apps/www/content/docs/components/button.mdx b/apps/www/content/docs/components/button.mdx
index 6546211..c56d79b 100644
--- a/apps/www/content/docs/components/button.mdx
+++ b/apps/www/content/docs/components/button.mdx
@@ -57,18 +57,21 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- Click me
+ Click me!
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
- Button
- )
- }
- ```
+```tsx
+// [!code word:Button]
+import { Button } from "ruru-ui/components/button";
+
+export function Demo() {
+ return (
+
+ Click me!
+
+ )
+}
+```
@@ -109,7 +112,7 @@ Alternatively, you can set the `asChild` parameter and nest the link component.
The `Button` component is like a chameleon, it can change its appearance to match different moods and situations. It's like having a button for every occasion!
-
+
Default
Secondary
Tertiary
@@ -117,21 +120,22 @@ The `Button` component is like a chameleon, it can change its appearance to matc
Warning
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
-
- Default
- Secondary
- Tertiary
- Error
- Warning
-
- )
- }
- ```
+```tsx
+// [!code word:variant]
+import { Button } from "ruru-ui/components/button";
+
+export function ButtonDemo() {
+ return (
+
+ Default
+ Secondary
+ Tertiary
+ Error
+ Warning
+
+ )
+}
+```
@@ -153,25 +157,26 @@ So go ahead, unleash your creativity and choose the perfect variant for your but
The `Button` component comes in a variety of sizes to suit your needs. Whether you want a small button for a subtle touch or a large button that commands attention, we've got you covered!
-
+
Small
Default
Large
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
-
- Small
- Default
- Large
-
- )
- }
- ```
+```tsx
+// [!code word:size]
+import { Button } from "ruru-ui/components/button";
+
+export function ButtonDemo() {
+ return (
+
+ Small
+ Default
+ Large
+
+ )
+}
+```
@@ -192,7 +197,7 @@ So go ahead, choose the perfect size for your button and make a statement that c
The `Button` component allows you to add a prefix and suffix to your button. This is perfect for when you need to add a little extra flair or information to your button.
-
+
}>Upload
} suffix={ }>
Upload
@@ -200,22 +205,24 @@ The `Button` component allows you to add a prefix and suffix to your button. Thi
}>Upload
- ```tsx
- import { Button } from "ruru-ui/components/button";
- import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
-
- export function ButtonDemo() {
- return (
-
-
}>Upload
-
} suffix={
}>
- Upload
-
-
}>Upload
-
- )
- }
- ```
+```tsx
+// [!code word:prefix]
+// [!code word:suffix]
+import { Button } from "ruru-ui/components/button";
+import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
+
+export function ButtonDemo() {
+ return (
+
+
}>Upload
+
} suffix={
}>
+ Upload
+
+
}>Upload
+
+ )
+}
+```
@@ -234,7 +241,7 @@ So go ahead, add a prefix and suffix to your button and make it stand out from t
The `Button` component allows you to show a loading spinner when the button is in a loading state. This is perfect for when you need to indicate to the user that something is happening in the background.
-
+
default
@@ -246,25 +253,26 @@ The `Button` component allows you to show a loading spinner when the button is i
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
-
-
- default
-
-
- default
-
-
- default
-
-
- )
- }
- ```
+```tsx
+// [!code word:loading]
+import { Button } from "ruru-ui/components/button";
+
+export function ButtonDemo() {
+ return (
+
+
+ default
+
+
+ default
+
+
+ default
+
+
+ )
+}
+```
@@ -282,7 +290,7 @@ So go ahead, show a loading spinner on your button and let your users know that
The `Button` component allows you to disable the button by passing the `disabled` prop. This is perfect for when you need to prevent the user from interacting with the button.
-
+
Default
Secondary
Tertiary
@@ -290,21 +298,22 @@ The `Button` component allows you to disable the button by passing the `disabled
Warning
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
-
- Default
- Secondary
- Tertiary
- Error
- Warning
-
- )
- }
- ```
+```tsx
+// [!code word:disabled]
+import { Button } from "ruru-ui/components/button";
+
+export function ButtonDemo() {
+ return (
+
+ Default
+ Secondary
+ Tertiary
+ Error
+ Warning
+
+ )
+}
+```
@@ -314,25 +323,27 @@ The `Button` component allows you to disable the button by passing the `disabled
#### Icon
-
+
- ```tsx
- import { Button } from "ruru-ui/components/button";
-
- export function ButtonDemo() {
- return (
-
-
-
-
-
- )
- }
- ```
+```tsx
+// [!code word:size]
+// [!code word:"icon"]
+import { Button } from "ruru-ui/components/button";
+
+export function ButtonDemo() {
+ return (
+
+
+
+
+
+ )
+}
+```
diff --git a/apps/www/content/docs/components/checkbox.mdx b/apps/www/content/docs/components/checkbox.mdx
index c9b333e..fbec83f 100644
--- a/apps/www/content/docs/components/checkbox.mdx
+++ b/apps/www/content/docs/components/checkbox.mdx
@@ -54,23 +54,19 @@ To use the Checkbox component, import it from `ruru-ui` and include it in your c
This example shows a basic checkbox.
-
+
- ```tsx
- import { Checkbox } from "ruru-ui/components/checkbox";
-
- export function CheckboxDemo() {
- return (
-
-
-
- )
- }
- ```
-
+```tsx
+// [!code word:Checkbox]
+import { Checkbox } from "ruru-ui/components/checkbox";
+export function Demo() {
+ return
+}
+```
+
## Examples
@@ -82,21 +78,18 @@ This example shows a basic checkbox.
This example demonstrates a checkbox in an indeterminate state.
-
+
- ```tsx
- import { Checkbox } from "ruru-ui/components/checkbox";
-
- export function CheckboxDemo() {
- return (
-
-
-
- )
- }
- ```
+```tsx
+// [!code word:indeterminate]
+import { Checkbox } from "ruru-ui/components/checkbox";
+
+export function CheckboxDemo() {
+ return
+}
+```
@@ -106,21 +99,18 @@ This example demonstrates a checkbox in an indeterminate state.
This example shows a checkbox in a disabled state, making it unclickable.
-
+
- ```tsx
- import { Checkbox } from "ruru-ui/components/checkbox";
-
- export function CheckboxDemo() {
- return (
-
-
-
- )
- }
- ```
+```tsx
+// [!code word:disabled]
+import { Checkbox } from "ruru-ui/components/checkbox";
+
+export function CheckboxDemo() {
+ return
+}
+```
@@ -130,21 +120,18 @@ This example shows a checkbox in a disabled state, making it unclickable.
This example shows a checkbox that is checked by default.
-
+
- ```tsx
- import { Checkbox } from "ruru-ui/components/checkbox";
-
- export function CheckboxDemo() {
- return (
-
-
-
- )
- }
- ```
+```tsx
+// [!code word:checked]
+import { Checkbox } from "ruru-ui/components/checkbox";
+
+export function CheckboxDemo() {
+ return
+}
+```
@@ -166,25 +153,26 @@ This example shows a checkbox with an associated label.
- ```tsx
- import { Checkbox } from "ruru-ui/components/checkbox";
-
- export function CheckboxDemo() {
- return (
-
-
-
-
- Accept terms and conditions
-
-
-
- )
- }
- ```
+```tsx
+// [!code word:policy]
+import { Checkbox } from "ruru-ui/components/checkbox"; // [!code highlight]
+
+export function CheckboxDemo() {
+ return (
+
+
// [!code highlight]
+ // [!code highlight]
+ // [!code highlight]
+ Accept terms and conditions // [!code highlight]
+ // [!code highlight]
+
// [!code highlight]
+
+ )
+}
+```
diff --git a/apps/www/content/docs/components/input.mdx b/apps/www/content/docs/components/input.mdx
index 582d4e1..1d073c2 100644
--- a/apps/www/content/docs/components/input.mdx
+++ b/apps/www/content/docs/components/input.mdx
@@ -53,36 +53,19 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function InputDemo() {
- return ;
- }
- ```
-
-
-
-
-## Variants
-
-### Default Input
-
-
-
-
-
-
```tsx
+// [!code word:Input]
import { Input } from "ruru-ui/components/input";
-export function DefaultInputDemo() {
+export function Demo() {
return ;
}
```
-
+
+## Variants
+
### Input with Label
@@ -90,15 +73,15 @@ export function DefaultInputDemo() {
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function LabelInputDemo() {
- return ;
- }
+```tsx
+// [!code word:label]
+import { Input } from "ruru-ui/components/input";
- ```
+export function InputDemo() {
+ return ;
+}
+```
@@ -110,19 +93,20 @@ export function DefaultInputDemo() {
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function PrefixSuffixInputDemo() {
- return (
-
-
-
-
- );
- }
- ```
+```tsx
+// [!code word:prefix]
+// [!code word:suffix]
+import { Input } from "ruru-ui/components/input";
+export function InputDemo() {
+ return (
+
+
+
+
+ );
+}
+```
@@ -133,15 +117,15 @@ export function DefaultInputDemo() {
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function ErrorInputDemo() {
- return ;
- }
+```tsx
+// [!code word:error]
+import { Input } from "ruru-ui/components/input";
- ```
+export function InputDemo() {
+ return ;
+}
+```
@@ -152,17 +136,36 @@ export function DefaultInputDemo() {
- ```tsx
- import { SearchInput } from "ruru-ui/components/input";
+```tsx
+// [!code word:SearchInput]
+import { SearchInput } from "ruru-ui/components/input";
- export function SearchInputDemo() {
- return ;
- }
- ```
+export function Demo() {
+ return ;
+}
+```
+
+
+
+### Disabled Input
+
+
+
+
+
+
+```tsx
+// [!code word:disabled]
+import { Input } from "ruru-ui/components/input";
+export function InputExample() {
+ return ;
+}
+```
+
## Props
### Input
@@ -181,125 +184,6 @@ export function DefaultInputDemo() {
### SearchInput
-| Name | Type | Default | Description |
-| --------------------- | --------- | ------- | ------------------------------------ |
-| `enablePrefixStyling` | `boolean` | `false` | Flag to apply styling to the prefix. |
-
-## Examples
-
-### Default Input Example
-
-
-
-
-
-
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function DefaultInputExample() {
- return ;
- }
-
- ```
-
-
-
-
-### Input with Label Example
-
-
-
-
-
-
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function LabelInputExample() {
- return ;
- }
- ```
-
-
-
-
-### Input with Prefix and Suffix Example
-
-
-
-
-
-
-
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function PrefixSuffixInputExample() {
- return (
-
-
-
-
- );
- }
-
- ```
-
-
-
-
-### Input with Error Message Example
-
-
-
-
-
-
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function ErrorInputExample() {
- return ;
- }
- ```
-
-
-
-
-### Disabled Input Example
-
-
-
-
-
-
- ```tsx
- import { Input } from "ruru-ui/components/input";
-
- export function DisabledInputExample() {
- return ;
- }
- ```
-
-
-
-
-### Search Input Example
-
-
-
-
-
-
- ```tsx
- import { SearchInput } from "ruru-ui/components/input";
-
- export function SearchInputExample() {
- return ;
- }
-
- ```
-
-
-
+| Name | Type | Default | Description |
+| ----------------------- | ----------- | ------- | ------------------------------------ |
+| **enablePrefixStyling** | **boolean** | `false` | Flag to apply styling to the prefix. |
diff --git a/apps/www/content/docs/components/spinner.mdx b/apps/www/content/docs/components/spinner.mdx
index 723d7bf..6bf469c 100644
--- a/apps/www/content/docs/components/spinner.mdx
+++ b/apps/www/content/docs/components/spinner.mdx
@@ -58,17 +58,17 @@ The Spinner component is used to indicate that a page is loading or that an acti
- ```tsx
- import { Spinner } from "ruru-ui/components/spinner";
-
- export function SpinnerDemo() {
- return (
-
- )
- }
- ```
-
+```tsx
+// [!code word:Spinner]
+import { Spinner } from "ruru-ui/components/spinner";
+export function Demo() {
+ return (
+
+ )
+}
+```
+
### Custom size
@@ -80,22 +80,23 @@ You can customize the size of the spinner by passing the `size` prop.
- ```tsx
- import { Spinner } from "ruru-ui/components/spinner";
-
- export function SpinnerDemo() {
- return (
-
- )
- }
- ```
-
+```tsx
+// [!code word:size]
+// [!code word:30]
+import { Spinner } from "ruru-ui/components/spinner";
+export function SpinnerDemo() {
+ return (
+
+ )
+}
+```
+
## Props
-| Name | Type | Default | Description |
-| --------- | ------ | ------- | ------------------------------ |
-| size | number | 20 | The size of the spinner. |
-| className | string | - | The class name of the spinner. |
+| Name | Type | Default | Description |
+| ------------- | ---------- | ------- | ------------------------------ |
+| **size** | **number** | `20` | The size of the spinner. |
+| **className** | **string** | `-` | The class name of the spinner. |
diff --git a/apps/www/content/docs/components/switch.mdx b/apps/www/content/docs/components/switch.mdx
index fb25d76..5b96e73 100644
--- a/apps/www/content/docs/components/switch.mdx
+++ b/apps/www/content/docs/components/switch.mdx
@@ -7,6 +7,7 @@ preview: switch
import { Tabs, Tab } from "fumadocs-ui/components/tabs";
import { Switch } from "ruru-ui/components/switch";
import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
+import SwiychEvent from "../../../components/SwitchEvent.tsx";
## Installation
@@ -53,37 +54,19 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Switch } from "your-ui-library/components/switch";
-
- export function SwitchDemo() {
- return ;
- }
- ```
-
+```tsx
+// [!code word:Switch]
+import { Switch } from "your-ui-library/components/switch";
+
+export function Demo() {
+ return ;
+}
+```
## Variants
-### Default Switch
-
-
-
-
-
-
- ```tsx
- import { Switch } from "your-ui-library/components/switch";
-
- export function DefaultSwitchDemo() {
- return ;
- }
- ```
-
-
-
-
### Disabled Switch
@@ -91,13 +74,14 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Switch } from "your-ui-library/components/switch";
-
- export function DisabledSwitchDemo() {
- return ;
- }
- ```
+```tsx
+// [!code word:disabled]
+import { Switch } from "your-ui-library/components/switch";
+
+export function SwitchDemo() {
+ return ;
+}
+```
@@ -106,8 +90,8 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
| Name | Type | Default | Description |
| ------------- | ------- | ------- | -------------------------------------- |
-| **className** | string | `"" ` | Additional class names for the switch. |
-| **disabled** | boolean | `false` | Flag to disable the switch. |
+| **className** | **string** | `"" ` | Additional class names for the switch. |
+| **disabled** | **boolean** | `false` | Flag to disable the switch. |
## Examples
@@ -121,19 +105,22 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Switch } from "your-ui-library/components/switch";
-
- export function DefaultSwitchExample() {
- return (
-
-
- toggle Mode
-
- );
- }
-
- ```
+```tsx
+// [!code word:airplane-mode]
+// [!code word:id]
+// [!code word:htmlFor]
+import { Switch } from "your-ui-library/components/switch";
+
+export function Demo() {
+ return (
+
+
+ toggle Mode
+
+ );
+}
+
+```
@@ -145,17 +132,50 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Switch } from "your-ui-library/components/switch";
-
- export function DefaultCheckedSwitchDemo() {
- return ;
- }
- ```
+```tsx
+// [!code word:defaultChecked]
+import { Switch } from "your-ui-library/components/switch";
+
+export function SwitchDemo() {
+ return ;
+}
+```
----
+### onCheckedChange function
+
+
+
+
+
+
+```tsx
+// [!code word:onCheckedChange]
+"use client";
+
+import React, { useState } from "react";
+import { Switch } from "ruru-ui/components/switch";
+
+const SwiychDemo = (): React.ReactNode => {
+ const [value, setValue] = useState(false); // [!code highlight]
+ return (
+
+
+ setValue(e)} // [!code highlight]
+ id="toggle-animation"
+ />
+ toggle value
+
+
{String(value)}
+
+ );
+};
+
+export default SwiychDemo;
+```
+
+
-This document provides an overview of the Switch component, including usage examples, variants, props, and example implementations.
diff --git a/apps/www/content/docs/components/tabs.mdx b/apps/www/content/docs/components/tabs.mdx
index 0289bff..7749d6e 100644
--- a/apps/www/content/docs/components/tabs.mdx
+++ b/apps/www/content/docs/components/tabs.mdx
@@ -58,10 +58,11 @@ import { Tabs, Tab } from "fumadocs-ui/components/tabs";
```tsx
+// [!code word:Tabs]
import { Tabs, Tab } from "ruru-ui/components/tabs";
-export function TabsDemo() {
- return (
+export function Demo() {
+ return (
Apple
@@ -79,39 +80,6 @@ export function TabsDemo() {
## Variants
-### Default Tabs
-
-
-
-
-
- Apple
- Orange
- Mango
-
-
-
-
-```tsx
-import { Tabs, Tab } from "ruru-ui/components/tabs";
-
-export function DefaultTabsDemo() {
- return (
-
-
- Apple
- Orange
- Mango
-
-
- );
-}
-
-```
-
-
-
-
### Disabled Tabs
@@ -126,9 +94,10 @@ export function DefaultTabsDemo() {
```tsx
+// [!code word:disabled]
import { Tabs, Tab } from "ruru-ui/components/tabs";
-export function DisabledTabsDemo() {
+export function TabsDemo() {
return (
@@ -158,9 +127,11 @@ export function DisabledTabsDemo() {
```tsx
+// [!code word:defaultIndex]
+// [!code word:2]
import { Tabs, Tab } from "ruru-ui/components/tabs";
-export function DefaultIndexTabsDemo() {
+export function TabsDemo() {
return (
@@ -180,17 +151,17 @@ export function DefaultIndexTabsDemo() {
### Tabs
-| Name | Type | Default | Description |
-| ---------------- | -------- | ----------- | --------------------------------------- |
-| **groupId** | string | `undefined` | Identifier for sharing value of tabs |
-| **persist** | boolean | `false` | Enable persistent state |
-| **defaultIndex** | number | `0` | Default index of the selected tab |
-| **disabled** | boolean | `false` | Disable all tabs |
-| **items** | string[] | `[]` | Array of items to be used as tab labels |
+| Name | Type | Default | Description |
+| ---------------- | ------------ | ----------- | --------------------------------------- |
+| **groupId** | **string** | `undefined` | Identifier for sharing value of tabs |
+| **persist** | **boolean** | `false` | Enable persistent state |
+| **defaultIndex** | **number** | `0` | Default index of the selected tab |
+| **disabled** | **boolean** | `false` | Disable all tabs |
+| **items** | **string[]** | `[]` | Array of items to be used as tab labels |
### Tab
-| Name | Type | Default | Description |
-| ------------- | ------ | ------- | ------------------------------------------------------- |
-| **value** | string | | Value of the tab, used for identifying the selected tab |
-| **className** | string | `""` | Additional class names for the container |
+| Name | Type | Default | Description |
+| ------------- | ---------- | ------- | ------------------------------------------------------- |
+| **value** | **string** | | Value of the tab, used for identifying the selected tab |
+| **className** | **string** | `""` | Additional class names for the container |
diff --git a/apps/www/content/docs/components/textarea.mdx b/apps/www/content/docs/components/textarea.mdx
index 81daa43..44cf482 100644
--- a/apps/www/content/docs/components/textarea.mdx
+++ b/apps/www/content/docs/components/textarea.mdx
@@ -55,38 +55,26 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
/>
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
+```tsx
+// [!code word:Textarea]
+import { Textarea } from "ruru-ui/components/textarea";
- export function TextareaDemo() {
- return ;
- }
+export function Demo() {
+ return (
+
+ );
+}
- ```
+```
## Variants
-### Default Textarea
-
-
-
-
-
-
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function DefaultTextareaDemo() {
- return ;
- }
- ```
-
-
-
-
### Textarea with Label
@@ -94,15 +82,21 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function LabelTextareaDemo() {
- return ;
- }
+```tsx
+// [!code word:label]
+import { Textarea } from "ruru-ui/components/textarea";
- ```
+export function LabelTextareaDemo() {
+ return (
+
+ );
+}
+```
@@ -113,85 +107,51 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function ErrorTextareaDemo() {
- return ;
- }
- ```
-
-
-
-
-## Props
-
-### Textarea
-
-| Name | Type | Default | Description |
-| ------------- | ---------------------------------------------------------------------------------------- | ----------- | --------------------------------------------- |
-| **className** | string | `""` | Additional class names for the container. |
-| **label** | string | `undefined` | Label for the textarea element. |
-| **error** | string | `""` | Error message to display below the textarea. |
-| **...props** | **Omit\
, "children" "prefix" "suffix">** | `-` | Any other props to be passed to the textarea. |
-
-## Examples
-
-### Default Textarea Example
+```tsx
+// [!code word:error]
+import { Textarea } from "ruru-ui/components/textarea";
-
-
+export function TextareaDemo() {
+ return (
-
-
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function DefaultTextareaExample() {
- return ;
- }
- ```
-
+ error="Invalid input"
+ placeholder="Enter text"
+ style={{ minHeight: 100, width: 500 }}
+ />
+ );
+}
+```
-### Textarea with Label Example
+### Disabled Input
-
+
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function LabelTextareaExample() {
- return ;
- }
-
- ```
+```tsx
+// [!code word:disabled]
+import { Textarea } from "ruru-ui/components/textarea";
+export function TextareaExample() {
+ return (
+
+ );
+}
+```
-### Textarea with Error Message Example
-
-
-
-
-
-
- ```tsx
- import { Textarea } from "ruru-ui/components/textarea";
-
- export function ErrorTextareaExample() {
- return ;
- }
-
- ```
+## Props
-
-
+| Name | Type | Default | Description |
+| ------------- | ---------------------------------------------------------------------------------------- | ----------- | --------------------------------------------- |
+| **className** | **string** | `""` | Additional class names for the container. |
+| **label** | **string** | `undefined` | Label for the textarea element. |
+| **error** | **string** | `""` | Error message to display below the textarea. |
diff --git a/apps/www/content/docs/installation.mdx b/apps/www/content/docs/installation.mdx
index 000a2cb..26d3f68 100644
--- a/apps/www/content/docs/installation.mdx
+++ b/apps/www/content/docs/installation.mdx
@@ -45,7 +45,7 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
### Configure components
- You will need to answer a few questions to configure the `components.json` file:
+ You will need to answer a few questions to configure the `ruru.json` file:
```txt
// [!code word:Yes]
@@ -64,7 +64,7 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
◇ Configure the import alias for components: @/components/ui
◇ Configure the import alias for utils: @/lib/utils
◇ Would you like to use RSC ? Yes
-◇ Write configuration to components.json. Proceed? Yes
+◇ Write configuration to ruru.json. Proceed? Yes
```
diff --git a/apps/www/content/docs/provider.mdx b/apps/www/content/docs/provider.mdx
new file mode 100644
index 0000000..bd7f29a
--- /dev/null
+++ b/apps/www/content/docs/provider.mdx
@@ -0,0 +1,4 @@
+---
+title: Provider
+description: The Provider component is used to provide a context to all components in the tree.
+---
diff --git a/apps/www/content/docs/ruru-json.mdx b/apps/www/content/docs/ruru-json.mdx
new file mode 100644
index 0000000..fe685c9
--- /dev/null
+++ b/apps/www/content/docs/ruru-json.mdx
@@ -0,0 +1,5 @@
+---
+title: ruru.json
+description: The `ruru.json` file is used to configure the project.
+---
+
diff --git a/apps/www/mdx-components.tsx b/apps/www/mdx-components.tsx
index 31deb5d..feac75d 100644
--- a/apps/www/mdx-components.tsx
+++ b/apps/www/mdx-components.tsx
@@ -1,11 +1,15 @@
import type { MDXComponents } from "mdx/types";
import defaultComponents from "fumadocs-ui/mdx";
import { Wrapper } from "@/components/preview/wrapper";
+import { Popup, PopupContent, PopupTrigger } from "fumadocs-ui/twoslash/popup";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...defaultComponents,
...components,
+ Popup,
+ PopupContent,
+ PopupTrigger,
Wrapper,
};
}
diff --git a/apps/www/package.json b/apps/www/package.json
index 221bd57..83f94c1 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -12,6 +12,8 @@
"dependencies": {
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-scroll-area": "^1.1.0",
+ "@radix-ui/react-tabs": "^1.1.0",
+ "@vercel/analytics": "^1.3.1",
"fs": "0.0.1-security",
"fumadocs-core": "12.4.2",
"fumadocs-docgen": "^1.1.0",
diff --git a/apps/www/public/registry/components/avatar.json b/apps/www/public/registry/components/avatar.json
index 0964f07..2db9c52 100644
--- a/apps/www/public/registry/components/avatar.json
+++ b/apps/www/public/registry/components/avatar.json
@@ -3,7 +3,7 @@
"files": [
{
"name": "avatar.tsx",
- "content": "import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\n\n\ntype AvatarProps = Omit<\n React.ComponentPropsWithoutRef<\"img\">,\n \"src\" | \"alt\"\n> & {\n \n className?: string;\n \n size?: number;\n \n placeholder?: string;\n \n src: string;\n};\n\n\nconst Avatar = React.forwardRef(\n ({ className, size = 30, src, placeholder, ...props }, ref) => {\n return (\n \n );\n },\n);\nAvatar.displayName = \"Avatar\";\n\ntype AvatarGroupProps = Omit<\n React.ComponentPropsWithoutRef<\"div\">,\n \"children\"\n> & {\n \n className?: string;\n \n members: { src: string; alt: string }[];\n \n size?: number;\n \n limit?: number;\n aClassName?: string;\n lnClassName?: string;\n};\n\nconst AvatarGroup = React.forwardRef(\n (\n { className, aClassName, lnClassName, size = 30, members, limit, ...props },\n ref,\n ) => {\n const displayedMembers =\n limit && members.length > limit ? members.slice(0, limit - 1) : members;\n const extraMembersCount =\n limit && members.length > limit ? members.length - limit + 1 : 0;\n\n return (\n \n {displayedMembers.map((member, index) => (\n
\n ))}\n {extraMembersCount > 0 && (\n
\n +{extraMembersCount}\n
\n )}\n
\n );\n },\n);\nAvatarGroup.displayName = \"AvatarGroup\";\n\ntype AvatarWithBadgeProps = Omit<\n React.ComponentPropsWithoutRef<\"div\">,\n \"children\"\n> & {\n \n className?: string;\n \n size?: number;\n \n src: string;\n \n placeholder?: string;\n \n badgeSrc: string;\n iClassName?: string;\n sClassName?: string;\n};\n\n\nconst AvatarWithBadge = React.forwardRef(\n (\n {\n className,\n sClassName,\n iClassName,\n size = 30,\n src,\n placeholder,\n badgeSrc,\n ...props\n },\n ref,\n ) => {\n return (\n \n
\n
\n
\n );\n },\n);\nAvatarWithBadge.displayName = \"AvatarWithBadge\";\n\nexport { Avatar, AvatarGroup, AvatarWithBadge };\n"
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\n\ntype AvatarProps = Omit<\n React.ComponentPropsWithoutRef<\"img\">,\n \"src\" | \"alt\"\n> & {\n \n className?: string;\n \n size?: number;\n \n placeholder?: string;\n \n src: string;\n};\n\nconst Avatar = React.forwardRef(\n ({ className, size = 30, src, placeholder, ...props }, ref) => {\n return (\n \n );\n },\n);\nAvatar.displayName = \"Avatar\";\n\ntype AvatarGroupProps = Omit<\n React.ComponentPropsWithoutRef<\"div\">,\n \"children\"\n> & {\n \n className?: string;\n \n members: { src: string; alt: string }[];\n \n size?: number;\n \n limit?: number;\n \n aClassName?: string;\n \n lnClassName?: string;\n};\n\nconst AvatarGroup = React.forwardRef(\n (\n { className, aClassName, lnClassName, size = 30, members, limit, ...props },\n ref,\n ) => {\n const displayedMembers =\n limit && members.length > limit ? members.slice(0, limit - 1) : members;\n const extraMembersCount =\n limit && members.length > limit ? members.length - limit + 1 : 0;\n\n return (\n \n {displayedMembers.map((member, index) => (\n
\n ))}\n {extraMembersCount > 0 && (\n
\n +{extraMembersCount}\n
\n )}\n
\n );\n },\n);\nAvatarGroup.displayName = \"AvatarGroup\";\n\ntype AvatarWithBadgeProps = Omit<\n React.ComponentPropsWithoutRef<\"div\">,\n \"children\"\n> & {\n \n className?: string;\n \n size?: number;\n \n src: string;\n \n placeholder?: string;\n \n badgeSrc: string;\n \n iClassName?: string;\n \n sClassName?: string;\n};\n\nconst AvatarWithBadge = React.forwardRef(\n (\n {\n className,\n sClassName,\n iClassName,\n size = 30,\n src,\n placeholder,\n badgeSrc,\n ...props\n },\n ref,\n ) => {\n return (\n \n
\n
\n
\n );\n },\n);\nAvatarWithBadge.displayName = \"AvatarWithBadge\";\n\nexport { Avatar, AvatarGroup, AvatarWithBadge };\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/components/button.json b/apps/www/public/registry/components/button.json
index a57a6a5..6dba07e 100644
--- a/apps/www/public/registry/components/button.json
+++ b/apps/www/public/registry/components/button.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "button.tsx",
- "content": "\"use client\";\r\n\r\nimport { Slot } from \"@radix-ui/react-slot\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport React from \"react\";\r\nimport { cn } from \"@/utils/cn\";\r\nimport { Spinner } from \"./spinner\";\r\nimport { motion } from \"framer-motion\";\r\nimport { useRuru } from \"@/provider\";\r\n\r\nexport const buttonVariants = cva(\r\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\r\n {\r\n variants: {\r\n variant: {\r\n default:\r\n \"bg-primary text-primary-foreground shadow hover:bg-primary/85 hover:shadow-md\",\r\n secondary:\r\n \"border-input border-[1.5px] bg-primary-foreground hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\r\n tertiary: \"text-primary hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\r\n error: \"bg-[#d93036] hover:bg-[#ff6166]\",\r\n warning: \"bg-[#ff990a] text-primary-foreground hover:bg-[#d27504]\",\r\n },\r\n size: {\r\n default: \"h-9 px-4 py-2\",\r\n small: \"h-8 rounded-md px-3 text-xs\",\r\n large: \"h-10 rounded-md px-8\",\r\n icon: \"size-9\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n },\r\n);\r\n\r\nexport interface ButtonProps\r\n extends Omit<\r\n React.ButtonHTMLAttributes,\r\n \"prefix\" | \"suffix\"\r\n >,\r\n VariantProps {\r\n \r\n asChild?: boolean;\r\n \r\n prefix?: React.ReactNode;\r\n \r\n suffix?: React.ReactNode;\r\n \r\n disabled?: boolean;\r\n \r\n loading?: boolean;\r\n}\r\n\r\nexport const Button = React.forwardRef(\r\n (\r\n {\r\n className,\r\n variant = \"default\",\r\n size = \"default\",\r\n asChild = false,\r\n prefix,\r\n suffix,\r\n disabled = false,\r\n loading = false,\r\n ...props\r\n },\r\n ref,\r\n ) => {\r\n const { animation } = useRuru();\r\n\r\n const Comp = asChild ? Slot : \"button\";\r\n\r\n const buttonContent = (\r\n \r\n {loading ? : null}\r\n {prefix ? (\r\n \r\n {prefix}\r\n \r\n ) : null}\r\n {props.children}\r\n {suffix ? (\r\n \r\n {suffix}\r\n \r\n ) : null}\r\n \r\n );\r\n\r\n return (\r\n \r\n {animation ? (\r\n {buttonContent} \r\n ) : (\r\n buttonContent\r\n )}\r\n
\r\n );\r\n },\r\n);\r\n\r\nButton.displayName = \"Button\";\r\n"
+ "content": "\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Spinner } from \"./spinner\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/85 hover:shadow-md\",\n secondary:\n \"border-input border-[1.5px] bg-primary-foreground hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n tertiary: \"text-primary hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n error: \"bg-[#d93036] hover:bg-[#ff6166]\",\n warning: \"bg-[#ff990a] text-primary-foreground hover:bg-[#d27504]\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n small: \"h-8 rounded-md px-3 text-xs\",\n large: \"h-10 rounded-md px-8\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends Omit<\n React.ButtonHTMLAttributes,\n \"prefix\" | \"suffix\"\n >,\n VariantProps {\n \n asChild?: boolean;\n \n prefix?: React.ReactNode;\n \n suffix?: React.ReactNode;\n \n disabled?: boolean;\n \n loading?: boolean;\n}\n\nexport const Button = React.forwardRef(\n (\n {\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n prefix,\n suffix,\n disabled = false,\n loading = false,\n ...props\n },\n ref,\n ) => {\n const { animation } = useRuru();\n\n const Comp = asChild ? Slot : \"button\";\n\n const buttonContent = (\n \n {loading ? : null}\n {prefix ? (\n \n {prefix}\n \n ) : null}\n {props.children}\n {suffix ? (\n \n {suffix}\n \n ) : null}\n \n );\n\n return (\n \n {animation ? (\n {buttonContent} \n ) : (\n buttonContent\n )}\n
\n );\n },\n);\n\nButton.displayName = \"Button\";\n"
}
],
"type": "components:ui",
diff --git a/apps/www/public/registry/components/select.json b/apps/www/public/registry/components/select.json
index fd02b82..8daab8e 100644
--- a/apps/www/public/registry/components/select.json
+++ b/apps/www/public/registry/components/select.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "select.tsx",
- "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n CaretSortIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n} from \"@radix-ui/react-icons\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { cn } from \"@/utils/cn\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nconst Select = SelectPrimitive.Root;\nconst SelectGroup = SelectPrimitive.Group;\nconst SelectValue = SelectPrimitive.Value;\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => {\n const { animation } = useRuru();\n return animation ? (\n \n span]:line-clamp-1\",\n className,\n )}\n {...props}\n >\n {children}\n \n \n \n \n \n ) : (\n span]:line-clamp-1\",\n className,\n )}\n {...props}\n >\n {children}\n \n \n \n \n );\n});\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName;\n\n\nconst AnimatedSelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n \n \n \n \n \n {children}\n \n \n \n \n \n));\nAnimatedSelectContent.displayName = SelectPrimitive.Content.displayName;\n\n\nconst StaticSelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n \n \n \n \n {children}\n \n \n \n \n));\nStaticSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nconst SelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => {\n const { animation } = useRuru();\n return animation ? (\n \n ) : (\n \n );\n});\n\nSelectContent.displayName = \"SelectContent\";\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nconst SelectItem = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => (\n \n \n \n \n \n \n {children} \n \n));\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n"
+ "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n CaretSortIcon,\n CheckIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n} from \"@radix-ui/react-icons\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { cn } from \"@/utils/cn\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nconst Select = SelectPrimitive.Root;\nconst SelectGroup = SelectPrimitive.Group;\nconst SelectValue = SelectPrimitive.Value;\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => {\n const { animation } = useRuru();\n return animation ? (\n \n span]:line-clamp-1\",\n className,\n )}\n {...props}\n >\n {children}\n \n \n \n \n \n ) : (\n span]:line-clamp-1\",\n className,\n )}\n {...props}\n >\n {children}\n \n \n \n \n );\n});\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName;\n\n\nconst AnimatedSelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n \n \n \n \n \n {children}\n \n \n \n \n \n));\nAnimatedSelectContent.displayName = SelectPrimitive.Content.displayName;\n\n\nconst StaticSelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n \n \n \n \n {children}\n \n \n \n \n));\nStaticSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nconst SelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => {\n const { animation } = useRuru();\n return animation ? (\n \n ) : (\n \n );\n});\n\nSelectContent.displayName = \"SelectContent\";\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nconst SelectItem = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => (\n \n \n \n \n \n \n {children} \n \n));\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/components/switch.json b/apps/www/public/registry/components/switch.json
index 24c2838..27dc904 100644
--- a/apps/www/public/registry/components/switch.json
+++ b/apps/www/public/registry/components/switch.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "switch.tsx",
- "content": "\n\nimport * as React from \"react\";\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst Switch = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n"
+ "content": "import * as React from \"react\";\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst Switch = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/components/tabs.json b/apps/www/public/registry/components/tabs.json
index c6c8c8b..795fe9f 100644
--- a/apps/www/public/registry/components/tabs.json
+++ b/apps/www/public/registry/components/tabs.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "tabs.tsx",
- "content": "\"use client\";\n\nimport type {\n TabsContentProps,\n TabsProps as BaseProps,\n} from \"@radix-ui/react-tabs\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport React, {\n useMemo,\n useState,\n useCallback,\n useLayoutEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nconst TabsPrimitiveRoot = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => {\n return (\n \n );\n});\n\nTabsPrimitiveRoot.displayName = \"TabsPrimitiveRoot\";\n\nconst AnimatedTabsList = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => {\n \n const [activeIndex, setActiveIndex] = useState(0);\n const [indicatorProps, setIndicatorProps] = useState({\n width: 0,\n left: 0,\n });\n\n const listRef = useRef(null);\n\n useLayoutEffect(() => {\n if (!listRef.current) return;\n\n const activeTab = listRef.current.querySelector(\n '[data-state=\"active\"]',\n ) as HTMLButtonElement | null;\n\n if (activeTab) {\n setIndicatorProps({\n width: activeTab.offsetWidth,\n left: activeTab.offsetLeft,\n });\n }\n }, [activeIndex, children]);\n\n return (\n {\n listRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }}\n {...props}\n className={cn(\n \"relative flex flex-row items-end gap-4 overflow-x-auto px-4 border-b\",\n props.className,\n )}\n >\n {children}\n \n \n );\n});\nAnimatedTabsList.displayName = \"AnimatedTabsList\";\n\nconst TabsList = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => (\n \n));\nTabsList.displayName = \"TabsList\";\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => (\n \n));\nTabsTrigger.displayName = \"TabsTrigger\";\n\nconst TabsContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => (\n \n));\nTabsContent.displayName = \"TabsContent\";\n\ntype ChangeListener = (v: string) => void;\nconst listeners = new Map();\n\nfunction addChangeListener(id: string, listener: ChangeListener): void {\n const list = listeners.get(id) ?? [];\n list.push(listener);\n listeners.set(id, list);\n}\n\nfunction removeChangeListener(id: string, listener: ChangeListener): void {\n const list = listeners.get(id) ?? [];\n listeners.set(\n id,\n list.filter((item) => item !== listener),\n );\n}\n\nfunction update(id: string, v: string, persist: boolean): void {\n listeners.get(id)?.forEach((item) => {\n item(v);\n });\n\n if (persist) localStorage.setItem(id, v);\n else sessionStorage.setItem(id, v);\n}\n\nexport interface TabsProps extends BaseProps {\n \n groupId?: string;\n \n persist?: boolean;\n \n defaultIndex?: number;\n \n disabled?: boolean;\n \n items?: string[];\n}\n\nexport function Tabs({\n groupId,\n items = [],\n persist = false,\n defaultIndex = 0,\n disabled = false,\n ...props\n}: TabsProps): React.ReactElement {\n const { animation } = useRuru();\n const values = useMemo(() => items.map((item) => toValue(item)), [items]);\n const [value, setValue] = useState(values[defaultIndex]);\n\n useLayoutEffect(() => {\n if (!groupId) return;\n\n const onUpdate: ChangeListener = (v) => {\n if (values.includes(v)) setValue(v);\n };\n\n const previous = persist\n ? localStorage.getItem(groupId)\n : sessionStorage.getItem(groupId);\n\n if (previous) onUpdate(previous);\n addChangeListener(groupId, onUpdate);\n return () => {\n removeChangeListener(groupId, onUpdate);\n };\n }, [groupId, persist, values]);\n\n const onValueChange = useCallback(\n (v: string) => {\n if (groupId) {\n update(groupId, v, persist);\n } else {\n setValue(v);\n }\n },\n [groupId, persist],\n );\n\n return (\n \n {animation ? (\n \n {values.map((v, i) => (\n \n {items[i]}\n \n ))}\n \n ) : (\n \n {values.map((v, i) => (\n \n {items[i]}\n \n ))}\n \n )}\n {props.children}\n \n );\n}\n\nfunction toValue(v: string): string {\n return v.toLowerCase().replace(/\\s/, \"-\");\n}\n\nexport function Tab({\n value,\n className,\n ...props\n}: TabsContentProps): React.ReactElement {\n return (\n figure:only-child]:-m-4 [&>figure:only-child]:rounded-none [&>figure:only-child]:border-none\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { TabsList, TabsTrigger, TabsContent };\n"
+ "content": "\"use client\";\n\nimport type {\n TabsContentProps,\n TabsProps as BaseProps,\n} from \"@radix-ui/react-tabs\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport React, {\n useMemo,\n useState,\n useCallback,\n useLayoutEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nconst TabsPrimitiveRoot = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => {\n return (\n \n );\n});\n\nTabsPrimitiveRoot.displayName = \"TabsPrimitiveRoot\";\n\nconst AnimatedTabsList = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => {\n const [activeIndex] = useState(0);\n const [indicatorProps, setIndicatorProps] = useState({\n width: 0,\n left: 0,\n });\n const [hasMounted, setHasMounted] = useState(false);\n\n const listRef = useRef(null);\n\n useLayoutEffect(() => {\n if (!listRef.current) return;\n\n const activeTab = listRef.current.querySelector(\n '[data-state=\"active\"]',\n ) as HTMLButtonElement | null;\n\n if (activeTab) {\n setIndicatorProps({\n width: activeTab.offsetWidth,\n left: activeTab.offsetLeft,\n });\n }\n\n if (!hasMounted) {\n setHasMounted(true);\n }\n }, [activeIndex, children, hasMounted]);\n\n return (\n {\n listRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }}\n {...props}\n className={cn(\n \"relative flex flex-row items-end gap-4 overflow-x-auto px-4 border-b\",\n props.className,\n )}\n >\n {children}\n \n \n );\n});\nAnimatedTabsList.displayName = \"AnimatedTabsList\";\n\nconst TabsList = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => (\n \n));\nTabsList.displayName = \"TabsList\";\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => {\n const { animation } = useRuru();\n return (\n \n );\n});\nTabsTrigger.displayName = \"TabsTrigger\";\n\nconst TabsContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>((props, ref) => (\n \n));\nTabsContent.displayName = \"TabsContent\";\n\ntype ChangeListener = (v: string) => void;\nconst listeners = new Map();\n\nfunction addChangeListener(id: string, listener: ChangeListener): void {\n const list = listeners.get(id) ?? [];\n list.push(listener);\n listeners.set(id, list);\n}\n\nfunction removeChangeListener(id: string, listener: ChangeListener): void {\n const list = listeners.get(id) ?? [];\n listeners.set(\n id,\n list.filter((item) => item !== listener),\n );\n}\n\nfunction update(id: string, v: string, persist: boolean): void {\n listeners.get(id)?.forEach((item) => {\n item(v);\n });\n\n if (persist) localStorage.setItem(id, v);\n else sessionStorage.setItem(id, v);\n}\n\nexport interface TabsProps extends BaseProps {\n \n groupId?: string;\n \n persist?: boolean;\n \n defaultIndex?: number;\n \n disabled?: boolean;\n \n items?: string[];\n}\n\nexport function Tabs({\n groupId,\n items = [],\n persist = false,\n defaultIndex = 0,\n disabled = false,\n ...props\n}: TabsProps): React.ReactElement {\n const { animation } = useRuru();\n const values = useMemo(() => items.map((item) => toValue(item)), [items]);\n const [value, setValue] = useState(values[defaultIndex]);\n\n useLayoutEffect(() => {\n if (!groupId) return;\n\n const onUpdate: ChangeListener = (v) => {\n if (values.includes(v)) setValue(v);\n };\n\n const previous = persist\n ? localStorage.getItem(groupId)\n : sessionStorage.getItem(groupId);\n\n if (previous) onUpdate(previous);\n addChangeListener(groupId, onUpdate);\n return () => {\n removeChangeListener(groupId, onUpdate);\n };\n }, [groupId, persist, values]);\n\n const onValueChange = useCallback(\n (v: string) => {\n if (groupId) {\n update(groupId, v, persist);\n } else {\n setValue(v);\n }\n },\n [groupId, persist],\n );\n\n return (\n \n {animation ? (\n \n {values.map((v, i) => (\n \n {items[i]}\n \n ))}\n \n ) : (\n \n {values.map((v, i) => (\n \n {items[i]}\n \n ))}\n \n )}\n {props.children}\n \n );\n}\n\nfunction toValue(v: string): string {\n return v.toLowerCase().replace(/\\s/, \"-\");\n}\n\nexport function Tab({\n value,\n className,\n ...props\n}: TabsContentProps): React.ReactElement {\n return (\n figure:only-child]:-m-4 [&>figure:only-child]:rounded-none [&>figure:only-child]:border-none\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { TabsList, TabsTrigger, TabsContent };\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/components/tooltip.json b/apps/www/public/registry/components/tooltip.json
index c6a4de1..bbb4d0f 100644
--- a/apps/www/public/registry/components/tooltip.json
+++ b/apps/www/public/registry/components/tooltip.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "tooltip.tsx",
- "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, sideOffset = 5, ...props }, ref) => (\n \n {props.children}\n \n \n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"
+ "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport { motion } from \"framer-motion\";\n\nimport { cn } from \"@/utils/cn\";\nimport { useRuru } from \"@/provider\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, sideOffset = 5, ...props }, ref) => {\n const { animation } = useRuru();\n\n return animation ? (\n \n ) : (\n \n {props.children}\n \n \n );\n});\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nconst AnimatedTooltipContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, sideOffset = 5, ...props }, ref) => (\n \n \n \n {props.children}\n \n
\n \n \n));\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/interface/RuruContextType.json b/apps/www/public/registry/interface/RuruContextType.json
new file mode 100644
index 0000000..3a54542
--- /dev/null
+++ b/apps/www/public/registry/interface/RuruContextType.json
@@ -0,0 +1,4 @@
+{
+ "name": "RuruContextType",
+ "content": "\nexport interface RuruContextType {\n disableAnimation: boolean;\n animation: boolean;\n setAnimation: (Animation: boolean) => void;\n}\n"
+}
diff --git a/apps/www/public/registry/interface/RuruProviderProps.json b/apps/www/public/registry/interface/RuruProviderProps.json
new file mode 100644
index 0000000..9bf80e6
--- /dev/null
+++ b/apps/www/public/registry/interface/RuruProviderProps.json
@@ -0,0 +1,4 @@
+{
+ "name": "RuruProviderProps",
+ "content": "import { type ReactNode } from \"react\";\n\nexport interface RuruProviderProps {\n children: ReactNode;\n togleThemeAnimation?: boolean;\n disableAnimation?: boolean;\n disableBaseColor?: boolean;\n}\n"
+}
diff --git a/apps/www/public/registry/interface/RuruThemeProviderProps.json b/apps/www/public/registry/interface/RuruThemeProviderProps.json
new file mode 100644
index 0000000..fb7e294
--- /dev/null
+++ b/apps/www/public/registry/interface/RuruThemeProviderProps.json
@@ -0,0 +1,4 @@
+{
+ "name": "RuruThemeProviderProps",
+ "content": "import { type ReactNode } from \"react\";\n\nexport interface RuruThemeProviderProps {\n children: ReactNode;\n attribute?: string;\n defaultTheme?: string;\n enableSystem?: boolean;\n disableTransitionOnChange?: boolean;\n}\n"
+}
diff --git a/apps/www/public/registry/interface/index.json b/apps/www/public/registry/interface/index.json
new file mode 100644
index 0000000..a43cb43
--- /dev/null
+++ b/apps/www/public/registry/interface/index.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name": "RuruContextType"
+ },
+ {
+ "name": "RuruProviderProps"
+ },
+ {
+ "name": "RuruThemeProviderProps"
+ }
+]
diff --git a/apps/www/public/registry/providers/components/index.json b/apps/www/public/registry/providers/components/index.json
new file mode 100644
index 0000000..56663a0
--- /dev/null
+++ b/apps/www/public/registry/providers/components/index.json
@@ -0,0 +1,10 @@
+{
+ "name": "index",
+ "files": [
+ {
+ "name": "index.tsx",
+ "content": "\"use client\";\n\nimport React, { createContext, useContext, useState } from \"react\";\nimport { type RuruContextType } from \"@/interfaces/RuruContextType\";\nimport { type RuruProviderProps } from \"@/interfaces/RuruProviderProps\";\nimport { RuruThemeProvider } from \"./theme\";\n\nconst RuruContext = createContext(undefined);\n\nexport const RuruProvider: React.FC = ({\n children,\n togleThemeAnimation = false,\n disableAnimation = false,\n disableBaseColor = false,\n}) => {\n const [animation, setAnimation] = useState(!disableAnimation);\n\n return (\n \n \n {disableBaseColor ? (\n children\n ) : (\n {children}
\n )}\n \n \n );\n};\n\nexport const useRuru = (): RuruContextType => {\n const context = useContext(RuruContext);\n if (!context) {\n throw new Error(\"useRuru must be used within a RuruProvider\");\n }\n return context;\n};\n"
+ }
+ ],
+ "type": "components:component"
+}
diff --git a/apps/www/public/registry/providers/components/theme.json b/apps/www/public/registry/providers/components/theme.json
new file mode 100644
index 0000000..8a86e45
--- /dev/null
+++ b/apps/www/public/registry/providers/components/theme.json
@@ -0,0 +1,10 @@
+{
+ "name": "theme",
+ "files": [
+ {
+ "name": "theme.tsx",
+ "content": "\"use client\";\n\nimport { ThemeProvider } from \"next-themes\";\nimport React from \"react\";\nimport { type RuruThemeProviderProps } from \"@/interfaces/RuruThemeProviderProps\";\n\nexport const RuruThemeProvider: React.FC = ({\n children,\n attribute = \"class\",\n defaultTheme = \"system\",\n enableSystem = true,\n disableTransitionOnChange = true,\n}) => {\n return (\n \n {children}
\n \n );\n};\n\nimport { useTheme } from \"next-themes\";\nexport { useTheme };\n"
+ }
+ ],
+ "type": "components:component"
+}
diff --git a/apps/www/public/registry/providers/index.json b/apps/www/public/registry/providers/index.json
new file mode 100644
index 0000000..dbb0306
--- /dev/null
+++ b/apps/www/public/registry/providers/index.json
@@ -0,0 +1,12 @@
+[
+ {
+ "name": "index",
+ "files": ["index.tsx"],
+ "type": "components:component"
+ },
+ {
+ "name": "theme",
+ "files": ["theme.tsx"],
+ "type": "components:component"
+ }
+]
diff --git a/apps/www/public/schema.json b/apps/www/public/schema.json
new file mode 100644
index 0000000..aa8f0de
--- /dev/null
+++ b/apps/www/public/schema.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "properties": {
+ "tailwind": {
+ "type": "object",
+ "properties": {
+ "config": {
+ "type": "string"
+ },
+ "css": {
+ "type": "string"
+ },
+ "baseColor": {
+ "type": "string"
+ },
+ "cssVariables": {
+ "type": "boolean"
+ },
+ "prefix": {
+ "type": "string"
+ }
+ },
+ "required": ["config", "css", "baseColor", "cssVariables"]
+ },
+ "rsc": {
+ "type": "boolean"
+ },
+ "tsx": {
+ "type": "boolean"
+ },
+ "aliases": {
+ "type": "object",
+ "properties": {
+ "utils": {
+ "type": "string"
+ },
+ "components": {
+ "type": "string"
+ },
+ "ui": {
+ "type": "string"
+ },
+ "provider": {
+ "type": "string"
+ },
+ "interfaces": {
+ "type": "string"
+ }
+ },
+ "required": ["utils", "components", "provider"]
+ }
+ },
+ "required": ["tailwind", "rsc", "aliases"]
+}
diff --git a/apps/www/registry/components.ts b/apps/www/registry/components.ts
new file mode 100644
index 0000000..a36542e
--- /dev/null
+++ b/apps/www/registry/components.ts
@@ -0,0 +1,14 @@
+import { Registry } from "@/registry/schema";
+
+export const components: Registry = [
+ {
+ name: "index",
+ type: "components:component",
+ files: ["index.tsx"],
+ },
+ {
+ name: "theme",
+ type: "components:component",
+ files: ["theme.tsx"],
+ },
+];
diff --git a/apps/www/registry/registry.ts b/apps/www/registry/registry.ts
index 95bf1e4..effdb08 100644
--- a/apps/www/registry/registry.ts
+++ b/apps/www/registry/registry.ts
@@ -1,4 +1,5 @@
import { Registry } from "@/registry/schema";
import { ui } from "@/registry/ui";
+import { components } from "@/registry/components";
-export const registry: Registry = [...ui];
+export const registry: Registry = [...ui, ...components];
diff --git a/apps/www/scripts/build-registry.mts b/apps/www/scripts/build-registry.mts
index 7b2c78f..eda5e54 100644
--- a/apps/www/scripts/build-registry.mts
+++ b/apps/www/scripts/build-registry.mts
@@ -8,50 +8,9 @@ import { Registry, registrySchema } from "../registry/schema";
const REGISTRY_PATH = path.join(process.cwd(), "public/registry");
-// ----------------------------------------------------------------------------
-// Build __registry__/index.tsx.
-// ----------------------------------------------------------------------------
async function buildRegistry(registry: Registry) {
console.log(" ⚒️ Building registry...");
- let index = `// @ts-nocheck
-// This file is autogenerated by scripts/build-registry.ts
-// Do not edit this file directly.
-import * as React from "react"
-
-export const Index: Record = {
-`;
-
- index += ` "components": {`;
-
- // Build style index.
- for (const item of registry) {
- const resolveFiles = item.files.map(
- (file) => `@/../../packages/ui/src/components/${file}`,
- );
-
- let sourceFilename = "";
-
- index += `
- "${item.name}": {
- name: "${item.name}",
- type: "${item.type}",
- registryDependencies: ${JSON.stringify(item.registryDependencies)},
- component: React.lazy(() => import("@/../../packages/ui/src/components/${item.name}")),
- source: "${sourceFilename}",
- files: [${resolveFiles.map((file) => `"${file}"`)}],
- category: "${item.category}",
- subcategory: ${JSON.stringify(item.subcategory)}
- },`;
- }
-
- index += `
- },`;
-
- index += `
-}
-`;
-
// ----------------------------------------------------------------------------
// Build registry/index.json.
// ----------------------------------------------------------------------------
@@ -64,6 +23,17 @@ export const Index: Record = {
"utf8",
);
+ const providersNames = registry.filter(
+ (item) => item.type === "components:component",
+ );
+ const providersRregistryJson = JSON.stringify(providersNames, null, 2);
+ rimraf.sync(path.join(REGISTRY_PATH, "providers", "index.json"));
+ await fs.writeFile(
+ path.join(REGISTRY_PATH, "providers", "index.json"),
+ providersRregistryJson,
+ "utf8",
+ );
+
console.log("✅ Registry built!");
}
@@ -84,7 +54,39 @@ async function buildStyles(registry: Registry) {
for (const item of registry) {
console.log(` |- ${item.name} `);
- if (item.type !== "components:ui") {
+ if (item.type === "components:component") {
+ const files = item.files?.map((file) => {
+ let content = readFileSync(
+ path.join("../../packages/ui/src/provider", file),
+ "utf8",
+ );
+
+ // Remove single-line comments, excluding URLs
+ content = content.replace(/(^|[^:])\/\/.*$/gm, "$1");
+
+ // Remove multi-line comments
+ content = content.replace(/\/\*[\s\S]*?\*\//gm, "");
+
+ // Remove lines that are completely empty (after removing comments)
+ // content = content.replace(/^\s*\n/gm, "");
+
+ return {
+ name: basename(file),
+ content,
+ };
+ });
+
+ const payload = {
+ ...item,
+ files,
+ };
+
+ await fs.writeFile(
+ path.join(targetPath, "providers", "components", `${item.name}.json`),
+ JSON.stringify(payload, null, 2),
+ "utf8",
+ );
+
continue;
}
@@ -124,6 +126,64 @@ async function buildStyles(registry: Registry) {
console.log("✅ Styles built!");
}
+// ----------------------------------------------------------------------------
+// Build registry/interface/[name].json.
+// ----------------------------------------------------------------------------
+async function buildinterfaceRegistry() {
+ console.log(" ⚒️ Building interface registry...");
+
+ const targetPath = path.join(REGISTRY_PATH, "interface");
+
+ // Create directory if it doesn't exist.
+ if (!existsSync(targetPath)) {
+ await fs.mkdir(targetPath, { recursive: true });
+ }
+
+ const files = await fs.readdir("../../packages/ui/src/interfaces");
+
+ // ---------------------------------------------------------------------
+ // Build registry/interface/index.json.
+ // ---------------------------------------------------------------------
+
+ // storeing data like [ { name: interfacefilename } ]
+
+ const names = files.map((file) => ({ name: file.replace(".ts", "") }));
+
+ await fs.writeFile(
+ path.join(REGISTRY_PATH, "interface", "index.json"),
+ JSON.stringify(names, null, 2),
+ "utf8",
+ );
+
+ // ---------------------------------------------------------------------
+ // Build registry/interface/[name].json.
+ // ---------------------------------------------------------------------
+
+ for (const file of files) {
+ let content = readFileSync(
+ path.join("../../packages/ui/src/interfaces", file),
+ "utf8",
+ );
+
+ // Remove single-line comments, excluding URLs
+ content = content.replace(/(^|[^:])\/\/.*$/gm, "$1");
+
+ // Remove multi-line comments
+ content = content.replace(/\/\*[\s\S]*?\*\//gm, "");
+
+ // Remove lines that are completely empty (after removing comments)
+ // content = content.replace(/^\s*\n/gm, "");
+
+ await fs.writeFile(
+ path.join(targetPath, `${file.replace(".ts", "")}.json`),
+ JSON.stringify({ name: file.replace(".ts", ""), content }, null, 2),
+ "utf8",
+ );
+ }
+
+ console.log("✅ Interface registry built!");
+}
+
try {
const result = registrySchema.safeParse(registry);
@@ -134,6 +194,7 @@ try {
await buildRegistry(result.data);
await buildStyles(result.data);
+ await buildinterfaceRegistry();
console.log("✅ All done!");
} catch (error) {
diff --git a/apps/www/tailwind.config.js b/apps/www/tailwind.config.js
index b97df53..52846e1 100644
--- a/apps/www/tailwind.config.js
+++ b/apps/www/tailwind.config.js
@@ -1,4 +1,5 @@
-import { createPreset } from "fumadocs-ui/tailwind-plugin";
+import { createPreset, presets } from "fumadocs-ui/tailwind-plugin";
+import animate from "tailwindcss-animate";
/** @type {import('tailwindcss').Config} */
// eslint-disable-next-line import/no-anonymous-default-export
@@ -24,6 +25,25 @@ export default {
},
},
},
- plugins: [require("tailwindcss-animate")],
- presets: [createPreset()],
+ plugins: [animate],
+ // presets: [createPreset()],
+ presets: [
+ createPreset({
+ preset: {
+ ...presets.default,
+ dark: {
+ ...presets.default.dark,
+ background: "0 0% 2%",
+ foreground: "0 0% 98%",
+ popover: "0 0% 4%",
+ card: "0 0% 4%",
+ muted: "0 0% 8%",
+ border: "0 0% 14%",
+ accent: "0 0% 15%",
+ "accent-foreground": "0 0% 100%",
+ "muted-foreground": "0 0% 60%",
+ },
+ },
+ }),
+ ],
};
diff --git a/apps/www/tsconfig.json b/apps/www/tsconfig.json
index 77171ab..42bafc9 100644
--- a/apps/www/tsconfig.json
+++ b/apps/www/tsconfig.json
@@ -1,31 +1,10 @@
{
"extends": "tsconfig/nextjs.json",
"compilerOptions": {
- "baseUrl": ".",
- "target": "ESNext",
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- // "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
"paths": {
"@/*": ["./*"],
"@ruru-ui": ["../../packages/ui/dist"]
- },
- "plugins": [
- {
- "name": "next"
- }
- ]
+ }
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts
index 9853dd8..7275691 100644
--- a/packages/cli/src/commands/add.ts
+++ b/packages/cli/src/commands/add.ts
@@ -3,7 +3,7 @@ import * as z from "zod";
import pc from "picocolors";
import { getConfig } from "@/utils/get-config";
import {
- fetchTree,
+ fetchComponentsTree,
getItemTargetPath,
getRegistryBaseColor,
getRegistryIndex,
@@ -57,7 +57,7 @@ export const add = new Command()
if (!config) {
pc.white(
- `Configuration is missing. Please run ${pc.green(`init`)} to create a components.json file.`,
+ `Configuration is missing. Please run ${pc.green(`init`)} to create a ruru.json file.`,
);
process.exit(1);
}
@@ -91,7 +91,7 @@ export const add = new Command()
}
const tree = await resolveTree(registryIndex, selectedComponents);
- const payload = await fetchTree(tree);
+ const payload = await fetchComponentsTree(tree);
const baseColor = await getRegistryBaseColor();
if (!payload.length) {
@@ -217,7 +217,7 @@ export const add = new Command()
registryIndex,
subcategoryComponents,
);
- const subcategoryPayload = await fetchTree(subcategoryTree);
+ const subcategoryPayload = await fetchComponentsTree(subcategoryTree);
for (const subitem of subcategoryPayload) {
spinner.text = `Installing subcategory component ${subitem.name}...`;
diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts
index d89bdae..b060946 100644
--- a/packages/cli/src/commands/init.ts
+++ b/packages/cli/src/commands/init.ts
@@ -29,12 +29,25 @@ import ora from "ora";
import { applyPrefixesCss } from "@/utils/transformers/transform-tw-prefix";
import * as z from "zod";
import { Command } from "commander";
+import {
+ fetchInterfaceTree,
+ fetchTree,
+ getInterfaceRegistryIndex,
+ getItemTargetPath,
+ getProviderRegistryIndex,
+ getRegistryBaseColor,
+ resolveInterfaceTree,
+ resolveTree,
+} from "@/utils/registry";
+import { transform } from "@/utils/transformers";
const PROJECT_DEPENDENCIES = [
"tailwindcss-animate",
"class-variance-authority",
"clsx",
"tailwind-merge",
+ "framer-motion",
+ "next-themes",
];
const initOptionsSchema = z.object({
@@ -78,6 +91,7 @@ export const init = new Command()
} else {
// Read config.
const existingConfig = await getConfig(cwd);
+
const config = await promptForConfig(
cwd,
existingConfig,
@@ -104,6 +118,12 @@ export const init = new Command()
export async function runInit(cwd: string, config: Config) {
const spinner = ora(`Initializing project...`)?.start();
+ const registryIndex = await getProviderRegistryIndex();
+
+ const tree = await resolveTree(registryIndex, ["index", "theme"]);
+ const payload = await fetchTree(tree);
+ const baseColor = await getRegistryBaseColor();
+
// Ensure all resolved paths directories exist.
for (const [key, resolvedPath] of Object.entries(config.resolvedPaths)) {
// Determine if the path is a file or directory.
@@ -173,6 +193,96 @@ export async function runInit(cwd: string, config: Config) {
"utf8",
);
+ // write provider file. and theme file in config.resolvedPaths.provider
+
+ const providerSpinner = ora(`Writing provider file...`)?.start();
+
+ for (const item of payload) {
+ spinner.text = `Installing ${item.name}...`;
+ const targetDir = await getItemTargetPath(
+ config,
+ item,
+ cwd ? path.resolve(cwd, cwd) : undefined,
+ );
+
+ if (!targetDir) {
+ continue;
+ }
+
+ if (!existsSync(targetDir)) {
+ await fs.mkdir(targetDir, { recursive: true });
+ }
+
+ for (const file of item.files) {
+ let filePath = path.resolve(
+ targetDir,
+ config.resolvedPaths.provider,
+ file.name,
+ );
+
+ // Run transformers.
+ const content = await transform({
+ filename: file.name,
+ raw: file.content,
+ config,
+ baseColor,
+ });
+
+ if (!config.tsx) {
+ filePath = filePath.replace(/\.tsx$/, ".jsx");
+ filePath = filePath.replace(/\.ts$/, ".js");
+ }
+
+ await fs.writeFile(filePath, content);
+ }
+ }
+
+ providerSpinner?.succeed();
+
+ // write interface files if user use TS
+ // all TS files are on /registry/interface/index.json => [ "RuruContextType.ts", "RuruProviderProps.ts", "RuruThemeProviderProps.ts"]
+
+ // TODO: write interface files
+
+ if (config.tsx) {
+ // first get all interface files name
+ const interfaceRegistryIndex = await getInterfaceRegistryIndex();
+ const interfaceTree = await resolveInterfaceTree(interfaceRegistryIndex);
+ const interfacePayload = await fetchInterfaceTree(interfaceTree);
+
+ const interfaceSpinner = ora(`Writing interface files...`)?.start();
+
+ for (const item of interfacePayload) {
+ spinner.text = `Installing ${item.name}...`;
+ const targetDir = config.resolvedPaths.interfaces;
+
+ if (!targetDir) {
+ continue;
+ }
+
+ if (!existsSync(targetDir)) {
+ await fs.mkdir(targetDir, { recursive: true });
+ }
+
+ // Determine the file path and force it to have a .ts extension
+ let filePath = path.resolve(targetDir, item.name);
+ filePath = `${filePath}.ts`; // Append .ts extension
+
+ // Run transformers.
+ const content = await transform({
+ filename: item.name,
+ raw: item.content,
+ config,
+ baseColor,
+ });
+
+ // Save the file with the .ts extension
+ await fs.writeFile(filePath, content);
+ }
+
+ interfaceSpinner?.succeed();
+ }
+
spinner?.succeed();
// Install dependencies.
@@ -209,11 +319,6 @@ export async function promptForConfig(
message: `Would you like to use ${pc.bold("TypeScript")} (recommended)?`,
initialValue: true,
}),
- defaults: () =>
- confirm({
- message: `use default configuration.`,
- initialValue: false,
- }),
tailwindCss: () =>
text({
message: `Where is your ${pc.bold("global CSS")} file?`,
@@ -257,6 +362,18 @@ export async function promptForConfig(
placeholder: "@/lib/utils",
initialValue: "@/lib/utils",
}),
+ provider: () =>
+ text({
+ message: `Configure the import alias for ${pc.bold("provider")}:`,
+ placeholder: "@/provider",
+ initialValue: "@/provider",
+ }),
+ interfaces: () =>
+ text({
+ message: `Configure the import alias for ${pc.bold("interfaces")}:`,
+ placeholder: "@/interfaces",
+ initialValue: "@/interfaces",
+ }),
rsc: () =>
confirm({
message: `Would you like to use ${pc.bold("RSC")} ?`,
@@ -272,6 +389,7 @@ export async function promptForConfig(
);
const config = rawConfigSchema.parse({
+ $schema: "https://ruru-ui.vercel.app/schema.json",
tailwind: {
config: options.tailwindConfig,
css: options.tailwindCss,
@@ -284,12 +402,14 @@ export async function promptForConfig(
components: options.components,
utils: options.utils,
ui: options.components,
+ provider: options.provider,
+ interfaces: options.interfaces,
},
});
if (!skip) {
const proceed = confirm({
- message: `Write configuration to ${pc.bold("components.json")}. Proceed??`,
+ message: `Write configuration to ${pc.bold("ruru.json")}. Proceed??`,
initialValue: true,
});
@@ -300,8 +420,8 @@ export async function promptForConfig(
// Write to file.
- const spinner = ora(`Writing components.json...`)?.start();
- const targetPath = path.resolve(cwd, "components.json");
+ const spinner = ora(`Writing ruru.json...`)?.start();
+ const targetPath = path.resolve(cwd, "ruru.json");
await fs.writeFile(targetPath, JSON.stringify(config, null, 2), "utf8");
spinner.succeed();
@@ -338,8 +458,8 @@ export async function promptForMinimalConfig(
});
// Write to file.
- const spinner = ora(`Writing components.json...`).start();
- const targetPath = path.resolve(cwd, "components.json");
+ const spinner = ora(`Writing ruru.json...`).start();
+ const targetPath = path.resolve(cwd, "ruru.json");
await fs.writeFile(targetPath, JSON.stringify(config, null, 2), "utf8");
spinner.succeed();
diff --git a/packages/cli/src/utils/get-config.ts b/packages/cli/src/utils/get-config.ts
index 8c7ca01..5437fec 100644
--- a/packages/cli/src/utils/get-config.ts
+++ b/packages/cli/src/utils/get-config.ts
@@ -12,9 +12,9 @@ export const DEFAULT_TAILWIND_CONFIG = "tailwind.config.js";
export const DEFAULT_TAILWIND_BASE_COLOR = "slate";
// TODO: Figure out if we want to support all cosmiconfig formats.
-// A simple components.json file would be nice.
+// A simple ruru.json file would be nice.
const explorer = cosmiconfig("components", {
- searchPlaces: ["components.json"],
+ searchPlaces: ["ruru.json"],
});
export const rawConfigSchema = z
@@ -31,6 +31,8 @@ export const rawConfigSchema = z
components: z.string(),
utils: z.string(),
ui: z.string().optional(),
+ provider: z.string().optional(),
+ interfaces: z.string().optional(),
}),
})
.strict();
@@ -44,6 +46,8 @@ export const configSchema = rawConfigSchema.extend({
utils: z.string(),
components: z.string(),
ui: z.string(),
+ provider: z.string(),
+ interfaces: z.string(),
}),
});
@@ -81,6 +85,12 @@ export async function resolveConfigPaths(cwd: string, config: RawConfig) {
ui: config.aliases["ui"]
? await resolveImport(config.aliases["ui"], tsConfig)
: await resolveImport(config.aliases["components"], tsConfig),
+ provider: config.aliases["provider"]
+ ? await resolveImport(config.aliases["provider"], tsConfig)
+ : await resolveImport(config.aliases["components"], tsConfig),
+ interfaces: config.aliases["interfaces"]
+ ? await resolveImport(config.aliases["interfaces"], tsConfig)
+ : await resolveImport(config.aliases["components"], tsConfig),
},
});
}
@@ -95,6 +105,6 @@ export async function getRawConfig(cwd: string): Promise {
return rawConfigSchema.parse(configResult.config);
} catch (error) {
- throw new Error(`Invalid configuration found in ${cwd}/components.json.`);
+ throw new Error(`Invalid configuration found in ${cwd}/ruru.json.`);
}
}
diff --git a/packages/cli/src/utils/registry/index.ts b/packages/cli/src/utils/registry/index.ts
index cd29be2..0cc8691 100644
--- a/packages/cli/src/utils/registry/index.ts
+++ b/packages/cli/src/utils/registry/index.ts
@@ -1,10 +1,12 @@
import path from "path";
import { Config } from "@/utils/get-config";
import {
+ interfaceRegistryIndexsSchemaSchema,
registryBaseColorSchema,
registryIndexSchema,
registryItemWithContentSchema,
registryWithContentSchema,
+ registryWithContentSchemaForInterfaces,
stylesSchema,
} from "@/utils/registry/schema";
import { HttpsProxyAgent } from "https-proxy-agent";
@@ -28,6 +30,26 @@ export async function getRegistryIndex() {
}
}
+export async function getProviderRegistryIndex() {
+ try {
+ const [result] = await fetchRegistry(["providers/index.json"]);
+
+ return registryIndexSchema.parse(result);
+ } catch (error) {
+ throw new Error(`Failed to fetch components from registry.`);
+ }
+}
+
+export async function getInterfaceRegistryIndex() {
+ try {
+ const [result] = await fetchRegistry(["interface/index.json"]);
+
+ return interfaceRegistryIndexsSchemaSchema.parse(result);
+ } catch (error) {
+ throw new Error(`Failed to fetch components from registry.`);
+ }
+}
+
export async function getRegistryStyles() {
try {
const [result] = await fetchRegistry(["styles/index.json"]);
@@ -100,7 +122,15 @@ export async function resolveTree(
);
}
-export async function fetchTree(tree: z.infer) {
+export async function resolveInterfaceTree(
+ index: z.infer,
+) {
+ return index;
+}
+
+export async function fetchComponentsTree(
+ tree: z.infer,
+) {
try {
const paths = tree.map((item) => `components/${item.name}.json`);
const result = await fetchRegistry(paths);
@@ -111,6 +141,30 @@ export async function fetchTree(tree: z.infer) {
}
}
+export async function fetchTree(tree: z.infer) {
+ try {
+ const paths = tree.map((item) => `providers/components/${item.name}.json`);
+ const result = await fetchRegistry(paths);
+
+ return registryWithContentSchema.parse(result);
+ } catch (error) {
+ throw new Error(`Failed to fetch tree from registry.`);
+ }
+}
+
+export async function fetchInterfaceTree(
+ tree: z.infer,
+) {
+ try {
+ const paths = tree.map((item) => `interface/${item.name}.json`);
+ const result = await fetchRegistry(paths);
+
+ return registryWithContentSchemaForInterfaces.parse(result);
+ } catch (error) {
+ throw new Error(`Failed to fetch tree from registry.`);
+ }
+}
+
export async function getItemTargetPath(
config: Config,
item: Pick, "type">,
@@ -124,6 +178,10 @@ export async function getItemTargetPath(
return config.resolvedPaths.ui;
}
+ if (item.type === "components:component" && config.aliases.provider) {
+ return config.resolvedPaths.provider;
+ }
+
const [parent, type] = item.type.split(":");
if (!(parent in config.resolvedPaths)) {
return null;
diff --git a/packages/cli/src/utils/registry/schema.ts b/packages/cli/src/utils/registry/schema.ts
index 0586de2..8001324 100644
--- a/packages/cli/src/utils/registry/schema.ts
+++ b/packages/cli/src/utils/registry/schema.ts
@@ -24,6 +24,15 @@ export const registryItemWithContentSchema = registryItemSchema.extend({
export const registryWithContentSchema = z.array(registryItemWithContentSchema);
+export const registryItemWithContentSchemaForInterfaces = z.object({
+ name: z.string(),
+ content: z.string(),
+});
+
+export const registryWithContentSchemaForInterfaces = z.array(
+ registryItemWithContentSchemaForInterfaces,
+);
+
export const stylesSchema = z.array(
z.object({
name: z.string(),
@@ -43,3 +52,11 @@ export const registryBaseColorSchema = z.object({
inlineColorsTemplate: z.string(),
cssVarsTemplate: z.string(),
});
+
+export const interfaceRegistryIndexSchemaSchema = z.object({
+ name: z.string(),
+});
+
+export const interfaceRegistryIndexsSchemaSchema = z.array(
+ interfaceRegistryIndexSchemaSchema,
+);
diff --git a/packages/cli/src/utils/transformers/transform-import.ts b/packages/cli/src/utils/transformers/transform-import.ts
index d989e89..ff31a05 100644
--- a/packages/cli/src/utils/transformers/transform-import.ts
+++ b/packages/cli/src/utils/transformers/transform-import.ts
@@ -33,6 +33,29 @@ export const transformImport: Transformer = async ({ sourceFile, config }) => {
);
}
}
+
+ // Replace `import { type RuruProviderProps } from "@/interface/RuruProviderProps";`
+ if (
+ moduleSpecifier.startsWith("@/interface/") &&
+ config.aliases.interfaces
+ ) {
+ console.log(moduleSpecifier);
+ if (config.aliases.ui) {
+ importDeclaration.setModuleSpecifier(
+ moduleSpecifier.replace(
+ /^@\/interface\/[^/]+\/ui/,
+ config.aliases.interfaces,
+ ),
+ );
+ } else {
+ importDeclaration.setModuleSpecifier(
+ moduleSpecifier.replace(
+ /^@\/interface\/[^/]+/,
+ config.aliases.interfaces,
+ ),
+ );
+ }
+ }
}
return sourceFile;
diff --git a/packages/ui/package.json b/packages/ui/package.json
index b3dc9e7..e5d5329 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -43,6 +43,10 @@
"import": "./dist/provider/index.js",
"types": "./dist/provider/index.d.ts"
},
+ "./theme": {
+ "import": "./dist/provider/theme.js",
+ "types": "./dist/provider/theme.d.ts"
+ },
"./utils": {
"import": "./dist/utils/cn.js",
"types": "./dist/utils/cn.d.ts"
diff --git a/packages/ui/src/components/avatar.tsx b/packages/ui/src/components/avatar.tsx
index bbf3a5b..19bce19 100644
--- a/packages/ui/src/components/avatar.tsx
+++ b/packages/ui/src/components/avatar.tsx
@@ -1,6 +1,5 @@
import * as React from "react";
import { cn } from "@/utils/cn";
-
/**
* Props for the Avatar component.
*/
@@ -56,7 +55,6 @@ type AvatarProps = Omit<
*/
src: string;
};
-
/**
* Avatar component
* @param {AvatarProps} props
@@ -134,10 +132,34 @@ type AvatarGroupProps = Omit<
*
*/
limit?: number;
+ /**
+ * Additional class name for the avatar
+ *
+ * @type string
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ */
aClassName?: string;
+ /**
+ * Additional class name for the last count
+ *
+ * @type string
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ */
lnClassName?: string;
};
-
+/**
+ * AvatarGroup component
+ * @param {AvatarGroupProps} props
+ * @returns {ReactElement}
+ */
const AvatarGroup = React.forwardRef(
(
{ className, aClassName, lnClassName, size = 30, members, limit, ...props },
@@ -246,10 +268,30 @@ type AvatarWithBadgeProps = Omit<
*
*/
badgeSrc: string;
+ /**
+ * Additional class name for the avatar
+ *
+ * @type string
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ */
iClassName?: string;
+ /**
+ * Additional class name for the badge
+ *
+ * @type string
+ *
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ */
sClassName?: string;
};
-
/**
* AvatarWithBadge component
*
diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx
index 65f81b1..07ced29 100644
--- a/packages/ui/src/components/checkbox.tsx
+++ b/packages/ui/src/components/checkbox.tsx
@@ -11,7 +11,19 @@ type CheckboxProps = React.ComponentPropsWithoutRef<
> & {
indeterminate?: boolean;
};
-
+/**
+ * Checkbox component
+ *
+ * @param {React.ComponentPropsWithoutRef} props
+ * @param {boolean} [props.indeterminate=false] - Indeterminate state of the checkbox
+ * @returns {React.ReactElement} - The checkbox component
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ *
+ */
const Checkbox = React.forwardRef<
React.ElementRef,
CheckboxProps
diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx
index bb9c99f..13c14ed 100644
--- a/packages/ui/src/components/select.tsx
+++ b/packages/ui/src/components/select.tsx
@@ -26,7 +26,7 @@ const SelectTrigger = React.forwardRef<
span]:line-clamp-1",
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:border-primary/75 focus:dark:ring-primary/35 focus:ring-primary/35 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}
@@ -41,7 +41,7 @@ const SelectTrigger = React.forwardRef<
span]:line-clamp-1",
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:border-primary/75 focus:dark:ring-primary/35 focus:ring-primary/35 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}
diff --git a/packages/ui/src/components/switch.tsx b/packages/ui/src/components/switch.tsx
index 51a603d..2761d2f 100644
--- a/packages/ui/src/components/switch.tsx
+++ b/packages/ui/src/components/switch.tsx
@@ -1,10 +1,19 @@
-// "use client";
-
import * as React from "react";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cn } from "@/utils/cn";
-
+/**
+ * Switch component
+ *
+ * @param {string} className - Additional class names for the switch.
+ * @param {React.Ref>} ref - Forwarded ref.
+ *
+ * @example
+ *
+ * ```tsx
+ *
+ * ```
+ */
const Switch = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx
index 89f7235..d79df4d 100644
--- a/packages/ui/src/components/tabs.tsx
+++ b/packages/ui/src/components/tabs.tsx
@@ -35,12 +35,12 @@ const AnimatedTabsList = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ children, ...props }, ref) => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [activeIndex, setActiveIndex] = useState(0);
+ const [activeIndex] = useState(0);
const [indicatorProps, setIndicatorProps] = useState({
width: 0,
left: 0,
});
+ const [hasMounted, setHasMounted] = useState(false);
const listRef = useRef(null);
@@ -57,7 +57,11 @@ const AnimatedTabsList = React.forwardRef<
left: activeTab.offsetLeft,
});
}
- }, [activeIndex, children]);
+
+ if (!hasMounted) {
+ setHasMounted(true);
+ }
+ }, [activeIndex, children, hasMounted]);
return (
@@ -101,16 +109,21 @@ TabsList.displayName = "TabsList";
const TabsTrigger = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
->((props, ref) => (
-
-));
+>((props, ref) => {
+ const { animation } = useRuru();
+ return (
+
+ );
+});
TabsTrigger.displayName = "TabsTrigger";
const TabsContent = React.forwardRef<
diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx
index 4d571f6..66a1097 100644
--- a/packages/ui/src/components/tooltip.tsx
+++ b/packages/ui/src/components/tooltip.tsx
@@ -2,8 +2,10 @@
import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
+import { motion } from "framer-motion";
import { cn } from "@/utils/cn";
+import { useRuru } from "@/provider";
/**
* TooltipProvider component.
* Provides context for all tooltip components.
@@ -66,20 +68,50 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 5, ...props }, ref) => {
+ const { animation } = useRuru();
+
+ return animation ? (
+
+ ) : (
+
+ {props.children}
+
+
+ );
+});
+TooltipContent.displayName = TooltipPrimitive.Content.displayName;
+
+const AnimatedTooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
>(({ className, sideOffset = 5, ...props }, ref) => (
-
- {props.children}
-
+
+
+
+ {props.children}
+
+
+
));
-TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/packages/ui/src/interface/RuruContextType.ts b/packages/ui/src/interfaces/RuruContextType.ts
similarity index 100%
rename from packages/ui/src/interface/RuruContextType.ts
rename to packages/ui/src/interfaces/RuruContextType.ts
diff --git a/packages/ui/src/interface/RuruProviderProps.ts b/packages/ui/src/interfaces/RuruProviderProps.ts
similarity index 95%
rename from packages/ui/src/interface/RuruProviderProps.ts
rename to packages/ui/src/interfaces/RuruProviderProps.ts
index caf68a4..704f30a 100644
--- a/packages/ui/src/interface/RuruProviderProps.ts
+++ b/packages/ui/src/interfaces/RuruProviderProps.ts
@@ -1,5 +1,4 @@
import { type ReactNode } from "react";
-
/**
* Properties for the `RuruProvider` component.
*
@@ -18,4 +17,5 @@ export interface RuruProviderProps {
children: ReactNode;
togleThemeAnimation?: boolean;
disableAnimation?: boolean;
+ disableBaseColor?: boolean;
}
diff --git a/packages/ui/src/interface/RuruThemeProviderProps.ts b/packages/ui/src/interfaces/RuruThemeProviderProps.ts
similarity index 99%
rename from packages/ui/src/interface/RuruThemeProviderProps.ts
rename to packages/ui/src/interfaces/RuruThemeProviderProps.ts
index 0e5a205..9cc220f 100644
--- a/packages/ui/src/interface/RuruThemeProviderProps.ts
+++ b/packages/ui/src/interfaces/RuruThemeProviderProps.ts
@@ -1,5 +1,4 @@
import { type ReactNode } from "react";
-
/**
* Properties for the `RuruThemeProvider` component.
*
diff --git a/packages/ui/src/provider/index.tsx b/packages/ui/src/provider/index.tsx
index 98ea0ca..9dceaee 100644
--- a/packages/ui/src/provider/index.tsx
+++ b/packages/ui/src/provider/index.tsx
@@ -1,10 +1,9 @@
"use client";
import React, { createContext, useContext, useState } from "react";
-import { type RuruContextType } from "@/interface/RuruContextType";
-import { type RuruProviderProps } from "@/interface/RuruProviderProps";
+import { type RuruContextType } from "@/interfaces/RuruContextType";
+import { type RuruProviderProps } from "@/interfaces/RuruProviderProps";
import { RuruThemeProvider } from "./theme";
-
// Create the context with default values
const RuruContext = createContext(undefined);
@@ -12,18 +11,22 @@ export const RuruProvider: React.FC = ({
children,
togleThemeAnimation = false,
disableAnimation = false,
+ disableBaseColor = false,
}) => {
const [animation, setAnimation] = useState(!disableAnimation);
return (
- {children}
+ {disableBaseColor ? (
+ children
+ ) : (
+ {children}
+ )}
);
};
-
/**
* Custom hook to use the Ruru context.
*
diff --git a/packages/ui/src/provider/theme.tsx b/packages/ui/src/provider/theme.tsx
index 9915348..af3073e 100644
--- a/packages/ui/src/provider/theme.tsx
+++ b/packages/ui/src/provider/theme.tsx
@@ -2,8 +2,7 @@
import { ThemeProvider } from "next-themes";
import React from "react";
-import { type RuruThemeProviderProps } from "@/interface/RuruThemeProviderProps";
-
+import { type RuruThemeProviderProps } from "@/interfaces/RuruThemeProviderProps";
/**
* A wrapper component to provide theme context using `next-themes`.
*
@@ -36,7 +35,10 @@ export const RuruThemeProvider: React.FC = ({
enableSystem={enableSystem}
disableTransitionOnChange={disableTransitionOnChange}
>
- {children}
+ {children}
);
};
+
+import { useTheme } from "next-themes";
+export { useTheme };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f2ab2a8..f1bdaf1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -130,6 +130,12 @@ importers:
'@radix-ui/react-scroll-area':
specifier: ^1.1.0
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
+ '@radix-ui/react-tabs':
+ specifier: ^1.1.0
+ version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
+ '@vercel/analytics':
+ specifier: ^1.3.1
+ version: 1.3.1(next@14.2.5)(react@18.3.1)
fs:
specifier: 0.0.1-security
version: 0.0.1-security
@@ -3624,6 +3630,22 @@ packages:
/@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ /@vercel/analytics@1.3.1(next@14.2.5)(react@18.3.1):
+ resolution: {integrity: sha512-xhSlYgAuJ6Q4WQGkzYTLmXwhYl39sWjoMA3nHxfkvG+WdBT25c563a7QhwwKivEOZtPJXifYHR1m2ihoisbWyA==}
+ peerDependencies:
+ next: '>= 13'
+ react: ^18 || ^19
+ peerDependenciesMeta:
+ next:
+ optional: true
+ react:
+ optional: true
+ dependencies:
+ next: 14.2.5(@babel/core@7.24.9)(react-dom@18.3.1)(react@18.3.1)
+ react: 18.3.1
+ server-only: 0.0.1
+ dev: false
+
/@vercel/style-guide@6.0.0(eslint@8.57.0)(prettier@3.3.3)(typescript@5.4.5):
resolution: {integrity: sha512-tu0wFINGz91EPwaT5VjSqUwbvCY9pvLach7SPG4XyfJKPU9Vku2TFa6+AyzJ4oroGbo9fK+TQhIFHrnFl0nCdg==}
engines: {node: '>=18.18'}
@@ -9170,6 +9192,10 @@ packages:
upper-case-first: 1.1.2
dev: true
+ /server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+ dev: false
+
/set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}