This commit is contained in:
2025-09-05 23:23:26 +10:00
parent fb40c0a2ce
commit d0163020f9
4 changed files with 51 additions and 35 deletions

View File

@@ -74,14 +74,14 @@
'resizer-col': direction === 'col', 'resizer-col': direction === 'col',
'resizer-row': direction === 'row' 'resizer-row': direction === 'row'
})} })}
/> ></div>
</div> </div>
<style> <style>
.placeholder { .placeholder {
font-size: 14px; font-size: 14px;
border: 1px solid var(--input); border: 1px solid var(--input);
overflow: hidden; overflow: visible;
} }
.active { .active {
@@ -95,31 +95,32 @@
/* Make it easier to grab */ /* Make it easier to grab */
z-index: 10; z-index: 10;
/* Subtle visual cue, becomes more visible on hover */ /* Subtle visual cue, becomes more visible on hover */
background-color: transparent; background-color: var(--color-primary);
transition: background-color 0.2s ease-in-out; opacity: 0;
transition: opacity 0.1s ease-in-out;
} }
/* Style for vertical (column) resizing */ /* Style for vertical (column) resizing */
.resizer-col { .resizer-col {
cursor: col-resize; cursor: col-resize;
top: 0; top: 0;
right: 0; right: -5px;
width: 8px; /* Larger grab area */ width: 9px; /* Larger grab area */
height: 100%; height: 100%;
} }
/* Style for horizontal (row) resizing */ /* Style for horizontal (row) resizing */
.resizer-row { .resizer-row {
cursor: row-resize; cursor: row-resize;
bottom: 0; bottom: -5px;
left: 0; left: 0;
height: 8px; /* Larger grab area */ height: 9px; /* Larger grab area */
width: 100%; width: 100%;
} }
/* Make the handle visible when hovering over the component */ /* Make the handle visible when hovering over the component */
.resizer:hover,
.group:hover > .resizer { .group:hover > .resizer {
background-color: var(--color-primary);
opacity: 0.5; opacity: 0.5;
} }
</style> </style>

View File

