diff --git a/src/AdvancedCalculator.js b/src/AdvancedCalculator.js index a13914f..8e8c4ec 100644 --- a/src/AdvancedCalculator.js +++ b/src/AdvancedCalculator.js @@ -1,22 +1,37 @@ -import React, { useState, useEffect } from 'react'; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, ScatterChart, Scatter } from 'recharts'; -import * as math from 'mathjs'; -import _ from 'lodash'; -import './AdvancedCalculatorStyles.css'; +import React, { useState, useEffect } from "react"; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ScatterChart, + Scatter, +} from "recharts"; +import * as math from "mathjs"; +import _ from "lodash"; +import "./AdvancedCalculatorStyles.css"; // Main Calculator Component const AdvancedCalculator = () => { // State management - const [activeTab, setActiveTab] = useState('basic'); + const [activeTab, setActiveTab] = useState("basic"); const [darkMode, setDarkMode] = useState(false); - const [display, setDisplay] = useState('0'); + const [display, setDisplay] = useState("0"); const [memory, setMemory] = useState(0); const [history, setHistory] = useState([]); - const [graphFunction, setGraphFunction] = useState('x^2'); + const [graphFunction, setGraphFunction] = useState("x^2"); const [graphRange, setGraphRange] = useState([-10, 10]); const [graphData, setGraphData] = useState([]); - const [secondGraph, setSecondGraph] = useState({ enabled: false, function: 'x', color: '#82ca9d' }); - const [physicsType, setPhysicsType] = useState('projectile'); + const [secondGraph, setSecondGraph] = useState({ + enabled: false, + function: "x", + color: "#82ca9d", + }); + const [physicsType, setPhysicsType] = useState("projectile"); const [physicsParams, setPhysicsParams] = useState({ velocity: 20, angle: 45, @@ -30,76 +45,68 @@ const AdvancedCalculator = () => { magneticField: 1.0, resistance: 10, voltage: 12, - current: 1.2 + current: 1.2, }); const [simulationData, setSimulationData] = useState([]); const [simulationRunning, setSimulationRunning] = useState(false); const [simulationInterval, setSimulationInterval] = useState(null); const [unitConversion, setUnitConversion] = useState({ fromValue: 1, - fromUnit: 'm', - toUnit: 'ft', - category: 'length' + fromUnit: "m", + toUnit: "ft", + category: "length", }); const [matrixA, setMatrixA] = useState([ [1, 2], - [3, 4] + [3, 4], ]); const [matrixB, setMatrixB] = useState([ [5, 6], - [7, 8] + [7, 8], ]); const [matrixResult, setMatrixResult] = useState(null); - const [matrixOperation, setMatrixOperation] = useState('add'); - const [statisticsData, setStatisticsData] = useState('1, 2, 3, 4, 5'); + const [matrixOperation, setMatrixOperation] = useState("add"); + const [statisticsData, setStatisticsData] = useState("1, 2, 3, 4, 5"); const [statisticsResults, setStatisticsResults] = useState({}); - const [saveName, setSaveName] = useState(''); + const [saveName, setSaveName] = useState(""); const [savedStates, setSavedStates] = useState({}); const [showSettings, setShowSettings] = useState(false); const [settings, setSettings] = useState({ precision: 4, - angleUnit: 'deg', - notationFormat: 'auto', - graphTheme: 'default' + angleUnit: "deg", + notationFormat: "auto", + graphTheme: "default", }); - - // Adding state for spreadsheet - const [spreadsheetTemplate, setSpreadsheetTemplate] = useState('budget'); - const [spreadsheetData, setSpreadsheetData] = useState([]); - const [spreadsheetHeaders, setSpreadsheetHeaders] = useState([]); - const [activeCell, setActiveCell] = useState({ row: 0, col: 0 }); - const [cellValues, setCellValues] = useState({}); - const [cellFormulas, setCellFormulas] = useState({}); - // Get graph colors based on theme + // Get graph colors based on theme const getGraphColors = () => { const themes = { default: { line1: "#8884d8", line2: "#82ca9d", grid: "#ccc", - background: darkMode ? "#1f2937" : "#fff" + background: darkMode ? "#1f2937" : "#fff", }, dark: { line1: "#ff7300", line2: "#00bcd4", grid: "#555", - background: "#111" + background: "#111", }, pastel: { line1: "#ffb6c1", line2: "#add8e6", grid: "#d3d3d3", - background: darkMode ? "#1f2937" : "#f0f8ff" + background: darkMode ? "#1f2937" : "#f0f8ff", }, neon: { line1: "#39ff14", line2: "#ff00ff", grid: "#0066ff", - background: "#000" - } + background: "#000", + }, }; - + return themes[settings.graphTheme]; }; @@ -109,7 +116,7 @@ const AdvancedCalculator = () => { const data = []; const step = (graphRange[1] - graphRange[0]) / 200; const expr = math.compile(graphFunction); - + for (let x = graphRange[0]; x <= graphRange[1]; x += step) { try { const y = expr.evaluate({ x }); @@ -125,12 +132,12 @@ const AdvancedCalculator = () => { console.error("Error generating graph data:", error); } }, [graphFunction, graphRange]); - + // Generate second graph data useEffect(() => { if (secondGraph.enabled) { try { - const data = graphData.map(point => { + const data = graphData.map((point) => { try { const expr = math.compile(secondGraph.function); const y2 = expr.evaluate({ x: point.x }); @@ -145,14 +152,17 @@ const AdvancedCalculator = () => { } } }, [secondGraph.function, secondGraph.enabled, graphRange, graphData]); - + // Statistical calculations useEffect(() => { try { - const data = statisticsData.split(',').map(n => parseFloat(n.trim())).filter(n => !isNaN(n)); - + const data = statisticsData + .split(",") + .map((n) => parseFloat(n.trim())) + .filter((n) => !isNaN(n)); + if (data.length === 0) return; - + const mean = math.mean(data); const median = math.median(data); const mode = calculateMode(data); @@ -163,26 +173,34 @@ const AdvancedCalculator = () => { const range = max - min; const sum = math.sum(data); const count = data.length; - + setStatisticsResults({ - mean, median, mode, standardDeviation, variance, - min, max, range, sum, count + mean, + median, + mode, + standardDeviation, + variance, + min, + max, + range, + sum, + count, }); } catch (error) { console.error("Error calculating statistics:", error); } }, [statisticsData]); - + // Calculate mode const calculateMode = (arr) => { const frequency = {}; - arr.forEach(val => { + arr.forEach((val) => { frequency[val] = (frequency[val] || 0) + 1; }); - + let maxFreq = 0; let modes = []; - + for (const key in frequency) { if (frequency[key] > maxFreq) { maxFreq = frequency[key]; @@ -191,103 +209,107 @@ const AdvancedCalculator = () => { modes.push(parseFloat(key)); } } - - return modes.join(', '); + + return modes.join(", "); }; - + // Handle basic calculator button clicks const handleButtonClick = (value) => { - if (display === '0' || display === 'Error') { + if (display === "0" || display === "Error") { setDisplay(value); } else { setDisplay(display + value); } }; - + // Handle calculation const calculateResult = () => { try { // Replace π with pi and adjust for angle units - let formula = display.replace(/π/g, 'pi'); - - if (settings.angleUnit === 'deg') { + let formula = display.replace(/π/g, "pi"); + + if (settings.angleUnit === "deg") { // Convert degrees to radians for trig functions - formula = formula.replace(/sin\(([^)]+)\)/g, 'sin($1 * pi/180)'); - formula = formula.replace(/cos\(([^)]+)\)/g, 'cos($1 * pi/180)'); - formula = formula.replace(/tan\(([^)]+)\)/g, 'tan($1 * pi/180)'); + formula = formula.replace(/sin\(([^)]+)\)/g, "sin($1 * pi/180)"); + formula = formula.replace(/cos\(([^)]+)\)/g, "cos($1 * pi/180)"); + formula = formula.replace(/tan\(([^)]+)\)/g, "tan($1 * pi/180)"); } - + const result = math.evaluate(formula); let formattedResult; - + // Format result according to settings - if (settings.notationFormat === 'scientific') { + if (settings.notationFormat === "scientific") { formattedResult = result.toExponential(settings.precision); - } else if (settings.notationFormat === 'fixed') { + } else if (settings.notationFormat === "fixed") { formattedResult = result.toFixed(settings.precision); } else { - formattedResult = typeof result === 'number' - ? math.format(result, { precision: settings.precision }) - : result.toString(); + formattedResult = + typeof result === "number" + ? math.format(result, { precision: settings.precision }) + : result.toString(); } - - setHistory([...history, { expression: display, result: formattedResult }]); + + setHistory([ + ...history, + { expression: display, result: formattedResult }, + ]); setDisplay(formattedResult.toString()); } catch (error) { - setDisplay('Error'); + setDisplay("Error"); } }; - + // Clear display const clearDisplay = () => { - setDisplay('0'); + setDisplay("0"); }; - + // Memory functions const memoryAdd = () => { try { const value = math.evaluate(display); setMemory(memory + value); } catch (error) { - setDisplay('Error'); + setDisplay("Error"); } }; - + const memorySubtract = () => { try { const value = math.evaluate(display); setMemory(memory - value); } catch (error) { - setDisplay('Error'); + setDisplay("Error"); } }; - + const memoryRecall = () => { setDisplay(memory.toString()); }; - + const memoryClear = () => { setMemory(0); }; - + // Handle physics simulations const startSimulation = () => { if (simulationRunning) return; - + setSimulationData([]); - setPhysicsParams({...physicsParams, time: 0}); + setPhysicsParams({ ...physicsParams, time: 0 }); setSimulationRunning(true); - + const interval = setInterval(() => { - setPhysicsParams(prevParams => ({ + setPhysicsParams((prevParams) => ({ ...prevParams, - time: prevParams.time + 0.1 + time: prevParams.time + 0.1, })); }, 100); - + setSimulationInterval(interval); }; - + // eslint-disable-next-line react-hooks/exhaustive-deps const stopSimulation = () => { if (simulationInterval) { @@ -295,216 +317,253 @@ const AdvancedCalculator = () => { } setSimulationRunning(false); }; - + // Unit conversion const convertUnit = () => { try { - const result = math.evaluate(`${unitConversion.fromValue} ${unitConversion.fromUnit} to ${unitConversion.toUnit}`); + const result = math.evaluate( + `${unitConversion.fromValue} ${unitConversion.fromUnit} to ${unitConversion.toUnit}` + ); // Return just the numeric value, without units - return result.toString().replace(unitConversion.toUnit, '').trim(); + return result.toString().replace(unitConversion.toUnit, "").trim(); } catch (error) { - return 'Error: Invalid conversion'; + return "Error: Invalid conversion"; } }; - + // Matrix operations const performMatrixOperation = () => { try { const A = math.matrix(matrixA); const B = math.matrix(matrixB); let result; - + switch (matrixOperation) { - case 'add': + case "add": result = math.add(A, B); break; - case 'subtract': + case "subtract": result = math.subtract(A, B); break; - case 'multiply': + case "multiply": result = math.multiply(A, B); break; - case 'determinantA': + case "determinantA": result = math.det(A); break; - case 'determinantB': + case "determinantB": result = math.det(B); break; - case 'inverseA': + case "inverseA": result = math.inv(A); break; - case 'inverseB': + case "inverseB": result = math.inv(B); break; - case 'transposeA': + case "transposeA": result = math.transpose(A); break; - case 'transposeB': + case "transposeB": result = math.transpose(B); break; default: result = null; } - - if (['determinantA', 'determinantB'].includes(matrixOperation)) { + + if (["determinantA", "determinantB"].includes(matrixOperation)) { setMatrixResult(result); } else { setMatrixResult(result.toArray()); } } catch (error) { - setMatrixResult('Error: ' + error.message); + setMatrixResult("Error: " + error.message); } }; - + // Calculate physics simulation data useEffect(() => { if (!simulationRunning) return; - + let newData = []; - - if (physicsType === 'projectile') { + + if (physicsType === "projectile") { const { velocity, angle, height, gravity, time } = physicsParams; - const radians = angle * Math.PI / 180; + const radians = (angle * Math.PI) / 180; const vx = velocity * Math.cos(radians); const vy = velocity * Math.sin(radians); - + // Calculate the theoretical range - const theoreticalRange = calculatePhysics('range'); - + const theoreticalRange = calculatePhysics("range"); + // Calculate current position const x = vx * time; const y = height + vy * time - 0.5 * gravity * time * time; - + // If we've reached or passed the ground, add final points and stop if (y < 0) { // Calculate the exact time when y=0 (impact time) // Using the quadratic formula for: height + vy*t - 0.5*g*t² = 0 const a = -0.5 * gravity; - const b = vy; + const b = vy; const c = height; - - const discriminant = b*b - 4*a*c; + + const discriminant = b * b - 4 * a * c; let impactTime; - + if (discriminant >= 0) { // Get both solutions - const t1 = (-b + Math.sqrt(discriminant)) / (2*a); - const t2 = (-b - Math.sqrt(discriminant)) / (2*a); - + const t1 = (-b + Math.sqrt(discriminant)) / (2 * a); + const t2 = (-b - Math.sqrt(discriminant)) / (2 * a); + // Choose the positive solution that makes sense in context if (t1 > 0 && t2 > 0) { impactTime = Math.min(t1, t2); } else { impactTime = Math.max(t1, t2); } - + // Make sure we have the right data array to work with newData = simulationData.slice(0, -1); // Remove the last point (which is below ground) - + // Add a point just before landing for a smooth curve if (impactTime > 0.1) { const preImpactTime = impactTime - 0.05; const preImpactX = vx * preImpactTime; - const preImpactY = height + vy * preImpactTime - 0.5 * gravity * preImpactTime * preImpactTime; - + const preImpactY = + height + + vy * preImpactTime - + 0.5 * gravity * preImpactTime * preImpactTime; + if (preImpactY > 0) { newData.push({ x: preImpactX, y: preImpactY }); } } - - // Add the exact landing point (y = 0) + + // Calculate the exact landing point (y = 0) const impactX = vx * impactTime; newData.push({ x: impactX, y: 0 }); + + // Add additional points to show the full theoretical range + // Only do this if we haven't reached the theoretical range yet + if (impactX < theoreticalRange) { + // Add points at regular intervals to extend to the theoretical range + const intervalCount = 5; // Number of additional points to add + const xInterval = (theoreticalRange - impactX) / intervalCount; + + for (let i = 1; i <= intervalCount; i++) { + const extendedX = impactX + xInterval * i; + newData.push({ x: extendedX, y: 0 }); + } + } } else { newData = [...simulationData]; } - + + // Set the final data and stop the simulation + setSimulationData(newData); stopSimulation(); return; } - + newData = [...simulationData, { x, y }]; - } - - else if (physicsType === 'pendulum') { + } else if (physicsType === "pendulum") { const { pendulumLength, gravity, time } = physicsParams; const omega = Math.sqrt(gravity / pendulumLength); - const angle = 30 * Math.PI / 180; // Starting angle (30 degrees) - + const angle = (30 * Math.PI) / 180; // Starting angle (30 degrees) + const x = pendulumLength * Math.sin(angle * Math.cos(omega * time)); const y = -pendulumLength * Math.cos(angle * Math.cos(omega * time)); - + newData = [...simulationData, { x, y }]; - } - else if (physicsType === 'spring') { + } else if (physicsType === "spring") { const { mass, springConstant, time } = physicsParams; const omega = Math.sqrt(springConstant / mass); const amplitude = 5; - + const x = amplitude * Math.cos(omega * time); const y = 0; - + newData = [...simulationData, { x, y }]; - } - else if (physicsType === 'circuit') { + } else if (physicsType === "circuit") { const { voltage, resistance, time } = physicsParams; const current = voltage / resistance; - + newData = [...simulationData, { x: time, y: current }]; - } - else if (physicsType === 'wave') { + } else if (physicsType === "wave") { const { time } = physicsParams; const amplitude = 3; const wavelength = 2; const frequency = 1; - + for (let x = 0; x <= 20; x += 0.5) { - const y = amplitude * Math.sin(2 * Math.PI * (x / wavelength - frequency * time)); + const y = + amplitude * + Math.sin(2 * Math.PI * (x / wavelength - frequency * time)); newData.push({ x, y, time }); } - + // Filter to keep only the latest time values - newData = _.uniqBy(newData, 'x'); - newData = _.sortBy(newData, 'x'); + newData = _.uniqBy(newData, "x"); + newData = _.sortBy(newData, "x"); } - + setSimulationData(newData); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [physicsParams.time, simulationRunning, physicsParams, physicsType, simulationData]); - + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + physicsParams.time, + simulationRunning, + physicsParams, + physicsType, + simulationData, + ]); + // Calculate physics values const calculatePhysics = (type) => { - const { velocity, angle, height, mass, gravity, - current, voltage, - electricCharge, magneticField } = physicsParams; - const radians = angle * Math.PI / 180; - + const { + velocity, + angle, + height, + mass, + gravity, + current, + voltage, + electricCharge, + magneticField, + } = physicsParams; + const radians = (angle * Math.PI) / 180; + switch (type) { - case 'range': + case "range": return (velocity * velocity * Math.sin(2 * radians)) / gravity; - case 'maxHeight': + case "maxHeight": return height + (velocity * Math.sin(radians)) ** 2 / (2 * gravity); - case 'flightTime': - return (velocity * Math.sin(radians) + Math.sqrt((velocity * Math.sin(radians)) ** 2 + 2 * gravity * height)) / gravity; - case 'kineticEnergy': + case "flightTime": + return ( + (velocity * Math.sin(radians) + + Math.sqrt( + (velocity * Math.sin(radians)) ** 2 + 2 * gravity * height + )) / + gravity + ); + case "kineticEnergy": return 0.5 * mass * velocity * velocity; - case 'potentialEnergy': + case "potentialEnergy": return mass * gravity * height; - case 'power': + case "power": return voltage * current; - case 'resistance': + case "resistance": return voltage / current; - case 'lorentzForce': + case "lorentzForce": return electricCharge * velocity * magneticField; default: return 0; } }; - + // Save current state const saveCurrentState = () => { if (!saveName) return; - + const currentState = { display, memory, @@ -515,22 +574,22 @@ const AdvancedCalculator = () => { physicsParams, statisticsData, matrixA, - matrixB + matrixB, }; - + setSavedStates({ ...savedStates, - [saveName]: currentState + [saveName]: currentState, }); - - setSaveName(''); + + setSaveName(""); }; - + // Load saved state const loadState = (stateName) => { const state = savedStates[stateName]; if (!state) return; - + setDisplay(state.display); setMemory(state.memory); setHistory(state.history); @@ -542,19 +601,33 @@ const AdvancedCalculator = () => { setMatrixA(state.matrixA); setMatrixB(state.matrixB); }; - + // Render theme-aware button const Button = ({ children, onClick, className = "", color = "default" }) => { const colorClasses = { - default: darkMode ? "bg-gray-700 hover:bg-gray-600" : "bg-gray-200 hover:bg-gray-300", - blue: darkMode ? "bg-blue-700 hover:bg-blue-600" : "bg-blue-500 hover:bg-blue-400", - green: darkMode ? "bg-green-700 hover:bg-green-600" : "bg-green-500 hover:bg-green-400", - red: darkMode ? "bg-red-700 hover:bg-red-600" : "bg-red-500 hover:bg-red-400", - yellow: darkMode ? "bg-yellow-700 hover:bg-yellow-600" : "bg-yellow-100 hover:bg-yellow-200", + default: darkMode + ? "bg-gray-700 hover:bg-gray-600" + : "bg-gray-200 hover:bg-gray-300", + blue: darkMode + ? "bg-blue-700 hover:bg-blue-600" + : "bg-blue-500 hover:bg-blue-400", + green: darkMode + ? "bg-green-700 hover:bg-green-600" + : "bg-green-500 hover:bg-green-400", + red: darkMode + ? "bg-red-700 hover:bg-red-600" + : "bg-red-500 hover:bg-red-400", + yellow: darkMode + ? "bg-yellow-700 hover:bg-yellow-600" + : "bg-yellow-100 hover:bg-yellow-200", }; - - const textColor = darkMode ? "text-gray-100" : (color === "default" ? "text-gray-800" : "text-white"); - + + const textColor = darkMode + ? "text-gray-100" + : color === "default" + ? "text-gray-800" + : "text-white"; + return ( ); }; - - // Evaluate cell formula - Helper function - const evaluateFormula = (formula, cellValues) => { - try { - // Replace cell references (e.g., A1, B2) with their values - let expression = formula.substring(1); // Remove the '=' - - // Replace SUM function - expression = expression.replace(/SUM\(([A-Z][0-9]+):([A-Z][0-9]+)\)/g, (match, start, end) => { - const startCol = start.charAt(0).charCodeAt(0) - 65; - const startRow = parseInt(start.substring(1)) - 1; - const endCol = end.charAt(0).charCodeAt(0) - 65; - const endRow = parseInt(end.substring(1)) - 1; - - let sum = 0; - for (let row = startRow; row <= endRow; row++) { - for (let col = startCol; col <= endCol; col++) { - const cellKey = `${row}-${col}`; - const value = parseFloat(cellValues[cellKey] || '0'); - if (!isNaN(value)) { - sum += value; - } - } - } - return sum; - }); - - // Replace PMT function (simplified) - expression = expression.replace(/PMT\(([^,]+),\s*([^,]+),\s*([^)]+)\)/g, (match, rate, nper, pv) => { - // Parse values instead of using eval - const rateVal = parseFloat(rate); - const nperVal = parseFloat(nper); - const pvVal = parseFloat(pv); - return (pvVal * rateVal) / (1 - Math.pow(1 + rateVal, -nperVal)); - }); - - // Replace FV function (simplified) - expression = expression.replace(/FV\(([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)/g, (match, rate, nper, pmt, pv) => { - // Parse values instead of using eval - const rateVal = parseFloat(rate); - const nperVal = parseFloat(nper); - const pmtVal = parseFloat(pmt); - const pvVal = parseFloat(pv); - return -1 * (pvVal * Math.pow(1 + rateVal, nperVal) + pmtVal * (Math.pow(1 + rateVal, nperVal) - 1) / rateVal); - }); - - // Replace cell references - expression = expression.replace(/([A-Z])([0-9]+)/g, (match, col, row) => { - const colIndex = col.charCodeAt(0) - 65; - const rowIndex = parseInt(row) - 1; - const cellKey = `${rowIndex}-${colIndex}`; - - // First check if there's a direct value in cellValues - if (cellValues[cellKey] !== undefined) { - return isNaN(parseFloat(cellValues[cellKey])) ? '0' : cellValues[cellKey]; - } - - // Otherwise, check the spreadsheetData - if (spreadsheetData[rowIndex] && spreadsheetData[rowIndex][colIndex] !== undefined) { - const value = spreadsheetData[rowIndex][colIndex]; - // If it's a formula, we need to recursively evaluate it - if (typeof value === 'string' && value.startsWith('=')) { - return evaluateFormula(value, cellValues); - } - return isNaN(parseFloat(value)) ? '0' : value; - } - - return '0'; - }); - - // Use math.js to evaluate the expression instead of eval - const result = math.evaluate(expression); - return isNaN(result) ? 'Error' : parseFloat(result).toFixed(2); - } catch (error) { - console.error("Formula evaluation error:", error); - return 'Error'; - } - }; - - // Initialize spreadsheet data for blank template - useEffect(() => { - if (spreadsheetTemplate === 'blank' && (!spreadsheetData.length || spreadsheetData.length === 0)) { - setSpreadsheetHeaders(['A', 'B', 'C', 'D', 'E']); - setSpreadsheetData([ - ['', '', '', '', ''], - ['', '', '', '', ''], - ['', '', '', '', ''], - ['', '', '', '', ''], - ['', '', '', '', ''] - ]); - } - }, [spreadsheetTemplate, spreadsheetData]); - - // Handle cell input change - const handleCellChange = (rowIndex, colIndex, value) => { - const cellKey = `${rowIndex}-${colIndex}`; - - // Update the cell value - setCellValues(prev => ({ - ...prev, - [cellKey]: value - })); - - // If it's a formula, store it in cellFormulas - if (value.startsWith('=')) { - setCellFormulas(prev => ({ - ...prev, - [cellKey]: value - })); - } else if (cellFormulas[cellKey]) { - // Remove from formulas if it's no longer a formula - const newFormulas = {...cellFormulas}; - delete newFormulas[cellKey]; - setCellFormulas(newFormulas); - } - - // Update the spreadsheet data for proper rendering - const newData = [...spreadsheetData]; - if (newData[rowIndex] && colIndex < newData[rowIndex].length) { - const newRow = [...newData[rowIndex]]; - newRow[colIndex] = value; - newData[rowIndex] = newRow; - setSpreadsheetData(newData); - } - }; - - // Render spreadsheet - const renderSpreadsheet = () => ( -
-
- - -
- -
- - - - - {spreadsheetHeaders.map((header, index) => ( - - ))} - - - - {spreadsheetData.map((row, rowIndex) => ( - - - {row.map((cell, colIndex) => ( - - ))} - - ))} - -
#{header}
{rowIndex + 1} setActiveCell({ row: rowIndex, col: colIndex })} - > - {activeCell.row === rowIndex && activeCell.col === colIndex ? ( - handleCellChange(rowIndex, colIndex, e.target.value)} - autoFocus - className="cell-input" - /> - ) : ( - typeof cell === 'string' && cell.startsWith('=') - ? evaluateFormula(cell, cellValues) - : (cellValues[`${rowIndex}-${colIndex}`] !== undefined ? cellValues[`${rowIndex}-${colIndex}`] : cell) - )} -
-
- -
- - handleCellChange(activeCell.row, activeCell.col, e.target.value)} - placeholder="Enter formula (e.g., =A1+B2)" - className="formula-input" - /> -
- -
-

