Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useState, useEffect } from 'react';
- const AmbSheet = () => {
- const initialData = [
- ['Budget', '', ''],
- ['', '', ''],
- ['Car', '{500,1200}', ''],
- ['Apartment', '{2800,3700,5500}', ''],
- ['Netflix', '18', ''],
- ['', '', ''],
- ['TOTAL', '', ''],
- ];
- const initialFormulas = {
- 'B7': '=SUM(B3:B5)'
- };
- const [data, setData] = useState(initialData);
- const [formulas, setFormulas] = useState(initialFormulas);
- const [selectedCell, setSelectedCell] = useState(null);
- const [editValue, setEditValue] = useState('');
- // Parse amb values from string format {val1,val2,...}
- const parseAmbValue = (value) => {
- if (typeof value !== 'string') return null;
- const match = value.match(/^\{(.*)\}$/);
- if (!match) return null;
- return match[1].split(',').map(v => parseFloat(v.trim()));
- };
- // Calculate all combinations for a range
- const calculateRangeCombinations = (startRow, endRow, col) => {
- const values = [];
- const ambRows = [];
- // Collect all values and track which rows have amb values
- for (let i = startRow; i <= endRow; i++) {
- const value = data[i][col];
- const parsed = parseAmbValue(value);
- if (parsed) {
- values.push(parsed);
- ambRows.push(i);
- } else {
- values.push([parseFloat(value) || 0]);
- }
- }
- // Generate all combinations using cartesian product
- const combinations = values.reduce((acc, curr) => {
- if (acc.length === 0) return curr.map(v => [v]);
- const newCombos = [];
- for (const combo of acc) {
- for (const value of curr) {
- newCombos.push([...combo, value]);
- }
- }
- return newCombos;
- }, []);
- // Sum each combination
- return combinations.map(combo => combo.reduce((sum, val) => sum + val, 0));
- };
- // Calculate value for a cell
- const calculateCellValue = (rowIndex, colIndex) => {
- const cellRef = `${String.fromCharCode(65 + colIndex)}${rowIndex + 1}`;
- const formula = formulas[cellRef];
- if (!formula) {
- const value = data[rowIndex][colIndex];
- const parsed = parseAmbValue(value);
- return parsed ? `{${parsed.join(',')}}` : value;
- }
- if (formula.toLowerCase().startsWith('=sum')) {
- const range = formula.match(/\((.*?)\)/)[1];
- const [start, end] = range.split(':');
- const startCol = start.charAt(0).charCodeAt(0) - 65;
- const startRow = parseInt(start.slice(1)) - 1;
- const endRow = parseInt(end.slice(1)) - 1;
- const results = calculateRangeCombinations(startRow, endRow, startCol);
- return `{${results.join(',')}}`;
- }
- return formula;
- };
- const renderCellContent = (value) => {
- const parsed = parseAmbValue(value);
- if (!parsed) return value;
- return (
- <div className="flex flex-wrap gap-1">
- {parsed.map((val, idx) => (
- <span key={idx} className="bg-gray-100 px-2 py-1 rounded-md text-sm">
- {val.toLocaleString()}
- </span>
- ))}
- </div>
- );
- };
- const handleCellClick = (rowIndex, colIndex) => {
- const cellRef = `${String.fromCharCode(65 + colIndex)}${rowIndex + 1}`;
- setSelectedCell({ row: rowIndex, col: colIndex, ref: cellRef });
- setEditValue(formulas[cellRef] || data[rowIndex][colIndex]);
- };
- const handleFormulaSubmit = () => {
- if (!selectedCell) return;
- if (editValue.startsWith('=')) {
- setFormulas({
- ...formulas,
- [selectedCell.ref]: editValue
- });
- } else {
- const newData = [...data];
- newData[selectedCell.row][selectedCell.col] = editValue;
- setData(newData);
- }
- };
- return (
- <div className="p-8 max-w-6xl mx-auto">
- <div className="flex gap-8">
- <div className="flex-1">
- <h1 className="text-2xl font-bold mb-6">Ambsheet</h1>
- <div className="mb-4">
- <div className="flex items-center space-x-2">
- <span className="font-medium">{selectedCell ? selectedCell.ref : ''}</span>
- <input
- type="text"
- value={editValue}
- onChange={(e) => setEditValue(e.target.value)}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- handleFormulaSubmit();
- }
- }}
- className="flex-1 p-2 border rounded"
- placeholder="Enter value or formula"
- />
- </div>
- </div>
- <div className="bg-white rounded-lg shadow-md overflow-hidden">
- <table className="w-full">
- <tbody>
- {data.map((row, rowIndex) => (
- <tr key={rowIndex} className={rowIndex === 0 ? 'bg-gray-50' : ''}>
- <td className="border p-2 w-8 text-gray-500 text-sm">
- {rowIndex + 1}
- </td>
- {row.map((cell, colIndex) => {
- const displayValue = calculateCellValue(rowIndex, colIndex);
- return (
- <td
- key={colIndex}
- className={`border p-2 ${
- selectedCell?.row === rowIndex && selectedCell?.col === colIndex
- ? 'bg-blue-50'
- : ''
- }`}
- onClick={() => handleCellClick(rowIndex, colIndex)}
- >
- {renderCellContent(displayValue)}
- </td>
- );
- })}
- </tr>
- ))}
- </tbody>
- </table>
- </div>
- </div>
- {selectedCell && (
- <div className="w-96">
- <h2 className="text-lg font-semibold mb-4">{selectedCell.ref}</h2>
- <div className="bg-white rounded-lg shadow-md p-4">
- <div className="text-sm font-medium text-gray-500 mb-2">Value</div>
- <div className="mb-4">
- {renderCellContent(calculateCellValue(selectedCell.row, selectedCell.col))}
- </div>
- {formulas[selectedCell.ref] && (
- <>
- <div className="text-sm font-medium text-gray-500 mb-2">Formula</div>
- <div className="font-mono bg-gray-50 p-2 rounded">
- {formulas[selectedCell.ref]}
- </div>
- </>
- )}
- </div>
- </div>
- )}
- </div>
- </div>
- );
- };
- export default AmbSheet;
Advertisement
Add Comment
Please, Sign In to add comment