UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

192 lines 9.34 kB
"use strict"; "use client"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const cobe_1 = __importDefault(require("cobe")); const utils_1 = require("@/components/lib/utils"); // Assuming cn utility is available for Tailwind classes // Utility function to convert a hex color string to a normalized RGB array // Handles #RGB and #RRGGBB formats. const hexToRgbNormalized = (hex) => { let r = 0, g = 0, b = 0; // Remove the # if present const cleanHex = hex.startsWith("#") ? hex.slice(1) : hex; if (cleanHex.length === 3) { // Handle shorthand hex codes (e.g., #00F -> #0000FF) r = parseInt(cleanHex[0] + cleanHex[0], 16); g = parseInt(cleanHex[1] + cleanHex[1], 16); b = parseInt(cleanHex[2] + cleanHex[2], 16); } else if (cleanHex.length === 6) { // Handle full hex codes (e.g., #RRGGBB) r = parseInt(cleanHex.substring(0, 2), 16); g = parseInt(cleanHex.substring(2, 4), 16); b = parseInt(cleanHex.substring(4, 6), 16); } else { // Fallback for invalid hex (or if you want to throw an error) console.warn(`Invalid hex color: ${hex}. Falling back to black.`); return [0, 0, 0]; } // Normalize to 0-1 range return [r / 255, g / 255, b / 255]; }; const Globe = ({ className, theta = 0.25, dark = 0, scale = 1.1, diffuse = 1.2, mapSamples = 60000, mapBrightness = 10, baseColor = "#ffffff", // Removed default here, handled in useEffect markerColor = "#ffffff", // Removed default here glowColor = "#ffffff", // Removed default here }) => { const canvasRef = (0, react_1.useRef)(null); const globeRef = (0, react_1.useRef)(null); // To store the cobe globe instance // Refs for interactive rotation and dragging state const phiRef = (0, react_1.useRef)(0); const thetaRef = (0, react_1.useRef)(theta); // Initialize thetaRef with prop theta const isDragging = (0, react_1.useRef)(false); const lastMouseX = (0, react_1.useRef)(0); const lastMouseY = (0, react_1.useRef)(0); const autoRotateSpeed = 0.003; // Define auto-rotation speed (0, react_1.useEffect)(() => { const canvas = canvasRef.current; if (!canvas) return; // Resolve color props to the [R, G, B] format required by cobe const resolvedBaseColor = typeof baseColor === "string" ? hexToRgbNormalized(baseColor) : baseColor || [0.4, 0.6509, 1]; // Default if not provided or invalid hex const resolvedMarkerColor = typeof markerColor === "string" ? hexToRgbNormalized(markerColor) : markerColor || [1, 0, 0]; // Default if not provided or invalid hex const resolvedGlowColor = typeof glowColor === "string" ? hexToRgbNormalized(glowColor) : glowColor || [0.2745, 0.5765, 0.898]; // Default if not provided or invalid hex const initGlobe = () => { // Destroy existing globe instance if it exists to prevent multiple instances if (globeRef.current) { globeRef.current.destroy(); globeRef.current = null; } const rect = canvas.getBoundingClientRect(); const size = Math.min(rect.width, rect.height); const devicePixelRatio = window.devicePixelRatio || 1; const internalWidth = size * devicePixelRatio; const internalHeight = size * devicePixelRatio; canvas.width = internalWidth; canvas.height = internalHeight; globeRef.current = cobe_1.default(canvas, { devicePixelRatio: devicePixelRatio, width: internalWidth, height: internalHeight, phi: phiRef.current, theta: thetaRef.current, // Use thetaRef for initial and interactive theta dark: dark, scale: scale, diffuse: diffuse, mapSamples: mapSamples, mapBrightness: mapBrightness, baseColor: resolvedBaseColor, // Use converted/resolved colors markerColor: resolvedMarkerColor, // Use converted/resolved colors glowColor: resolvedGlowColor, // Use converted/resolved colors opacity: 1, offset: [0, 0], markers: [], onRender: (state) => { if (!isDragging.current) { // Only auto-rotate if not currently dragging phiRef.current += autoRotateSpeed; } state.phi = phiRef.current; state.theta = thetaRef.current; // Ensure cobe uses the updated thetaRef }, }); }; // --- Mouse Interaction Handlers --- const onMouseDown = (e) => { isDragging.current = true; lastMouseX.current = e.clientX; lastMouseY.current = e.clientY; canvas.style.cursor = "grabbing"; // Change cursor to indicate dragging }; const onMouseMove = (e) => { if (isDragging.current) { const deltaX = e.clientX - lastMouseX.current; const deltaY = e.clientY - lastMouseY.current; // Adjust rotation sensitivity as needed const rotationSpeed = 0.005; // Update phi (horizontal rotation) phiRef.current += deltaX * rotationSpeed; // Update theta (vertical rotation), clamp to prevent flipping // Clamped between -PI/2 and PI/2 to prevent globe from going upside down thetaRef.current = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, thetaRef.current - deltaY * rotationSpeed)); lastMouseX.current = e.clientX; lastMouseY.current = e.clientY; } }; const onMouseUp = () => { isDragging.current = false; canvas.style.cursor = "grab"; // Change cursor back }; const onMouseLeave = () => { // If mouse leaves canvas while dragging, stop dragging if (isDragging.current) { isDragging.current = false; canvas.style.cursor = "grab"; } }; // --- End Mouse Interaction Handlers --- initGlobe(); // Attach event listeners for mouse interaction canvas.addEventListener("mousedown", onMouseDown); canvas.addEventListener("mousemove", onMouseMove); canvas.addEventListener("mouseup", onMouseUp); canvas.addEventListener("mouseleave", onMouseLeave); // Important for when mouse leaves canvas during a drag const handleResize = () => { initGlobe(); }; window.addEventListener("resize", handleResize); // Cleanup function: destroy the globe instance and remove event listeners when component unmounts return () => { window.removeEventListener("resize", handleResize); // Remove mouse event listeners on cleanup if (canvas) { canvas.removeEventListener("mousedown", onMouseDown); canvas.removeEventListener("mousemove", onMouseMove); canvas.removeEventListener("mouseup", onMouseUp); canvas.removeEventListener("mouseleave", onMouseLeave); } if (globeRef.current) { globeRef.current.destroy(); globeRef.current = null; } }; }, [ theta, dark, scale, diffuse, mapSamples, mapBrightness, baseColor, // Include color props in dependency array so globe re-initializes if they change markerColor, glowColor, ]); return ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("flex items-center justify-center z-[10] mx-auto", className), style: { width: "auto", height: "auto", // Container takes full viewport height display: "flex", // Ensure flexbox properties are active for centering alignItems: "center", justifyContent: "center", overflow: "hidden", // Prevent scrollbars if content overflows }, children: (0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef, style: { width: "20rem", // Canvas takes full width of its parent (which is constrained) height: "20rem", // Canvas takes full height of its parent (which is constrained) maxWidth: "auto", // Limit max width to viewport height to ensure square aspect in landscape maxHeight: "auto", // Limit max height to viewport width to ensure square aspect in portrait aspectRatio: "1", // Force a 1:1 aspect ratio for the canvas element display: "block", // Ensure canvas behaves as a block element cursor: "grab", // Default cursor } }) })); }; exports.default = Globe; //# sourceMappingURL=globe.js.map