Formulas:

- -
-
- ); - + // Render basic calculator const renderBasicCalculator = () => (
-
- {display} -
- +
{display}
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- +
History:
@@ -832,13 +757,15 @@ const AdvancedCalculator = () => { ) : ( history.map((item, index) => (
-
{item.expression} = {item.result}
+
+ {item.expression} = {item.result} +
)) )}
- +
Save Current State:
@@ -850,10 +777,12 @@ const AdvancedCalculator = () => { placeholder="Name this state..." className="flex-1" /> - +
- +
Load Saved State:
@@ -863,7 +792,12 @@ const AdvancedCalculator = () => { Object.keys(savedStates).map((name) => (
{name} - +
)) )} @@ -872,11 +806,11 @@ const AdvancedCalculator = () => {
); - + // Render graphing calculator const renderGraphingCalculator = () => { const colors = getGraphColors(); - + return (
@@ -891,33 +825,42 @@ const AdvancedCalculator = () => { placeholder="e.g., x^2, sin(x), etc." />
- +
setSecondGraph({...secondGraph, enabled: e.target.checked})} + onChange={(e) => + setSecondGraph({ + ...secondGraph, + enabled: e.target.checked, + }) + } /> setSecondGraph({...secondGraph, function: e.target.value})} + onChange={(e) => + setSecondGraph({ ...secondGraph, function: e.target.value }) + } className="flex-1" placeholder="e.g., x^2, sin(x), etc." disabled={!secondGraph.enabled} />
- +
setGraphRange([parseFloat(e.target.value), graphRange[1]])} + onChange={(e) => + setGraphRange([parseFloat(e.target.value), graphRange[1]]) + } className="w-20" />
@@ -926,17 +869,20 @@ const AdvancedCalculator = () => { setGraphRange([graphRange[0], parseFloat(e.target.value)])} + onChange={(e) => + setGraphRange([graphRange[0], parseFloat(e.target.value)]) + } className="w-20" />
- +
- @@ -945,16 +891,22 @@ const AdvancedCalculator = () => { dataKey="x" domain={[graphRange[0], graphRange[1]]} type="number" - label={{ value: 'x', position: 'insideBottomRight', offset: -5 }} + label={{ + value: "x", + position: "insideBottomRight", + offset: -5, + }} stroke={darkMode ? "#fff" : "#333"} /> value.toFixed(settings.precision)} - labelFormatter={(value) => `x = ${parseFloat(value).toFixed(settings.precision)}`} + labelFormatter={(value) => + `x = ${parseFloat(value).toFixed(settings.precision)}` + } contentStyle={{ backgroundColor: darkMode ? "#333" : "#fff" }} /> @@ -979,41 +931,131 @@ const AdvancedCalculator = () => {
- +
Common Functions:
- - - - - - - - - - - - + + + + + + + + + + + +
View Presets:
- - - - - - + + + + + +
); }; - + // Render unit conversion const renderUnitConversion = () => (
@@ -1022,7 +1064,9 @@ const AdvancedCalculator = () => {
- +
setUnitConversion({...unitConversion, fromValue: parseFloat(e.target.value)})} + onChange={(e) => + setUnitConversion({ + ...unitConversion, + fromValue: parseFloat(e.target.value), + }) + } />
- +
- +
- + - +
- {unitConversion.fromValue} {unitConversion.fromUnit} = {convertUnit()} {unitConversion.toUnit} + {unitConversion.fromValue} {unitConversion.fromUnit} = {convertUnit()}{" "} + {unitConversion.toUnit}
- +
Common Conversions:
@@ -1420,14 +1478,22 @@ const AdvancedCalculator = () => {
); - + // Render matrix calculator const renderMatrixCalculator = () => ( -
+

