{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "burst", "registryDependencies": [ "https://powui.dev/r/utils.json", "https://powui.dev/r/geometry-utils.json", "https://powui.dev/r/theme.json" ], "files": [ { "path": "src/components/ui/burst.tsx", "content": "\"use client\";\n\nimport { getEllipticalPoints, getRectangularPoints } from \"@/lib/geometryUtils\";\nimport { cn } from \"@/lib/utils\";\nimport { useMemo, useState, type PropsWithChildren } from \"react\";\n\nconst HEIGHT_VARIANCE = 15;\nconst FLATTERY_FACTOR = 2;\n\nconst generateSVGPathBAM = (args: {\n points: { x: number; y: number }[];\n height: number;\n width: number;\n flatteryFactor: number;\n curvedDips?: boolean;\n}) => {\n const { points, height, width, flatteryFactor } = args;\n const path = [...points, points[0]].reduce((acc, point, index) => {\n if (index === 0) {\n return `M ${point.x / width} ${point.y / height}`;\n } else {\n const previousPoint = points[index - 1];\n const referencePoint = {\n x:\n (width / 2 + flatteryFactor * ((point.x + previousPoint.x) / 2)) /\n (1 + flatteryFactor),\n y:\n (height / 2 + flatteryFactor * ((point.y + previousPoint.y) / 2)) /\n (1 + flatteryFactor),\n };\n const relativePoints = {\n refX: referencePoint.x / width,\n refY: referencePoint.y / height,\n x: point.x / width,\n y: point.y / height,\n };\n if (args.curvedDips) {\n return `${acc} Q ${relativePoints.refX} ${relativePoints.refY}, ${relativePoints.x} ${relativePoints.y}`;\n }\n return `${acc} L ${relativePoints.refX} ${relativePoints.refY} L ${relativePoints.x} ${relativePoints.y}`;\n }\n }, \"\");\n\n return `${path} Z`;\n};\n\nconst getPointsAndPath = (args: {\n height: number;\n width: number;\n heightVariance: number;\n peakSeparation: number;\n flatteryFactor: number;\n huggingStyle: \"elliptical\" | \"rectangular\";\n curvedDips?: boolean;\n}) => {\n const {\n height,\n width,\n heightVariance,\n peakSeparation,\n flatteryFactor,\n curvedDips,\n } = args;\n if (height === 0 || width === 0) {\n return { points: [], path: \"\" };\n }\n\n let points: { x: number; y: number }[] = [];\n if (args.huggingStyle === \"rectangular\") {\n points = getRectangularPoints({\n height,\n width,\n peakSeparation,\n heightVariance,\n });\n } else {\n points = getEllipticalPoints({\n height,\n width,\n peakSeparation,\n heightVariance,\n });\n }\n const path = generateSVGPathBAM({\n points,\n height,\n width,\n flatteryFactor,\n curvedDips,\n });\n return { points, path };\n};\n\ntype TBurstWrapperProps = {\n heightVariance?: number;\n // peakSeparation is used to determine distance between each peak\n peakSeparation?: number;\n // change this prop name at some point\n // flatteryFactor is a factor that determines how much the points are \"flattened\"\n flatteryFactor?: number;\n // huggingStyle determines the shape of the burst\n huggingStyle?: \"elliptical\" | \"rectangular\";\n // curvedDips determines if the dips between peaks are curved or not\n curvedDips?: boolean;\n // borders determines the appearance of the borders around the burst\n borders?: {\n scale?: number;\n color?: CSSStyleDeclaration[\"backgroundColor\"];\n }[];\n // containerClassName is a class name to apply to the container div\n containerClassName?: string;\n // className is applied to the inner clipped content div\n className?: string;\n};\n\nconst BurstWrapper = (props: PropsWithChildren) => {\n const {\n children,\n heightVariance = HEIGHT_VARIANCE,\n flatteryFactor = FLATTERY_FACTOR,\n huggingStyle = \"elliptical\",\n curvedDips = false,\n borders = [],\n containerClassName,\n className,\n } = props;\n\n const [containerRef, setContainerRef] = useState(null);\n\n const { height = 0, width = 0 } = containerRef?.getBoundingClientRect() ?? {};\n\n const peakSeparation = props.peakSeparation ?? (height + width) / 10;\n\n const svgPath = useMemo(() => {\n return getPointsAndPath({\n height,\n width,\n heightVariance,\n peakSeparation,\n flatteryFactor,\n huggingStyle,\n curvedDips,\n });\n }, [\n height,\n width,\n heightVariance,\n peakSeparation,\n flatteryFactor,\n huggingStyle,\n curvedDips,\n ]);\n\n const id = window.crypto.randomUUID();\n const pathId = `path_${id}`;\n\n const borderDivs = useMemo(() => {\n if (!borders) return null;\n return borders\n .slice()\n .reverse()\n .map((config, index) => (\n \n ));\n }, [borders, pathId]);\n\n return (\n
\n \n \n \n \n \n \n \n {borderDivs}\n \n {children}\n
\n \n );\n};\n\nexport { BurstWrapper, type TBurstWrapperProps };\n", "type": "registry:ui" } ], "type": "registry:ui" }