{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "cloud", "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/cloud.tsx", "content": "\"use client\";\n\nimport type React from \"react\";\nimport { getEllipticalPoints, getRectangularPoints } from \"@/lib/geometryUtils\";\nimport { useCallback, useMemo, useState } from \"react\";\n\nconst HEIGHT_VARIANCE = 15;\nconst FLATTERY_FACTOR = 2;\n\nconst generateSVGPathCloud = (args: {\n points: { x: number; y: number }[];\n height: number;\n width: number;\n flatteryFactor: number;\n}) => {\n const { points, height, width, flatteryFactor } = args;\n\n let firstTrough = { x: 0, y: 0 };\n const path = points.reduce((acc, point, index) => {\n const nextPoint = points[index + 1] || points[0];\n const trough = {\n x:\n (width / 2 + flatteryFactor * ((point.x + nextPoint.x) / 2)) /\n (1 + flatteryFactor),\n y:\n (height / 2 + flatteryFactor * ((point.y + nextPoint.y) / 2)) /\n (1 + flatteryFactor),\n };\n\n if (index === 0) {\n firstTrough = trough;\n return `M ${trough.x} ${trough.y}`;\n } else {\n return `${acc} Q ${point.x} ${point.y}, ${trough.x} ${trough.y}`;\n }\n }, \"\");\n\n return `${path} Q ${points[0].x} ${points[0].y}, ${firstTrough.x} ${firstTrough.y} 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}) => {\n const {\n height,\n width,\n heightVariance,\n peakSeparation,\n flatteryFactor,\n huggingStyle,\n } = args;\n if (height === 0 || width === 0) {\n return { points: [], path: \"\" };\n }\n\n let points: { x: number; y: number }[] = [];\n if (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 = generateSVGPathCloud({ points, height, width, flatteryFactor });\n return { points, path };\n};\n\ntype TCloudWrapperProps = {\n children: React.ReactNode;\n // heightVariance determines how low each peak can be\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 cloud\n huggingStyle?: \"elliptical\" | \"rectangular\";\n};\n\nconst CloudWrapper = (props: TCloudWrapperProps) => {\n const {\n children,\n heightVariance = HEIGHT_VARIANCE,\n flatteryFactor = FLATTERY_FACTOR,\n huggingStyle = \"elliptical\",\n } = props;\n\n const [containerRef, setContainerRef] = useState(null);\n const loadRef = useCallback((node: HTMLDivElement | null) => {\n setContainerRef(node);\n }, []);\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 });\n }, [height, width, heightVariance, peakSeparation, flatteryFactor]);\n\n return (\n
\n {children}\n
\n );\n};\n\nexport { CloudWrapper, type TCloudWrapperProps };\n", "type": "registry:ui" } ], "type": "registry:ui" }