@@ -80,7 +80,9 @@
<style> <style>
.placeholder { .placeholder {
border: 1px solid var(--input); border: 1px solid var(--input);
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: clip;
} }
.active { .active {

View File

@@ -4,10 +4,10 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import CellHeader from './cell-header.svelte'; import CellHeader from './cell-header.svelte';
import { import {
fromGridRef, refFromStr,
splitErrorString, splitErrorString,
toColLetter, colToStr,
toGridRef, refFromPos,
type CellData, type CellData,
type CellValue type CellValue
} from './utils'; } from './utils';
@@ -30,46 +30,46 @@
return; return;
} }
let cellRef: string | undefined; let strRef: string | undefined;
let evalStr: string | undefined; let evalStr: string | undefined;
// Case 1: "Cell D4 = Integer(4)" // Case 1: "Cell D4 = Integer(4)"
let match = input.match(/^Cell\s+([A-Z]+\d+)\s*=\s*(.+)$/); let match = input.match(/^Cell\s+([A-Z]+\d+)\s*=\s*(.+)$/);
if (match) { if (match) {
[, cellRef, evalStr] = match; [, strRef, evalStr] = match;
} }
// Case 2: "D6 String("hello")" or "E9 Double(4.0)" // Case 2: "D6 String("hello")" or "E9 Double(4.0)"
if (!match) { if (!match) {
match = input.match(/^([A-Z]+\d+)\s+(.+)$/); match = input.match(/^([A-Z]+\d+)\s+(.+)$/);
if (match) { if (match) {
[, cellRef, evalStr] = match; [, strRef, evalStr] = match;
} }
} }
if (!cellRef || !evalStr) { if (!strRef || !evalStr) {
console.warn('Unrecognized message:', input); console.warn('Unrecognized message:', input);
return; return;
} }
console.log(`Cell: ${cellRef}`); console.log(`Cell: ${strRef}`);
console.log(`Eval: ${evalStr}`); console.log(`Eval: ${evalStr}`);
let [i, j] = fromGridRef(cellRef); let { row, col } = refFromStr(strRef);
// Parse eval types // Parse eval types
if (evalStr.startsWith('Integer(')) { if (evalStr.startsWith('Integer(')) {
const num = parseInt(evalStr.match(/^Integer\(([-\d]+)\)$/)?.[1] ?? 'NaN', 10); const num = parseInt(evalStr.match(/^Integer\(([-\d]+)\)$/)?.[1] ?? 'NaN', 10);
console.log(`Parsed integer:`, num); console.log(`Parsed integer:`, num);
setCellVal(i, j, num); setCellVal(row, col, num);
} else if (evalStr.startsWith('Double(')) { } else if (evalStr.startsWith('Double(')) {
const num = parseFloat(evalStr.match(/^Double\(([-\d.]+)\)$/)?.[1] ?? 'NaN'); const num = parseFloat(evalStr.match(/^Double\(([-\d.]+)\)$/)?.[1] ?? 'NaN');
console.log(`Parsed double:`, num); console.log(`Parsed double:`, num);
setCellVal(i, j, num); setCellVal(row, col, num);
} else if (evalStr.startsWith('String(')) { } else if (evalStr.startsWith('String(')) {
const str = evalStr.match(/^String\("(.+)"\)$/)?.[1]; const str = evalStr.match(/^String\("(.+)"\)$/)?.[1];
console.log(`Parsed string:`, str); console.log(`Parsed string:`, str);
setCellVal(i, j, str); setCellVal(row, col, str);
} }
}; };
@@ -77,7 +77,7 @@
let cols = 60; let cols = 60;
let default_row_height = '30px'; let default_row_height = '30px';
let default_col_width = '60px'; let default_col_width = '80px';
// Only store touched cells // Only store touched cells
let grid_vals: Record<string, CellData> = $state({}); let grid_vals: Record<string, CellData> = $state({});
@@ -150,7 +150,7 @@
else { else {
setCellRaw(i, j, v); setCellRaw(i, j, v);
console.log(i, j); console.log(i, j);
socket.send(`set ${toGridRef(i, j)} ${v}`); socket.send(`set ${refFromPos(i, j).str} ${v}`);
} }
}; };
@@ -170,7 +170,7 @@
e.preventDefault(); e.preventDefault();
// --- This is the same reference-inserting logic as before --- // --- This is the same reference-inserting logic as before ---
const ref = toGridRef(i, j); const ref = refFromPos(i, j).str;
if (el) { if (el) {
const { selectionStart, selectionEnd } = el; const { selectionStart, selectionEnd } = el;
const before = el.value.slice(0, selectionStart ?? 0); const before = el.value.slice(0, selectionStart ?? 0);
@@ -211,7 +211,7 @@
width={getColWidth(j)} width={getColWidth(j)}
setColWidth={(width) => setColWidth(j, width)} setColWidth={(width) => setColWidth(j, width)}
direction="col" direction="col"
val={toColLetter(j + 1)} val={colToStr(j)}
active={active_cell !== null && active_cell[1] === j} active={active_cell !== null && active_cell[1] === j}
/> />
{/each} {/each}

View File

@@ -5,7 +5,16 @@ export interface CellData {
val: CellValue; val: CellValue;
} }
export function fromGridRef(ref: string): [number, number] { export interface CellRef {
row: number;
col: number;
str: string;
}
/**
* Zero indexed | A1 == {row: 0, col: 0};
*/
export function refFromStr(ref: string): CellRef {
const match = ref.match(/^([A-Z]+)([0-9]+)$/i); const match = ref.match(/^([A-Z]+)([0-9]+)$/i);
if (!match) throw new Error('Invalid reference'); if (!match) throw new Error('Invalid reference');
@@ -17,25 +26,29 @@ export function fromGridRef(ref: string): [number, number] {
} }
const row = parseInt(rowStr, 10); const row = parseInt(rowStr, 10);
return [row - 1, col - 1]; return { row: row - 1, col: col - 1, str: ref };
} }
export function toColLetter(col: number): string { /**
* Zero indexed | 0 == A;
*/
export function colToStr(col: number): string {
let result = ''; let result = '';
let n = col; let n = col;
while (n > 0) { while (n >= 0) {
const rem = (n - 1) % 26; const rem = n % 26;
result = String.fromCharCode(65 + rem) + result; // 65 = 'A' result = String.fromCharCode(65 + rem) + result; // 65 = 'A'
n = Math.floor((n - 1) / 26); n = Math.floor(n / 26) - 1;
} }
return result; return result;
} }
export function toGridRef(row: number, col: number): string { /**
row++; * Zero indexed | A1 == {row: 0, col: 0};
col++; */
return toColLetter(col) + row.toString(); export function refFromPos(row: number, col: number): CellRef {
return { row, col, str: colToStr(col) + (row + 1).toString() };
} }
export function splitErrorString(errorString: string) { export function splitErrorString(errorString: string) {