Spaces:
Running
Running
Download index.html from Azerty2197/anycoder-cbf34bc5: direct link, hf CLI and curl.
- Browser
- Download file 35.8 kB
-
https://huggingface.co/spaces/Azerty2197/anycoder-cbf34bc5/resolve/main/index.html
- Command line
-
hf download hf://spaces/Azerty2197/anycoder-cbf34bc5/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Azerty2197/anycoder-cbf34bc5/resolve/main/index.html
35.8 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WebSheet - Online Spreadsheet Editor</title> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| --primary-color: #217346; | |
| --secondary-color: #107c41; | |
| --header-bg: #217346; | |
| --toolbar-bg: #f3f2f1; | |
| --border-color: #d0d7de; | |
| --cell-hover: #f0f6ff; | |
| --cell-selected: #e3f2fd; | |
| --cell-editing: #ffffff; | |
| --text-primary: #323130; | |
| --text-secondary: #605e5c; | |
| --shadow-sm: 0 1px 3px rgba(0,0,0,0.12); | |
| --shadow-md: 0 4px 6px rgba(0,0,0,0.1); | |
| --shadow-lg: 0 10px 25px rgba(0,0,0,0.1); | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| .app-container { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| background: white; | |
| max-width: 1400px; | |
| width: 100%; | |
| margin: 0 auto; | |
| box-shadow: 0 0 50px rgba(0,0,0,0.2); | |
| } | |
| header { | |
| background: var(--header-bg); | |
| color: white; | |
| padding: 0.75rem 1.5rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| box-shadow: var(--shadow-md); | |
| z-index: 100; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| } | |
| .logo-icon { | |
| width: 32px; | |
| height: 32px; | |
| background: white; | |
| border-radius: 6px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--primary-color); | |
| font-weight: bold; | |
| } | |
| .header-actions { | |
| display: flex; | |
| gap: 1rem; | |
| align-items: center; | |
| } | |
| .header-actions a { | |
| color: white; | |
| text-decoration: none; | |
| font-size: 0.875rem; | |
| opacity: 0.9; | |
| transition: opacity 0.2s; | |
| } | |
| .header-actions a:hover { | |
| opacity: 1; | |
| } | |
| .toolbar { | |
| background: var(--toolbar-bg); | |
| padding: 0.75rem 1.5rem; | |
| display: flex; | |
| gap: 1rem; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| border-bottom: 1px solid var(--border-color); | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .toolbar-group { | |
| display: flex; | |
| gap: 0.5rem; | |
| align-items: center; | |
| padding: 0 0.75rem; | |
| border-right: 1px solid var(--border-color); | |
| } | |
| .toolbar-group:last-child { | |
| border-right: none; | |
| } | |
| .toolbar-btn { | |
| background: white; | |
| border: 1px solid var(--border-color); | |
| padding: 0.5rem 0.75rem; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| font-size: 0.875rem; | |
| color: var(--text-primary); | |
| transition: all 0.2s; | |
| position: relative; | |
| } | |
| .toolbar-btn:hover { | |
| background: #f0f0f0; | |
| transform: translateY(-1px); | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .toolbar-btn:active { | |
| transform: translateY(0); | |
| } | |
| .toolbar-btn.primary { | |
| background: var(--secondary-color); | |
| color: white; | |
| border-color: var(--secondary-color); | |
| } | |
| .toolbar-btn.primary:hover { | |
| background: #0e5d2f; | |
| } | |
| .toolbar-btn.danger { | |
| background: #d83b01; | |
| color: white; | |
| border-color: #d83b01; | |
| } | |
| .toolbar-btn.danger:hover { | |
| background: #c23a00; | |
| } | |
| .toolbar-select { | |
| padding: 0.5rem; | |
| border: 1px solid var(--border-color); | |
| border-radius: 4px; | |
| background: white; | |
| color: var(--text-primary); | |
| font-size: 0.875rem; | |
| cursor: pointer; | |
| } | |
| .toolbar-input { | |
| width: 60px; | |
| padding: 0.5rem; | |
| border: 1px solid var(--border-color); | |
| border-radius: 4px; | |
| font-size: 0.875rem; | |
| } | |
| .formula-bar { | |
| background: white; | |
| border-bottom: 1px solid var(--border-color); | |
| padding: 0.75rem 1.5rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .cell-ref { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| min-width: 60px; | |
| font-size: 0.875rem; | |
| } | |
| .formula-input { | |
| flex: 1; | |
| padding: 0.5rem; | |
| border: 1px solid var(--border-color); | |
| border-radius: 4px; | |
| font-family: 'Consolas', 'Monaco', monospace; | |
| font-size: 0.875rem; | |
| } | |
| .spreadsheet-container { | |
| flex: 1; | |
| overflow: auto; | |
| position: relative; | |
| background: white; | |
| } | |
| .spreadsheet { | |
| display: flex; | |
| min-width: 100%; | |
| position: relative; | |
| } | |
| .column-headers { | |
| position: sticky; | |
| left: 0; | |
| top: 0; | |
| z-index: 20; | |
| background: var(--toolbar-bg); | |
| border-bottom: 2px solid var(--border-color); | |
| display: flex; | |
| } | |
| .corner-cell { | |
| width: 40px; | |
| height: 30px; | |
| background: var(--toolbar-bg); | |
| border-right: 2px solid var(--border-color); | |
| border-bottom: 2px solid var(--border-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| } | |
| .column-header { | |
| min-width: 100px; | |
| height: 30px; | |
| background: var(--toolbar-bg); | |
| border-right: 1px solid var(--border-color); | |
| border-bottom: 2px solid var(--border-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| cursor: pointer; | |
| user-select: none; | |
| position: sticky; | |
| top: 0; | |
| z-index: 10; | |
| } | |
| .column-header:hover { | |
| background: #e8e6e3; | |
| } | |
| .rows-container { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .row { | |
| display: flex; | |
| position: relative; | |
| } | |
| .row-header { | |
| width: 40px; | |
| height: 25px; | |
| background: var(--toolbar-bg); | |
| border-right: 2px solid var(--border-color); | |
| border-bottom: 1px solid var(--border-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| position: sticky; | |
| left: 0; | |
| z-index: 9; | |
| cursor: pointer; | |
| } | |
| .row-header:hover { | |
| background: #e8e6e3; | |
| } | |
| .cell { | |
| min-width: 100px; | |
| height: 25px; | |
| border-right: 1px solid var(--border-color); | |
| border-bottom: 1px solid var(--border-color); | |
| padding: 2px 6px; | |
| font-size: 0.875rem; | |
| color: var(--text-primary); | |
| cursor: cell; | |
| position: relative; | |
| overflow: hidden; | |
| white-space: nowrap; | |
| text-overflow: ellipsis; | |
| transition: background-color 0.15s; | |
| } | |
| .cell:hover { | |
| background: var(--cell-hover); | |
| } | |
| .cell.selected { | |
| background: var(--cell-selected); | |
| border: 2px solid #2188ff; | |
| border-radius: 0; | |
| outline: none; | |
| } | |
| .cell.editing { | |
| background: var(--cell-editing); | |
| outline: 2px solid #2188ff; | |
| outline-offset: -2px; | |
| } | |
| .cell-input { | |
| width: 100%; | |
| height: 100%; | |
| border: none; | |
| outline: none; | |
| padding: 2px 6px; | |
| font-size: 0.875rem; | |
| font-family: inherit; | |
| background: transparent; | |
| } | |
| .resize-handle { | |
| position: absolute; | |
| right: -3px; | |
| top: 0; | |
| width: 6px; | |
| height: 100%; | |
| cursor: col-resize; | |
| z-index: 100; | |
| background: transparent; | |
| } | |
| .resize-handle:hover { | |
| background: rgba(33, 136, 255, 0.3); | |
| } | |
| .status-bar { | |
| background: var(--toolbar-bg); | |
| padding: 0.5rem 1.5rem; | |
| border-top: 1px solid var(--border-color); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| } | |
| .status-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .zoom-controls { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .zoom-btn { | |
| background: white; | |
| border: 1px solid var(--border-color); | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 4px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| font-size: 0.75rem; | |
| color: var(--text-primary); | |
| transition: all 0.2s; | |
| } | |
| .zoom-btn:hover { | |
| background: #f0f0f0; | |
| } | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 1000; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .modal.active { | |
| display: flex; | |
| } | |
| .modal-content { | |
| background: white; | |
| padding: 2rem; | |
| border-radius: 8px; | |
| box-shadow: var(--shadow-lg); | |
| max-width: 400px; | |
| width: 90%; | |
| } | |
| .modal-header { | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| margin-bottom: 1rem; | |
| color: var(--text-primary); | |
| } | |
| .modal-body { | |
| margin-bottom: 1.5rem; | |
| color: var(--text-secondary); | |
| } | |
| .modal-actions { | |
| display: flex; | |
| gap: 1rem; | |
| justify-content: flex-end; | |
| } | |
| .btn { | |
| padding: 0.5rem 1rem; | |
| border-radius: 4px; | |
| border: none; | |
| cursor: pointer; | |
| font-size: 0.875rem; | |
| transition: all 0.2s; | |
| } | |
| .btn-cancel { | |
| background: #f3f2f1; | |
| color: var(--text-primary); | |
| } | |
| .btn-cancel:hover { | |
| background: #e1dfdd; | |
| } | |
| .btn-confirm { | |
| background: var(--secondary-color); | |
| color: white; | |
| } | |
| .btn-confirm:hover { | |
| background: #0e5d2f; | |
| } | |
| @media (max-width: 768px) { | |
| .toolbar { | |
| padding: 0.5rem; | |
| } | |
| .toolbar-group { | |
| padding: 0 0.5rem; | |
| } | |
| .cell { | |
| min-width: 80px; | |
| font-size: 0.75rem; | |
| } | |
| .column-header { | |
| min-width: 80px; | |
| font-size: 0.7rem; | |
| } | |
| .formula-bar { | |
| padding: 0.5rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <header> | |
| <div class="logo"> | |
| <div class="logo-icon">WS</div> | |
| <span>WebSheet</span> | |
| </div> | |
| <div class="header-actions"> | |
| <span>Untitled Spreadsheet</span> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a> | |
| </div> | |
| </header> | |
| <div class="toolbar"> | |
| <div class="toolbar-group"> | |
| <button class="toolbar-btn primary" onclick="downloadXLS()"> | |
| <span>📥</span> Download XLS | |
| </button> | |
| <button class="toolbar-btn" onclick="downloadCSV()"> | |
| <span>📄</span> CSV | |
| </button> | |
| </div> | |
| <div class="toolbar-group"> | |
| <button class="toolbar-btn" onclick="copySelection()"> | |
| <span>📋</span> Copy | |
| </button> | |
| <button class="toolbar-btn" onclick="pasteSelection()"> | |
| <span>📌</span> Paste | |
| </button> | |
| <button class="toolbar-btn danger" onclick="clearSelection()"> | |
| <span>🗑️</span> Clear | |
| </button> | |
| </div> | |
| <div class="toolbar-group"> | |
| <button class="toolbar-btn" onclick="addRow()"> | |
| <span>➕</span> Row | |
| </button> | |
| <button class="toolbar-btn" onclick="addColumn()"> | |
| <span>➕</span> Column | |
| </button> | |
| <button class="toolbar-btn" onclick="deleteRow()"> | |
| <span>➖</span> Delete Row | |
| </button> | |
| <button class="toolbar-btn" onclick="deleteColumn()"> | |
| <span>➖</span> Delete Column | |
| </button> | |
| </div> | |
| <div class="toolbar-group"> | |
| <select class="toolbar-select" onchange="changeFontSize(this.value)"> | |
| <option value="14px">Normal</option> | |
| <option value="12px">Small</option> | |
| <option value="16px">Large</option> | |
| </select> | |
| <button class="toolbar-btn" onclick="toggleBold()"> | |
| <span>B</span> | |
| </button> | |
| <button class="toolbar-btn" onclick="toggleItalic()"> | |
| <span>I</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="formula-bar"> | |
| <div class="cell-ref" id="cellRef">A1</div> | |
| <input type="text" class="formula-input" id="formulaInput" placeholder="Enter data or formula" onkeyup="updateCellFromFormula()"> | |
| </div> | |
| <div class="spreadsheet-container"> | |
| <div class="spreadsheet" id="spreadsheet"> | |
| <div class="column-headers"> | |
| <div class="corner-cell"></div> | |
| </div> | |
| <div class="rows-container" id="rowsContainer"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="status-bar"> | |
| <div class="status-item"> | |
| <span>Ready</span> | |
| </div> | |
| <div class="zoom-controls"> | |
| <button class="zoom-btn" onclick="zoomOut()">−</button> | |
| <span id="zoomLevel">100%</span> | |
| <button class="zoom-btn" onclick="zoomIn()">+</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal" id="confirmModal"> | |
| <div class="modal-content"> | |
| <div class="modal-header">Confirm Action</div> | |
| <div class="modal-body" id="modalMessage">Are you sure?</div> | |
| <div class="modal-actions"> | |
| <button class="btn btn-cancel" onclick="closeModal()">Cancel</button> | |
| <button class="btn btn-confirm" onclick="confirmAction()">Confirm</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let selectedCell = null; | |
| let editingCell = null; | |
| let currentZoom = 100; | |
| let clipboardData = null; | |
| let spreadsheetData = {}; | |
| let numRows = 100; | |
| let numCols = 26; | |
| function initSpreadsheet() { | |
| const spreadsheet = document.getElementById('spreadsheet'); | |
| const rowsContainer = document.getElementById('rowsContainer'); | |
| // Create column headers | |
| const columnHeaders = spreadsheet.querySelector('.column-headers'); | |
| for (let i = 0; i < numCols; i++) { | |
| const header = document.createElement('div'); | |
| header.className = 'column-header'; | |
| header.textContent = String.fromCharCode(65 + i); | |
| header.onclick = () => selectColumn(i); | |
| columnHeaders.appendChild(header); | |
| } | |
| // Create rows and cells | |
| for (let row = 0; row < numRows; row++) { | |
| const rowDiv = document.createElement('div'); | |
| rowDiv.className = 'row'; | |
| const rowHeader = document.createElement('div'); | |
| rowHeader.className = 'row-header'; | |
| rowHeader.textContent = row + 1; | |
| rowHeader.onclick = () => selectRow(row); | |
| rowDiv.appendChild(rowHeader); | |
| for (let col = 0; col < numCols; col++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'cell'; | |
| cell.id = `cell-${row}-${col}`; | |
| cell.onclick = () => selectCell(row, col); | |
| cell.ondblclick = () => startEditCell(row, col); | |
| cell.onkeydown = (e) => handleCellKeydown(e, row, col); | |
| cell.contentEditable = false; | |
| rowDiv.appendChild(cell); | |
| } | |
| rowsContainer.appendChild(rowDiv); | |
| } | |
| // Add resize handles to column headers | |
| document.querySelectorAll('.column-header').forEach((header, index) => { | |
| const handle = document.createElement('div'); | |
| handle.className = 'resize-handle'; | |
| handle.onmousedown = (e) => startResize(e, index); | |
| header.appendChild(handle); | |
| }); | |
| // Select first cell by default | |
| selectCell(0, 0); | |
| } | |
| function selectCell(row, col) { | |
| // Remove previous selection | |
| document.querySelectorAll('.cell.selected').forEach(cell => { | |
| cell.classList.remove('selected'); | |
| }); | |
| // Add selection to new cell | |
| const cell = document.getElementById(`cell-${row}-${col}`); | |
| cell.classList.add('selected'); | |
| selectedCell = { row, col }; | |
| // Update cell reference | |
| updateCellRef(row, col); | |
| // Update formula bar | |
| const cellKey = `${row}-${col}`; | |
| const formulaInput = document.getElementById('formulaInput'); | |
| formulaInput.value = spreadsheetData[cellKey] || ''; | |
| } | |
| function startEditCell(row, col) { | |
| const cell = document.getElementById(`cell-${row}-${col}`); | |
| cell.classList.add('editing'); | |
| cell.contentEditable = true; | |
| cell.focus(); | |
| // Select all text | |
| const range = document.createRange(); | |
| range.selectNodeContents(cell); | |
| const selection = window.getSelection(); | |
| selection.removeAllRanges(); | |
| selection.addRange(range); | |
| editingCell = { row, col }; | |
| } | |
| function finishEditCell() { | |
| if (!editingCell) return; | |
| const cell = document.getElementById(`cell-${editingCell.row}-${editingCell.col}`); | |
| const value = cell.textContent; | |
| // Save data | |
| const cellKey = `${editingCell.row}-${editingCell.col}`; | |
| if (value) { | |
| spreadsheetData[cellKey] = value; | |
| } else { | |
| delete spreadsheetData[cellKey]; | |
| } | |
| cell.contentEditable = false; | |
| cell.classList.remove('editing'); | |
| editingCell = null; | |
| } | |
| function handleCellKeydown(e, row, col) { | |
| if (e.key === 'Enter') { | |
| e.preventDefault(); | |
| if (editingCell) { | |
| finishEditCell(); | |
| } else { | |
| startEditCell(row, col); | |
| } | |
| } else if (e.key === 'Tab') { | |
| e.preventDefault(); | |
| if (col < numCols - 1) { | |
| selectCell(row, col + 1); | |
| } else if (row < numRows - 1) { | |
| selectCell(row + 1, 0); | |
| } | |
| } else if (e.key === 'ArrowUp' && !editingCell) { | |
| e.preventDefault(); | |
| if (row > 0) selectCell(row - 1, col); | |
| } else if (e.key === 'ArrowDown' && !editingCell) { | |
| e.preventDefault(); | |
| if (row < numRows - 1) selectCell(row + 1, col); | |
| } else if (e.key === 'ArrowLeft' && !editingCell) { | |
| e.preventDefault(); | |
| if (col > 0) selectCell(row, col - 1); | |
| } else if (e.key === 'ArrowRight' && !editingCell) { | |
| e.preventDefault(); | |
| if (col < numCols - 1) selectCell(row, col + 1); | |
| } else if (e.key === 'Delete' || e.key === 'Backspace') { | |
| if (!editingCell) { | |
| e.preventDefault(); | |
| clearCell(row, col); | |
| } | |
| } | |
| } | |
| function updateCellRef(row, col) { | |
| const colLetter = String.fromCharCode(65 + col); | |
| document.getElementById('cellRef').textContent = `${colLetter}${row + 1}`; | |
| } | |
| function updateCellFromFormula() { | |
| if (!selectedCell) return; | |
| const formulaInput = document.getElementById('formulaInput'); | |
| const cell = document.getElementById(`cell-${selectedCell.row}-${selectedCell.col}`); | |
| const cellKey = `${selectedCell.row}-${selectedCell.col}`; | |
| if (formulaInput.value) { | |
| spreadsheetData[cellKey] = formulaInput.value; | |
| cell.textContent = formulaInput.value; | |
| } else { | |
| delete spreadsheetData[cellKey]; | |
| cell.textContent = ''; | |
| } | |
| } | |
| function downloadXLS() { | |
| const ws_data = []; | |
| // Create header row | |
| const header = ['']; | |
| for (let i = 0; i < numCols; i++) { | |
| header.push(String.fromCharCode(65 + i)); | |
| } | |
| ws_data.push(header); | |
| // Create data rows | |
| for (let row = 0; row < numRows; row++) { | |
| const rowData = [row + 1]; | |
| for (let col = 0; col < numCols; col++) { | |
| const cellKey = `${row}-${col}`; | |
| rowData.push(spreadsheetData[cellKey] || ''); | |
| } | |
| ws_data.push(rowData); | |
| } | |
| // Create worksheet | |
| const ws = XLSX.utils.aoa_to_sheet(ws_data); | |
| // Create workbook | |
| const wb = XLSX.utils.book_new(); | |
| XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); | |
| // Generate and download XLS file | |
| XLSX.writeFile(wb, "websheet_export.xls"); | |
| } | |
| function downloadCSV() { | |
| let csv = ''; | |
| for (let row = 0; row < numRows; row++) { | |
| const rowData = []; | |
| for (let col = 0; col < numCols; col++) { | |
| const cellKey = `${row}-${col}`; | |
| const value = spreadsheetData[cellKey] || ''; | |
| rowData.push(`"${value}"`); | |
| } | |
| csv += rowData.join(',') + '\n'; | |
| } | |
| const blob = new Blob([csv], { type: 'text/csv' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'websheet_export.csv'; | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| } | |
| function copySelection() { | |
| if (!selectedCell) return; | |
| const cellKey = `${selectedCell.row}-${selectedCell.col}`; | |
| clipboardData = spreadsheetData[cellKey] || ''; | |
| // Also copy to system clipboard | |
| navigator.clipboard.writeText(clipboardData); | |
| } | |
| function pasteSelection() { | |
| if (!selectedCell || !clipboardData) return; | |
| const cell = document.getElementById(`cell-${selectedCell.row}-${selectedCell.col}`); | |
| const cellKey = `${selectedCell.row}-${selectedCell.col}`; | |
| spreadsheetData[cellKey] = clipboardData; | |
| cell.textContent = clipboardData; | |
| document.getElementById('formulaInput').value = clipboardData; | |
| } | |
| function clearSelection() { | |
| if (!selectedCell) return; | |
| clearCell(selectedCell.row, selectedCell.col); | |
| } | |
| function clearCell(row, col) { | |
| const cell = document.getElementById(`cell-${row}-${col}`); | |
| const cellKey = `${row}-${col}`; | |
| delete spreadsheetData[cellKey]; | |
| cell.textContent = ''; | |
| document.getElementById('formulaInput').value = ''; | |
| } | |
| function addRow() { | |
| const rowsContainer = document.getElementById('rowsContainer'); | |
| const newRow = document.createElement('div'); | |
| newRow.className = 'row'; | |
| const rowHeader = document.createElement('div'); | |
| rowHeader.className = 'row-header'; | |
| rowHeader.textContent = numRows + 1; | |
| rowHeader.onclick = () => selectRow(numRows); | |
| newRow.appendChild(rowHeader); | |
| for (let col = 0; col < numCols; col++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'cell'; | |
| cell.id = `cell-${numRows}-${col}`; | |
| cell.onclick = () => selectCell(numRows, col); | |
| cell.ondblclick = () => startEditCell(numRows, col); | |
| cell.onkeydown = (e) => handleCellKeydown(e, numRows, col); | |
| cell.contentEditable = false; | |
| newRow.appendChild(cell); | |
| } | |
| rowsContainer.appendChild(newRow); | |
| numRows++; | |
| } | |
| function addColumn() { | |
| if (numCols >= 26) { | |
| alert('Maximum columns reached'); | |
| return; | |
| } | |
| const columnHeaders = document.querySelector('.column-headers'); | |
| const header = document.createElement('div'); | |
| header.className = 'column-header'; | |
| header.textContent = String.fromCharCode(65 + numCols); | |
| header.onclick = () => selectColumn(numCols); | |
| const handle = document.createElement('div'); | |
| handle.className = 'resize-handle'; | |
| handle.onmousedown = (e) => startResize(e, numCols); | |
| header.appendChild(handle); | |
| columnHeaders.appendChild(header); | |
| // Add cells to each row | |
| document.querySelectorAll('.row').forEach((row, index) => { | |
| const cell = document.createElement('div'); | |
| cell.className = 'cell'; | |
| cell.id = `cell-${index}-${numCols}`; | |
| cell.onclick = () => selectCell(index, numCols); | |
| cell.ondblclick = () => startEditCell(index, numCols); | |
| cell.onkeydown = (e) => handleCellKeydown(e, index, numCols); | |
| cell.contentEditable = false; | |
| row.appendChild(cell); | |
| }); | |
| numCols++; | |
| } | |
| function deleteRow() { | |
| if (!selectedCell) return; | |
| const row = selectedCell.row; | |
| const rows = document.querySelectorAll('.row'); | |
| if (rows[row]) { | |
| rows[row].remove(); | |
| // Update data | |
| Object.keys(spreadsheetData).forEach(key => { | |
| const [r, c] = key.split('-').map(Number); | |
| if (r === row) { | |
| delete spreadsheetData[key]; | |
| } else if (r > row) { | |
| spreadsheetData[`${r-1}-${c}`] = spreadsheetData[key]; | |
| delete spreadsheetData[key]; | |
| } | |
| }); | |
| // Re-select first cell | |
| selectCell(0, 0); | |
| } | |
| } | |
| function deleteColumn() { | |
| if (!selectedCell) return; | |
| const col = selectedCell.col; | |
| // Update data | |
| Object.keys(spreadsheetData).forEach(key => { | |
| const [r, c] = key.split('-').map(Number); | |
| if (c === col) { | |
| delete spreadsheetData[key]; | |
| } else if (c > col) { | |
| spreadsheetData[`${r}-${c-1}`] = spreadsheetData[key]; | |
| delete spreadsheetData[key]; | |
| } | |
| }); | |
| // Remove column header | |
| const headers = document.querySelectorAll('.column-header'); | |
| if (headers[col]) headers[col].remove(); | |
| // Remove cells | |
| document.querySelectorAll('.row').forEach(row => { | |
| const cells = row.querySelectorAll('.cell'); | |
| if (cells[col]) cells[col].remove(); | |
| }); | |
| numCols--; | |
| // Re-select first cell | |
| selectCell(0, 0); | |
| } | |
| function selectColumn(col) { | |
| document.querySelectorAll('.cell').forEach(cell => { | |
| cell.classList.remove('selected'); | |
| }); | |
| for (let row = 0; row < numRows; row++) { | |
| const cell = document.getElementById(`cell-${row}-${col}`); | |
| if (cell) cell.classList.add('selected'); | |
| } | |
| } | |
| function selectRow(row) { | |
| document.querySelectorAll('.cell').forEach(cell => { | |
| cell.classList.remove('selected'); | |
| }); | |
| const cells = document.querySelectorAll(`#cell-${row}-0`).forEach(cell => { | |
| const cells = cell.parentElement.querySelectorAll('.cell'); | |
| cells.forEach(c => c.classList.add('selected')); | |
| }); | |
| } | |
| function changeFontSize(size) { | |
| document.querySelectorAll('.cell').forEach(cell => { | |
| cell.style.fontSize = size; | |
| }); | |
| } | |
| function toggleBold() { | |
| if (!selectedCell) return; | |
| const cell = document.getElementById(`cell-${selectedCell.row}-${selectedCell.col}`); | |
| cell.style.fontWeight = cell.style.fontWeight === 'bold' ? 'normal' : 'bold'; | |
| } | |
| function toggleItalic() { | |
| if (!selectedCell) return; | |
| const cell = document.getElementById(`cell-${selectedCell.row}-${selectedCell.col}`); | |
| cell.style.fontStyle = cell.style.fontStyle === 'italic' ? 'normal' : 'italic'; | |
| } | |
| function zoomIn() { | |
| if (currentZoom < 150) { | |
| currentZoom += 10; | |
| updateZoom(); | |
| } | |
| } | |
| function zoomOut() { | |
| if (currentZoom > 50) { | |
| currentZoom -= 10; | |
| updateZoom(); | |
| } | |
| } | |
| function updateZoom() { | |
| const spreadsheet = document.getElementById('spreadsheet'); | |
| spreadsheet.style.transform = `scale(${currentZoom / 100})`; | |
| spreadsheet.style.transformOrigin = 'top left'; | |
| document.getElementById('zoomLevel').textContent = `${currentZoom}%`; | |
| } | |
| function startResize(e, colIndex) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| const startX = e.clientX; | |
| const header = e.target.parentElement; | |
| const startWidth = header.offsetWidth; | |
| function handleMouseMove(e) { | |
| const newWidth = startWidth + (e.clientX - startX); | |
| if (newWidth > 50) { | |
| header.style.width = newWidth + 'px'; | |
| // Update all cells in this column | |
| document.querySelectorAll('.row').forEach((row, index) => { | |
| const cell = document.getElementById(`cell-${index}-${colIndex}`); | |
| if (cell) cell.style.width = newWidth + 'px'; | |
| }); | |
| } | |
| } | |
| function handleMouseUp() { | |
| document.removeEventListener('mousemove', handleMouseMove); | |
| document.removeEventListener('mouseup', handleMouseUp); | |
| } | |
| document.addEventListener('mousemove', handleMouseMove); | |
| document.addEventListener('mouseup', handleMouseUp); | |
| } | |
| function closeModal() { | |
| document.getElementById('confirmModal').classList.remove('active'); | |
| } | |
| function confirmAction() { | |
| closeModal(); | |
| } | |
| // Initialize on load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| initSpreadsheet(); | |
| // Global click handler to finish editing | |
| document.addEventListener('click', (e) => { | |
| if (editingCell && !e.target.classList.contains('cell')) { | |
| finishEditCell(); | |
| } | |
| }); | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', (e) => { | |
| if (e.ctrlKey || e.metaKey) { | |
| switch(e.key) { | |
| case 's': | |
| e.preventDefault(); | |
| downloadXLS(); | |
| break; | |
| case 'c': | |
| e.preventDefault(); | |
| copySelection(); | |
| break; | |
| case 'v': | |
| e.preventDefault(); | |
| pasteSelection(); | |
| break; | |
| } | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |