Skip to content
Open
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
2 changes: 1 addition & 1 deletion public/r/GlassSurface-JS-CSS.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{
"type": "registry:component",
"path": "GlassSurface/GlassSurface.jsx",
"content": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { useEffect, useState, useRef, useId } from 'react';\nimport './GlassSurface.css';\n\nconst GlassSurface = ({\n children,\n width = 200,\n height = 80,\n borderRadius = 20,\n borderWidth = 0.07,\n brightness = 50,\n opacity = 0.93,\n blur = 11,\n displace = 0,\n backgroundOpacity = 0,\n saturation = 1,\n distortionScale = -180,\n redOffset = 0,\n greenOffset = 10,\n blueOffset = 20,\n xChannel = 'R',\n yChannel = 'G',\n mixBlendMode = 'difference',\n className = '',\n style = {}\n}) => {\n const uniqueId = useId().replace(/:/g, '-');\n const filterId = `glass-filter-${uniqueId}`;\n const redGradId = `red-grad-${uniqueId}`;\n const blueGradId = `blue-grad-${uniqueId}`;\n\n const [svgSupported, setSvgSupported] = useState(false);\n\n const containerRef = useRef(null);\n const feImageRef = useRef(null);\n const redChannelRef = useRef(null);\n const greenChannelRef = useRef(null);\n const blueChannelRef = useRef(null);\n const gaussianBlurRef = useRef(null);\n\n const generateDisplacementMap = () => {\n const rect = containerRef.current?.getBoundingClientRect();\n const actualWidth = rect?.width || 400;\n const actualHeight = rect?.height || 200;\n const edgeSize = Math.min(actualWidth, actualHeight) * (borderWidth * 0.5);\n\n const svgContent = `\n <svg viewBox=\"0 0 ${actualWidth} ${actualHeight}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"${redGradId}\" x1=\"100%\" y1=\"0%\" x2=\"0%\" y2=\"0%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"red\"/>\n </linearGradient>\n <linearGradient id=\"${blueGradId}\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"blue\"/>\n </linearGradient>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" fill=\"black\"></rect>\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" rx=\"${borderRadius}\" fill=\"url(#${redGradId})\" />\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" rx=\"${borderRadius}\" fill=\"url(#${blueGradId})\" style=\"mix-blend-mode: ${mixBlendMode}\" />\n <rect x=\"${edgeSize}\" y=\"${edgeSize}\" width=\"${actualWidth - edgeSize * 2}\" height=\"${actualHeight - edgeSize * 2}\" rx=\"${borderRadius}\" fill=\"hsl(0 0% ${brightness}% / ${opacity})\" style=\"filter:blur(${blur}px)\" />\n </svg>\n `;\n\n return `data:image/svg+xml,${encodeURIComponent(svgContent)}`;\n };\n\n const updateDisplacementMap = () => {\n feImageRef.current?.setAttribute('href', generateDisplacementMap());\n };\n\n useEffect(() => {\n updateDisplacementMap();\n [\n { ref: redChannelRef, offset: redOffset },\n { ref: greenChannelRef, offset: greenOffset },\n { ref: blueChannelRef, offset: blueOffset }\n ].forEach(({ ref, offset }) => {\n if (ref.current) {\n ref.current.setAttribute('scale', (distortionScale + offset).toString());\n ref.current.setAttribute('xChannelSelector', xChannel);\n ref.current.setAttribute('yChannelSelector', yChannel);\n }\n });\n\n gaussianBlurRef.current?.setAttribute('stdDeviation', displace.toString());\n }, [\n width,\n height,\n borderRadius,\n borderWidth,\n brightness,\n opacity,\n blur,\n displace,\n distortionScale,\n redOffset,\n greenOffset,\n blueOffset,\n xChannel,\n yChannel,\n mixBlendMode\n ]);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDisplacementMap, 0);\n });\n\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDisplacementMap, 0);\n });\n\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n useEffect(() => {\n setTimeout(updateDisplacementMap, 0);\n }, [width, height]);\n\n useEffect(() => {\n setSvgSupported(supportsSVGFilters());\n }, []);\n\n const supportsSVGFilters = () => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return false;\n }\n\n const isWebkit = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);\n const isFirefox = /Firefox/.test(navigator.userAgent);\n\n if (isWebkit || isFirefox) {\n return false;\n }\n\n const div = document.createElement('div');\n div.style.backdropFilter = `url(#${filterId})`;\n\n return div.style.backdropFilter !== '';\n };\n\n const containerStyle = {\n ...style,\n width: typeof width === 'number' ? `${width}px` : width,\n height: typeof height === 'number' ? `${height}px` : height,\n borderRadius: `${borderRadius}px`,\n '--glass-frost': backgroundOpacity,\n '--glass-saturation': saturation,\n '--filter-id': `url(#${filterId})`\n };\n\n return (\n <div\n ref={containerRef}\n className={`glass-surface ${svgSupported ? 'glass-surface--svg' : 'glass-surface--fallback'} ${className}`}\n style={containerStyle}\n >\n <svg className=\"glass-surface__filter\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <filter id={filterId} colorInterpolationFilters=\"sRGB\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n <feImage ref={feImageRef} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"none\" result=\"map\" />\n\n <feDisplacementMap ref={redChannelRef} in=\"SourceGraphic\" in2=\"map\" id=\"redchannel\" result=\"dispRed\" />\n <feColorMatrix\n in=\"dispRed\"\n type=\"matrix\"\n values=\"1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"red\"\n />\n\n <feDisplacementMap\n ref={greenChannelRef}\n in=\"SourceGraphic\"\n in2=\"map\"\n id=\"greenchannel\"\n result=\"dispGreen\"\n />\n <feColorMatrix\n in=\"dispGreen\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"green\"\n />\n\n <feDisplacementMap ref={blueChannelRef} in=\"SourceGraphic\" in2=\"map\" id=\"bluechannel\" result=\"dispBlue\" />\n <feColorMatrix\n in=\"dispBlue\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0\"\n result=\"blue\"\n />\n\n <feBlend in=\"red\" in2=\"green\" mode=\"screen\" result=\"rg\" />\n <feBlend in=\"rg\" in2=\"blue\" mode=\"screen\" result=\"output\" />\n <feGaussianBlur ref={gaussianBlurRef} in=\"output\" stdDeviation=\"0.7\" />\n </filter>\n </defs>\n </svg>\n\n <div className=\"glass-surface__content\">{children}</div>\n </div>\n );\n};\n\nexport default GlassSurface;\n"
"content": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { useEffect, useState, useRef, useId } from 'react';\nimport './GlassSurface.css';\n\nconst GlassSurface = ({\n children,\n width = 200,\n height = 80,\n borderRadius = 20,\n borderWidth = 0.07,\n brightness = 50,\n opacity = 0.93,\n blur = 11,\n displace = 0,\n backgroundOpacity = 0,\n saturation = 1,\n distortionScale = -180,\n redOffset = 0,\n greenOffset = 10,\n blueOffset = 20,\n xChannel = 'R',\n yChannel = 'G',\n mixBlendMode = 'difference',\n className = '',\n style = {}\n}) => {\n const uniqueId = useId().replace(/:/g, '-');\n const filterId = `glass-filter-${uniqueId}`;\n const redGradId = `red-grad-${uniqueId}`;\n const blueGradId = `blue-grad-${uniqueId}`;\n\n const [svgSupported, setSvgSupported] = useState(false);\n\n const containerRef = useRef(null);\n const feImageRef = useRef(null);\n const redChannelRef = useRef(null);\n const greenChannelRef = useRef(null);\n const blueChannelRef = useRef(null);\n const gaussianBlurRef = useRef(null);\n\n const generateDisplacementMap = () => {\n const rect = containerRef.current?.getBoundingClientRect();\n const actualWidth = rect?.width || 400;\n const actualHeight = rect?.height || 200;\n const edgeSize = Math.min(actualWidth, actualHeight) * (borderWidth * 0.5);\n\n const svgContent = `\n <svg viewBox=\"0 0 ${actualWidth} ${actualHeight}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"${redGradId}\" x1=\"100%\" y1=\"0%\" x2=\"0%\" y2=\"0%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"red\"/>\n </linearGradient>\n <linearGradient id=\"${blueGradId}\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n <stop offset=\"0%\" stop-color=\"#0000\"/>\n <stop offset=\"100%\" stop-color=\"blue\"/>\n </linearGradient>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" fill=\"black\"></rect>\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" rx=\"${borderRadius}\" fill=\"url(#${redGradId})\" />\n <rect x=\"0\" y=\"0\" width=\"${actualWidth}\" height=\"${actualHeight}\" rx=\"${borderRadius}\" fill=\"url(#${blueGradId})\" style=\"mix-blend-mode: ${mixBlendMode}\" />\n <rect x=\"${edgeSize}\" y=\"${edgeSize}\" width=\"${actualWidth - edgeSize * 2}\" height=\"${actualHeight - edgeSize * 2}\" rx=\"${borderRadius}\" fill=\"hsl(0 0% ${brightness}% / ${opacity})\" style=\"filter:blur(${blur}px)\" />\n </svg>\n `;\n\n return `data:image/svg+xml,${encodeURIComponent(svgContent)}`;\n };\n\n const updateDisplacementMap = () => {\n feImageRef.current?.setAttribute('href', generateDisplacementMap());\n };\n\n useEffect(() => {\n updateDisplacementMap();\n [\n { ref: redChannelRef, offset: redOffset },\n { ref: greenChannelRef, offset: greenOffset },\n { ref: blueChannelRef, offset: blueOffset }\n ].forEach(({ ref, offset }) => {\n if (ref.current) {\n ref.current.setAttribute('scale', (distortionScale + offset).toString());\n ref.current.setAttribute('xChannelSelector', xChannel);\n ref.current.setAttribute('yChannelSelector', yChannel);\n }\n });\n\n gaussianBlurRef.current?.setAttribute('stdDeviation', displace.toString());\n }, [\n width,\n height,\n borderRadius,\n borderWidth,\n brightness,\n opacity,\n blur,\n displace,\n distortionScale,\n redOffset,\n greenOffset,\n blueOffset,\n xChannel,\n yChannel,\n mixBlendMode\n ]);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDisplacementMap, 0);\n });\n\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n useEffect(() => {\n setTimeout(updateDisplacementMap, 0);\n }, [width, height]);\n\n useEffect(() => {\n setSvgSupported(supportsSVGFilters());\n }, []);\n\n const supportsSVGFilters = () => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return false;\n }\n\n const isWebkit = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);\n const isFirefox = /Firefox/.test(navigator.userAgent);\n\n if (isWebkit || isFirefox) {\n return false;\n }\n\n const div = document.createElement('div');\n div.style.backdropFilter = `url(#${filterId})`;\n\n return div.style.backdropFilter !== '';\n };\n\n const containerStyle = {\n ...style,\n width: typeof width === 'number' ? `${width}px` : width,\n height: typeof height === 'number' ? `${height}px` : height,\n borderRadius: `${borderRadius}px`,\n '--glass-frost': backgroundOpacity,\n '--glass-saturation': saturation,\n '--filter-id': `url(#${filterId})`\n };\n\n return (\n <div\n ref={containerRef}\n className={`glass-surface ${svgSupported ? 'glass-surface--svg' : 'glass-surface--fallback'} ${className}`}\n style={containerStyle}\n >\n <svg className=\"glass-surface__filter\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <filter id={filterId} colorInterpolationFilters=\"sRGB\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n <feImage ref={feImageRef} x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" preserveAspectRatio=\"none\" result=\"map\" />\n\n <feDisplacementMap ref={redChannelRef} in=\"SourceGraphic\" in2=\"map\" id=\"redchannel\" result=\"dispRed\" />\n <feColorMatrix\n in=\"dispRed\"\n type=\"matrix\"\n values=\"1 0 0 0 0\n 0 0 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"red\"\n />\n\n <feDisplacementMap\n ref={greenChannelRef}\n in=\"SourceGraphic\"\n in2=\"map\"\n id=\"greenchannel\"\n result=\"dispGreen\"\n />\n <feColorMatrix\n in=\"dispGreen\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 1 0 0 0\n 0 0 0 0 0\n 0 0 0 1 0\"\n result=\"green\"\n />\n\n <feDisplacementMap ref={blueChannelRef} in=\"SourceGraphic\" in2=\"map\" id=\"bluechannel\" result=\"dispBlue\" />\n <feColorMatrix\n in=\"dispBlue\"\n type=\"matrix\"\n values=\"0 0 0 0 0\n 0 0 0 0 0\n 0 0 1 0 0\n 0 0 0 1 0\"\n result=\"blue\"\n />\n\n <feBlend in=\"red\" in2=\"green\" mode=\"screen\" result=\"rg\" />\n <feBlend in=\"rg\" in2=\"blue\" mode=\"screen\" result=\"output\" />\n <feGaussianBlur ref={gaussianBlurRef} in=\"output\" stdDeviation=\"0.7\" />\n </filter>\n </defs>\n </svg>\n\n <div className=\"glass-surface__content\">{children}</div>\n </div>\n );\n};\n\nexport default GlassSurface;\n"
}
],
"registryDependencies": [],
Expand Down
Loading