Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: button types and component export standard improvement #492

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions content/docs/components/animated-subscribe-button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ npx shadcn@latest add "https://magicui.design/r/animated-subscribe-button"

## Props

| Prop | Type | Default | Description |
| ----------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `buttonColor` | `string` | `-` | The accent color for the button. This allows you to set a custom color that matches your brand's theme. |
| `buttonTextColor` | `string` | `-` | The color of the button text. This allows you to ensure the text is visible and matches your desired color scheme. |
| `subscribeStatus` | `boolean` | `-` | A boolean flag to check the condition for the button. This property can be used to toggle the button's state, such as subscribed or unsubscribed. |
| `initialText` | `string` | `-` | The initial text displayed on the button. This is useful for setting a default label when the button first appears. |
| `changeText` | `string` | `-` | The final text displayed on the button after an action has been taken. This can be used to indicate a state change, such as from "Subscribe" to "Subscribed". |
| Prop | Type | Default | Description |
| ----------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subscribeStatus` | `boolean` | `false` | A boolean flag to check the condition for the button. This property can be used to toggle the button's state, such as subscribed or unsubscribed. |
| `children` | `React.ReactNode` | `-` | The content to be displayed inside the button. Should contain two children - first for unsubscribed state and second for subscribed state. |
| `className` | `string` | `-` | Optional class name to be applied to the button for custom styling. |

## Credits

Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/default/animated-subscribe-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"files": [
{
"path": "magicui/animated-subscribe-button.tsx",
"content": "\"use client\";\n\nimport { AnimatePresence, motion } from \"motion/react\";\nimport React, { useState } from \"react\";\n\ninterface AnimatedSubscribeButtonProps {\n buttonColor: string;\n buttonTextColor?: string;\n subscribeStatus: boolean;\n initialText: React.ReactElement | string;\n changeText: React.ReactElement | string;\n}\n\nexport const AnimatedSubscribeButton: React.FC<\n AnimatedSubscribeButtonProps\n> = ({\n buttonColor,\n subscribeStatus,\n buttonTextColor,\n changeText,\n initialText,\n}) => {\n const [isSubscribed, setIsSubscribed] = useState<boolean>(subscribeStatus);\n\n return (\n <AnimatePresence mode=\"wait\">\n {isSubscribed ? (\n <motion.button\n className=\"relative flex h-10 w-[200px] items-center justify-center overflow-hidden rounded-md bg-white outline outline-1 outline-black\"\n onClick={() => setIsSubscribed(false)}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n >\n <motion.span\n key=\"action\"\n className=\"relative flex h-full w-full items-center justify-center font-semibold\"\n initial={{ y: -50 }}\n animate={{ y: 0 }}\n style={{ color: buttonColor }}\n >\n {changeText}\n </motion.span>\n </motion.button>\n ) : (\n <motion.button\n className=\"relative flex h-10 w-[200px] cursor-pointer items-center justify-center rounded-md border-none\"\n style={{ backgroundColor: buttonColor, color: buttonTextColor }}\n onClick={() => setIsSubscribed(true)}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n >\n <motion.span\n key=\"reaction\"\n className=\"relative flex items-center justify-center font-semibold\"\n initial={{ x: 0 }}\n exit={{ x: 50, transition: { duration: 0.1 } }}\n >\n {initialText}\n </motion.span>\n </motion.button>\n )}\n </AnimatePresence>\n );\n};\n",
"content": "\"use client\";\n\nimport { HTMLMotionProps } from \"motion/react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport React, { useState } from \"react\";\n\ninterface AnimatedSubscribeButtonProps extends HTMLMotionProps<\"button\"> {\n buttonColor: string;\n buttonTextColor?: string;\n subscribeStatus: boolean;\n initialText: React.ReactElement | string;\n changeText: React.ReactElement | string;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nexport const AnimatedSubscribeButton = React.forwardRef<\n HTMLButtonElement,\n AnimatedSubscribeButtonProps\n>(\n (\n {\n buttonColor,\n subscribeStatus,\n buttonTextColor,\n changeText,\n initialText,\n onClick,\n ...props\n },\n ref\n ) => {\n const [isSubscribed, setIsSubscribed] = useState<boolean>(subscribeStatus);\n\n return (\n <AnimatePresence mode=\"wait\">\n {isSubscribed ? (\n <motion.button\n ref={ref}\n className=\"relative flex h-10 w-[200px] items-center justify-center overflow-hidden rounded-md bg-white outline outline-1 outline-black\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n setIsSubscribed(false);\n onClick?.(e);\n }}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n {...props}\n >\n <motion.span\n ref={ref}\n key=\"action\"\n className=\"relative flex h-full w-full items-center justify-center font-semibold\"\n initial={{ y: -50 }}\n animate={{ y: 0 }}\n style={{ color: buttonColor }}\n >\n {changeText}\n </motion.span>\n </motion.button>\n ) : (\n <motion.button\n className=\"relative flex h-10 w-[200px] cursor-pointer items-center justify-center rounded-md border-none\"\n style={{ backgroundColor: buttonColor, color: buttonTextColor }}\n onClick={(e) => {\n setIsSubscribed(true);\n onClick?.(e);\n }}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n {...props}\n >\n <motion.span\n key=\"reaction\"\n className=\"relative flex items-center justify-center font-semibold\"\n initial={{ x: 0 }}\n exit={{ x: 50, transition: { duration: 0.1 } }}\n >\n {initialText}\n </motion.span>\n </motion.button>\n )}\n </AnimatePresence>\n );\n }\n);\n\nAnimatedSubscribeButton.displayName = \"AnimatedSubscribeButton\";\n",
"type": "registry:ui",
"target": ""
}
Expand Down
6 changes: 2 additions & 4 deletions public/r/styles/default/interactive-hover-button.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"name": "interactive-hover-button",
"type": "registry:ui",
"dependencies": [
"lucide-react"
],
"dependencies": ["lucide-react"],
"files": [
{
"path": "magicui/interactive-hover-button.tsx",
Expand All @@ -12,4 +10,4 @@
"target": ""
}
]
}
}
2 changes: 1 addition & 1 deletion public/r/styles/default/pulsating-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"files": [
{
"path": "magicui/pulsating-button.tsx",
"content": "\"use client\";\n\nimport React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface PulsatingButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n pulseColor?: string;\n duration?: string;\n}\n\nexport default function PulsatingButton({\n className,\n children,\n pulseColor = \"#0096ff\",\n duration = \"1.5s\",\n ...props\n}: PulsatingButtonProps) {\n return (\n <button\n className={cn(\n \"relative flex cursor-pointer items-center justify-center rounded-lg bg-blue-500 px-4 py-2 text-center text-white dark:bg-blue-500 dark:text-black\",\n className,\n )}\n style={\n {\n \"--pulse-color\": pulseColor,\n \"--duration\": duration,\n } as React.CSSProperties\n }\n {...props}\n >\n <div className=\"relative z-10\">{children}</div>\n <div className=\"absolute left-1/2 top-1/2 size-full -translate-x-1/2 -translate-y-1/2 animate-pulse rounded-lg bg-inherit\" />\n </button>\n );\n}\n",
"content": "\"use client\";\n\nimport React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface PulsatingButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n pulseColor?: string;\n duration?: string;\n}\n\nexport const PulsatingButton = React.forwardRef<\n HTMLButtonElement,\n PulsatingButtonProps\n>(\n (\n {\n className,\n children,\n pulseColor = \"#0096ff\",\n duration = \"1.5s\",\n ...props\n },\n ref,\n ) => {\n return (\n <button\n className={cn(\n \"relative flex cursor-pointer items-center justify-center rounded-lg bg-blue-500 px-4 py-2 text-center text-white dark:bg-blue-500 dark:text-black\",\n className,\n )}\n style={\n {\n \"--pulse-color\": pulseColor,\n \"--duration\": duration,\n } as React.CSSProperties\n }\n {...props}\n >\n <div className=\"relative z-10\">{children}</div>\n <div className=\"absolute left-1/2 top-1/2 size-full -translate-x-1/2 -translate-y-1/2 animate-pulse rounded-lg bg-inherit\" />\n </button>\n );\n },\n);\n\nPulsatingButton.displayName = \"PulsatingButton\";\n",
"type": "registry:ui",
"target": ""
}
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/default/rainbow-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"files": [
{
"path": "magicui/rainbow-button.tsx",
"content": "import React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\ninterface RainbowButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\nexport function RainbowButton({\n children,\n className,\n ...props\n}: RainbowButtonProps) {\n return (\n <button\n className={cn(\n \"group relative inline-flex h-11 animate-rainbow cursor-pointer items-center justify-center rounded-xl border-0 bg-[length:200%] px-8 py-2 font-medium text-primary-foreground transition-colors [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.08*1rem)_solid_transparent] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n\n // before styles\n \"before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))] before:[filter:blur(calc(0.8*1rem))]\",\n\n // light mode colors\n \"bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))]\",\n\n // dark mode colors\n \"dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))]\",\n\n className,\n )}\n {...props}\n >\n {children}\n </button>\n );\n}\n",
"content": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface RainbowButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\nexport const RainbowButton = React.forwardRef<\n HTMLButtonElement,\n RainbowButtonProps\n>(({ children, className, ...props }, ref) => {\n return (\n <button\n ref={ref}\n className={cn(\n \"group relative inline-flex h-11 animate-rainbow cursor-pointer items-center justify-center rounded-xl border-0 bg-[length:200%] px-8 py-2 font-medium text-primary-foreground transition-colors [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.08*1rem)_solid_transparent] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n // before styles\n \"before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))] before:[filter:blur(calc(0.8*1rem))]\",\n // light mode colors\n \"bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))]\",\n // dark mode colors\n \"dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,hsl(var(--color-1)),hsl(var(--color-5)),hsl(var(--color-3)),hsl(var(--color-4)),hsl(var(--color-2)))]\",\n className,\n )}\n {...props}\n >\n {children}\n </button>\n );\n});\n\nRainbowButton.displayName = \"RainbowButton\";\n",
"type": "registry:ui",
"target": ""
}
Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/default/ripple-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"files": [
{
"path": "magicui/ripple-button.tsx",
"content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React, { MouseEvent, useEffect, useState } from \"react\";\n\ninterface RippleButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n rippleColor?: string;\n duration?: string;\n}\n\nconst RippleButton = React.forwardRef<HTMLButtonElement, RippleButtonProps>(\n (\n {\n className,\n children,\n rippleColor = \"#ffffff\",\n duration = \"600ms\",\n onClick,\n ...props\n },\n ref,\n ) => {\n const [buttonRipples, setButtonRipples] = useState<\n Array<{ x: number; y: number; size: number; key: number }>\n >([]);\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n createRipple(event);\n onClick?.(event);\n };\n\n const createRipple = (event: MouseEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const rect = button.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height);\n const x = event.clientX - rect.left - size / 2;\n const y = event.clientY - rect.top - size / 2;\n\n const newRipple = { x, y, size, key: Date.now() };\n setButtonRipples((prevRipples) => [...prevRipples, newRipple]);\n };\n\n useEffect(() => {\n if (buttonRipples.length > 0) {\n const lastRipple = buttonRipples[buttonRipples.length - 1];\n const timeout = setTimeout(() => {\n setButtonRipples((prevRipples) =>\n prevRipples.filter((ripple) => ripple.key !== lastRipple.key),\n );\n }, parseInt(duration));\n return () => clearTimeout(timeout);\n }\n }, [buttonRipples, duration]);\n\n return (\n <button\n className={cn(\n \"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 bg-background px-4 py-2 text-center text-primary\",\n className,\n )}\n onClick={handleClick}\n ref={ref}\n {...props}\n >\n <div className=\"relative z-10\">{children}</div>\n <span className=\"pointer-events-none absolute inset-0\">\n {buttonRipples.map((ripple) => (\n <span\n className=\"absolute animate-rippling rounded-full bg-background opacity-30\"\n key={ripple.key}\n style={{\n width: `${ripple.size}px`,\n height: `${ripple.size}px`,\n top: `${ripple.y}px`,\n left: `${ripple.x}px`,\n backgroundColor: rippleColor,\n transform: `scale(0)`,\n }}\n />\n ))}\n </span>\n </button>\n );\n },\n);\n\nRippleButton.displayName = \"RippleButton\";\n\nexport default RippleButton;\n",
"content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React, { MouseEvent, useEffect, useState } from \"react\";\n\ninterface RippleButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n rippleColor?: string;\n duration?: string;\n}\n\nexport const RippleButton = React.forwardRef<\n HTMLButtonElement,\n RippleButtonProps\n>(\n (\n {\n className,\n children,\n rippleColor = \"#ffffff\",\n duration = \"600ms\",\n onClick,\n ...props\n },\n ref,\n ) => {\n const [buttonRipples, setButtonRipples] = useState<\n Array<{ x: number; y: number; size: number; key: number }>\n >([]);\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n createRipple(event);\n onClick?.(event);\n };\n\n const createRipple = (event: MouseEvent<HTMLButtonElement>) => {\n const button = event.currentTarget;\n const rect = button.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height);\n const x = event.clientX - rect.left - size / 2;\n const y = event.clientY - rect.top - size / 2;\n\n const newRipple = { x, y, size, key: Date.now() };\n setButtonRipples((prevRipples) => [...prevRipples, newRipple]);\n };\n\n useEffect(() => {\n if (buttonRipples.length > 0) {\n const lastRipple = buttonRipples[buttonRipples.length - 1];\n const timeout = setTimeout(() => {\n setButtonRipples((prevRipples) =>\n prevRipples.filter((ripple) => ripple.key !== lastRipple.key),\n );\n }, parseInt(duration));\n return () => clearTimeout(timeout);\n }\n }, [buttonRipples, duration]);\n\n return (\n <button\n className={cn(\n \"relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 bg-background px-4 py-2 text-center text-primary\",\n className,\n )}\n onClick={handleClick}\n ref={ref}\n {...props}\n >\n <div className=\"relative z-10\">{children}</div>\n <span className=\"pointer-events-none absolute inset-0\">\n {buttonRipples.map((ripple) => (\n <span\n className=\"absolute animate-rippling rounded-full bg-background opacity-30\"\n key={ripple.key}\n style={{\n width: `${ripple.size}px`,\n height: `${ripple.size}px`,\n top: `${ripple.y}px`,\n left: `${ripple.x}px`,\n backgroundColor: rippleColor,\n transform: `scale(0)`,\n }}\n />\n ))}\n </span>\n </button>\n );\n },\n);\n\nRippleButton.displayName = \"RippleButton\";\n",
"type": "registry:ui",
"target": ""
}
Expand Down
Loading
Loading