diff --git a/src/components/MassSlider/MassSlider.tsx b/src/components/MassSlider/MassSlider.tsx index fdeb29f..16eaa2d 100644 --- a/src/components/MassSlider/MassSlider.tsx +++ b/src/components/MassSlider/MassSlider.tsx @@ -1,6 +1,7 @@ import React from "react"; import { motion } from "framer-motion"; import { StarRenderer } from "../StarRenderer/StarRenderer"; +import { massToPercentage, percentageToMass } from "../../utils/mass/massUtils"; interface MassSliderProps { value: number; @@ -9,7 +10,6 @@ interface MassSliderProps { orientation?: "vertical" | "horizontal"; onDragStart?: () => void; onDragEnd?: () => void; - label?: string; } export const MassSlider: React.FC = ({ @@ -19,27 +19,11 @@ export const MassSlider: React.FC = ({ orientation = "horizontal", onDragStart, onDragEnd, - label, }) => { const sliderRef = React.useRef(null); const padding = 20; const innerLength = length - padding * 2; - // Constants for mass calculation - const MIN_MASS = 1; - const MAX_MASS = 2500000; - const EXPONENT = 4; - - const percentageToMass = (percentage: number): number => { - const exponentialValue = Math.pow(percentage, EXPONENT); - return MIN_MASS + (MAX_MASS - MIN_MASS) * exponentialValue; - }; - - const massToPercentage = (mass: number): number => { - const normalizedMass = (mass - MIN_MASS) / (MAX_MASS - MIN_MASS); - return Math.pow(normalizedMass, 1 / EXPONENT); - }; - // Calculate initial position based on current value const initialPercentage = massToPercentage(value); const isVertical = orientation === "vertical"; @@ -47,50 +31,21 @@ export const MassSlider: React.FC = ({ // Calculate the initial position relative to the center of the slider const initialPosition = initialPercentage * innerLength - innerLength / 2; - const formatMass = (mass: number): string => { - if (mass >= 1000000) { - return `${(mass / 1000000).toFixed(1)}M`; - } else if (mass >= 1000) { - return `${(mass / 1000).toFixed(1)}K`; - } - return mass.toFixed(0); - }; - - // Add this function to determine star type based on mass - const getStarType = (mass: number): string => { - if (mass < 1000) return "Brown Dwarf"; - if (mass < 20000) return "Red Dwarf"; - if (mass < 200000) return "Main Sequence"; - if (mass < 1000000) return "Red Giant"; - return "Super Giant"; - }; - return (
-
- {formatMass(value)} -
{label || getStarType(value)}
-
void; @@ -22,7 +23,7 @@ export const StarPalette: React.FC = ({ forceHover = false, }) => { const [starMasses, setStarMasses] = useState<{ [key: number]: number }>({}); - const [isPaletteHovered, setIsPaletteHovered] = useState(forceHover); + const [isDragging, setIsDragging] = useState(false); const handleStarMassChange = (index: number, mass: number) => { setStarMasses((prev) => ({ @@ -32,64 +33,121 @@ export const StarPalette: React.FC = ({ }; return ( -
e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} onTouchStart={(e) => e.stopPropagation()} - onMouseEnter={() => setIsPaletteHovered(true)} - onMouseLeave={() => setIsPaletteHovered(false)} className="floating-panel star-palette" + whileHover={{ + width: "400px", + height: "60px", + }} + transition={{ duration: 0.3, ease: "easeOut" }} + style={{ + overflow: isDragging ? "visible" : "hidden", + width: isDragging ? "400px" : "40px", + height: isDragging ? "60px" : "40px", + }} > -
+
{STAR_TEMPLATES.map((template, index) => (
- - onStarDragStart({ - ...template, - mass: starMasses[index] || template.mass, - }) - } - onDragEnd={(e) => - onStarDragEnd( - { - ...template, - mass: starMasses[index] || template.mass, - }, - e - ) - } +
- - + { + setIsDragging(true); + onStarDragStart({ + ...template, + mass: starMasses[index] || template.mass, + }); + }} + onDragEnd={(e) => { + setTimeout(() => { + setIsDragging(false); + }, 900); + onStarDragEnd( + { + ...template, + mass: starMasses[index] || template.mass, + }, + e + ); + }} + style={{ + width: "40px", + height: "40px", + display: "flex", + alignItems: "center", + justifyContent: "center", + cursor: "grab", + position: "relative", + touchAction: "none", + }} + > + + + + { + + + {formatMass(starMasses[index] || template.mass)} + + {getStarType(starMasses[index])} + + } + +
+ - {(forceHover || isPaletteHovered) && ( + { = ({ height: "40px", display: "flex", alignItems: "center", - overflow: "hidden", }} > = ({ onChange={(value) => handleStarMassChange(index, value)} /> - )} + }
))}
-
+ ); }; diff --git a/src/styles/theme.scss b/src/styles/theme.scss index 93cb17f..89711fc 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -40,12 +40,11 @@ .star-palette { position: absolute; left: 2%; - top: 50%; - transform: translateY(-50%); + top: 2%; + transform: none; display: flex; flex-direction: row; gap: 20px; - padding: 5px; } .action-button { diff --git a/src/utils/mass/massUtils.ts b/src/utils/mass/massUtils.ts new file mode 100644 index 0000000..26aad4e --- /dev/null +++ b/src/utils/mass/massUtils.ts @@ -0,0 +1,31 @@ +// Constants for mass calculation +export const MIN_MASS = 1; +export const MAX_MASS = 2500000; +export const EXPONENT = 4; + +export const percentageToMass = (percentage: number): number => { + const exponentialValue = Math.pow(percentage, EXPONENT); + return MIN_MASS + (MAX_MASS - MIN_MASS) * exponentialValue; +}; + +export const massToPercentage = (mass: number): number => { + const normalizedMass = (mass - MIN_MASS) / (MAX_MASS - MIN_MASS); + return Math.pow(normalizedMass, 1 / EXPONENT); +}; + +export const formatMass = (mass: number): string => { + if (mass >= 1000000) { + return `${(mass / 1000000).toFixed(1)}M`; + } else if (mass >= 1000) { + return `${(mass / 1000).toFixed(1)}K`; + } + return mass.toFixed(0); +}; + +export const getStarType = (mass: number): string => { + if (mass < 1000) return "Brown Dwarf"; + if (mass < 20000) return "Red Dwarf"; + if (mass < 200000) return "Main Sequence"; + if (mass < 1000000) return "Red Giant"; + return "Super Giant"; +};