(null)
+
+ return (
+
+
+
+
+ )
+}
```
## Props
### Confetti
-| Prop | Type | Default | Description |
-| ------------------------- | --------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------ |
-| `particleCount` | `Integer` | `50` | The number of confetti particles to launch |
-| `angle` | `Number` | `90` | The angle in degrees at which to launch confetti |
-| `spread` | `Number` | `45` | The spread in degrees of the confetti |
-| `startVelocity` | `Number` | `45` | The initial velocity of the confetti |
-| `decay` | `Number` | `0.9` | The rate at which confetti slows down |
-| `gravity` | `Number` | `1` | The gravity applied to confetti particles |
-| `drift` | `Number` | `0` | The horizontal drift applied to particles |
-| `flat` | `Boolean` | `false` | Whether confetti particles are flat |
-| `ticks` | `Number` | `200` | The number of frames confetti lasts |
-| `origin` | `Object` | `{ x: 0.5, y: 0.5 }` | The origin point of the confetti |
-| `colors` | `Array of Strings` | `['#26ccff', '#a25afd', '#ff5e7e', '#88ff5a', '#fcff42', '#ffa62d', '#ff36ff']` | Array of color strings in HEX format |
-| `shapes` | `Array of Strings` | `['square', 'circle', 'star']` | Array of shapes for the confetti |
-| `zIndex` | `Integer` | `100` | The z-index of the confetti |
-| `disableForReducedMotion` | `Boolean` | `false` | Disables confetti for users who prefer no motion |
-| `useWorker` | `Boolean` | `true` | Use Web Worker for better performance |
-| `resize` | `Boolean` | `true` | Whether to resize the canvas |
-| `canvas` | `HTMLCanvasElement or null` | `null` | Custom canvas element to draw confetti |
-| `scalar` | `Number` | `1` | Scaling factor for confetti size |
+| Prop | Type | Default | Description |
+| --------------- | ------------------------ | ----------------------------------- | ---------------------------------------------------------------- |
+| `ref` | `React.Ref` | `null` | Ref exposing imperative `fire(options?: ConfettiOptions)` method |
+| `options` | `ConfettiOptions` | `{}` | Default options for automatic or imperative confetti triggers |
+| `globalOptions` | `ConfettiGlobalOptions` | `{ resize: true, useWorker: true }` | Global options passed to `canvas-confetti.create()` |
+| `manualstart` | `Boolean` | `false` | Prevents automatic confetti firing on component mount |
+| `className` | `String` | `undefined` | Custom CSS classes applied to the portaled canvas element |
+
+### ConfettiRef
+
+| Method | Parameters | Return Type | Description |
+| ------ | --------------------------- | ----------------------- | ------------------------------------------------------------- |
+| `fire` | `options?: ConfettiOptions` | `Promise \| void` | Imperatively triggers a confetti burst on the canvas instance |
+
+### ConfettiOptions
+
+| Field | Type | Default | Description |
+| --------------- | -------------------------- | ------------------------------ | ------------------------------------------------------- |
+| `particleCount` | `Integer` | `50` | Number of confetti particles to launch |
+| `angle` | `Number` | `90` | Launch angle in degrees |
+| `spread` | `Number` | `45` | Spread angle in degrees |
+| `startVelocity` | `Number` | `45` | Initial particle velocity |
+| `decay` | `Number` | `0.9` | Velocity decay rate |
+| `gravity` | `Number` | `1` | Gravity applied to particles |
+| `drift` | `Number` | `0` | Horizontal drift factor |
+| `flat` | `Boolean` | `false` | Renders flat 2D shapes |
+| `ticks` | `Number` | `200` | Number of animation frames confetti lasts |
+| `origin` | `{ x: number, y: number }` | `{ x: 0.5, y: 0.5 }` | Normalized viewport origin coordinates (`0.0` to `1.0`) |
+| `colors` | `Array of Strings` | `HEX array` | Custom HEX color palette |
+| `shapes` | `Array of Strings` | `['square', 'circle', 'star']` | Particle shapes or custom text shapes |
+| `zIndex` | `Integer` | `9999` | Z-index of the confetti burst |
+| `scalar` | `Number` | `1` | Scaling factor for particle dimensions |
### ConfettiButton
-| Prop | Type | Default | Description |
-| ---------- | ----------------- | ------- | ------------------------------------ |
-| `options` | `Object` | `{}` | Options for the confetti |
-| `children` | `React.ReactNode` | `null` | Children to render inside the button |
+| Prop | Type | Default | Description |
+| ---------- | ----------------------------------------- | ------- | ------------------------------------------------- |
+| `options` | `ConfettiOptions & ConfettiGlobalOptions` | `{}` | Options for the confetti launched on button click |
+| `children` | `React.ReactNode` | `null` | Button label and child elements |
+| `ref` | `React.Ref` | `null` | Forwarded DOM reference to the underlying button |
## Credits
diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt
index e8a1ce9b1..546b3d114 100644
--- a/apps/www/public/llms-full.txt
+++ b/apps/www/public/llms-full.txt
@@ -5160,8 +5160,8 @@ import confetti from "canvas-confetti"
import { Button } from "@/components/ui/button"
-type Api = {
- fire: (options?: ConfettiOptions) => void
+export type ConfettiRef = {
+ fire: (options?: ConfettiOptions) => Promise | void
}
type Props = React.ComponentPropsWithRef<"canvas"> & {
@@ -5171,122 +5171,123 @@ type Props = React.ComponentPropsWithRef<"canvas"> & {
children?: ReactNode
}
-export type ConfettiRef = Api | null
+const ConfettiContext = createContext(null)
-const ConfettiContext = createContext({} as Api)
-
-// Define component first
const ConfettiComponent = forwardRef((props, ref) => {
const {
options,
globalOptions = { resize: true, useWorker: true },
manualstart = false,
children,
+ className,
...rest
} = props
+
+ const canvasNodeRef = useRef(null)
const instanceRef = useRef(null)
+ const optionsRef = useRef(options)
+ const globalOptionsRef = useRef(globalOptions)
- const canvasRef = useCallback(
- (node: HTMLCanvasElement) => {
- if (node !== null) {
- if (instanceRef.current) return
- instanceRef.current = confetti.create(node, {
- ...globalOptions,
- resize: true,
- })
- } else {
- if (instanceRef.current) {
- instanceRef.current.reset()
- instanceRef.current = null
- }
- }
- },
- [globalOptions]
- )
+ useEffect(() => {
+ optionsRef.current = options
+ }, [options])
- const fire = useCallback(
- async (opts = {}) => {
- try {
- await instanceRef.current?.({ ...options, ...opts })
- } catch (error) {
- console.error("Confetti error:", error)
- }
- },
- [options]
- )
+ useEffect(() => {
+ globalOptionsRef.current = globalOptions
+ }, [globalOptions])
- const api = useMemo(
- () => ({
- fire,
- }),
- [fire]
- )
+ useEffect(() => {
+ if (canvasNodeRef.current && !instanceRef.current) {
+ instanceRef.current = confetti.create(canvasNodeRef.current, {
+ resize: true,
+ useWorker: true,
+ ...globalOptionsRef.current,
+ })
+ }
+
+ return () => {
+ instanceRef.current?.reset()
+ instanceRef.current = null
+ }
+ }, [])
+
+ const fire = useCallback(async (opts: ConfettiOptions = {}) => {
+ try {
+ await instanceRef.current?.({
+ zIndex: 9999,
+ ...optionsRef.current,
+ ...opts,
+ })
+ } catch (error) {
+ console.error("Confetti error:", error)
+ }
+ }, [])
+
+ const api = useMemo(() => ({ fire }), [fire])
useImperativeHandle(ref, () => api, [api])
useEffect(() => {
if (!manualstart) {
- ;(async () => {
- try {
- await fire()
- } catch (error) {
- console.error("Confetti effect error:", error)
- }
- })()
+ void fire()
}
}, [manualstart, fire])
return (
-
+
{children}
)
})
-// Set display name immediately
ConfettiComponent.displayName = "Confetti"
-// Export as Confetti
export const Confetti = ConfettiComponent
-interface ConfettiButtonProps extends React.ComponentProps<"button"> {
+export interface ConfettiButtonProps extends React.ComponentPropsWithoutRef<
+ typeof Button
+> {
options?: ConfettiOptions &
ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }
}
-const ConfettiButtonComponent = ({
- options,
- children,
- ...props
-}: ConfettiButtonProps) => {
- const handleClick = async (event: React.MouseEvent) => {
+export const ConfettiButton = forwardRef<
+ HTMLButtonElement,
+ ConfettiButtonProps
+>(({ options, children, onClick, ...props }, ref) => {
+ const handleClick: ConfettiButtonProps["onClick"] = async (event) => {
try {
- const rect = event.currentTarget.getBoundingClientRect()
- const x = rect.left + rect.width / 2
- const y = rect.top + rect.height / 2
- await confetti({
- ...options,
- origin: {
- x: x / window.innerWidth,
- y: y / window.innerHeight,
- },
- })
+ onClick?.(event)
+ if (event?.defaultPrevented) return
+
+ const target = event?.currentTarget
+ if (target && "getBoundingClientRect" in target) {
+ const rect = target.getBoundingClientRect()
+ const origin = {
+ x: (rect.left + rect.width / 2) / window.innerWidth,
+ y: (rect.top + rect.height / 2) / window.innerHeight,
+ }
+
+ await confetti({
+ zIndex: 9999,
+ ...options,
+ origin,
+ })
+ }
} catch (error) {
console.error("Confetti button error:", error)
}
}
return (
-