Matrix A:

-
+
{matrixA.map((row, rowIndex) => (
{row.map((cell, cellIndex) => ( @@ -1437,40 +1503,68 @@ const AdvancedCalculator = () => { value={cell} onChange={(e) => { const newMatrix = [...matrixA]; - newMatrix[rowIndex][cellIndex] = parseFloat(e.target.value); + newMatrix[rowIndex][cellIndex] = parseFloat( + e.target.value + ); setMatrixA(newMatrix); }} - className={`w-16 p-2 mr-2 border rounded ${darkMode ? "bg-gray-800 border-gray-600 text-white" : "bg-white border-gray-300"}`} + className={`w-16 p-2 mr-2 border rounded ${ + darkMode + ? "bg-gray-800 border-gray-600 text-white" + : "bg-white border-gray-300" + }`} /> ))} - +
))}
- - - + + +
- +

Matrix B:

-
+
{matrixB.map((row, rowIndex) => (
{row.map((cell, cellIndex) => ( @@ -1480,85 +1574,158 @@ const AdvancedCalculator = () => { value={cell} onChange={(e) => { const newMatrix = [...matrixB]; - newMatrix[rowIndex][cellIndex] = parseFloat(e.target.value); + newMatrix[rowIndex][cellIndex] = parseFloat( + e.target.value + ); setMatrixB(newMatrix); }} - className={`w-16 p-2 mr-2 border rounded ${darkMode ? "bg-gray-800 border-gray-600 text-white" : "bg-white border-gray-300"}`} + className={`w-16 p-2 mr-2 border rounded ${ + darkMode + ? "bg-gray-800 border-gray-600 text-white" + : "bg-white border-gray-300" + }`} /> ))} - +
))}
- - - + + +
- +
- - - - - - - - - + + + + + + + + +
- +

Result:

-
+
{matrixResult === null ? ( -
Perform an operation to see results
- ) : typeof matrixResult === 'string' ? ( +
+ Perform an operation to see results +
+ ) : typeof matrixResult === "string" ? (
{matrixResult}
- ) : typeof matrixResult === 'number' ? ( + ) : typeof matrixResult === "number" ? (
{matrixResult}
) : (
@@ -1567,9 +1734,13 @@ const AdvancedCalculator = () => { {row.map((cell, cellIndex) => (
- {typeof cell === 'number' ? cell.toFixed(settings.precision) : cell} + {typeof cell === "number" + ? cell.toFixed(settings.precision) + : cell}
))}
@@ -1580,71 +1751,113 @@ const AdvancedCalculator = () => {
); - + // Render statistics calculator const renderStatisticsCalculator = () => ( -
+