This commit is contained in:
2025-09-02 23:30:14 +10:00
parent 4ddca1324a
commit 5c893d7ce7

View File

@@ -1,7 +1,19 @@
<script lang="ts">
import { Input } from "$lib/components/ui/input";
import { onMount } from "svelte";
const socket = new WebSocket("ws://localhost:7050");
socket.onmessage = (event) => {
const message = event.data;
console.log("Received message from server:", message);
};
socket.onopen = () => {
console.log("WebSocket connection established.");
};
// Connection opened
// --- Configuration ---
const numRows = 1000; // Increased to show performance
const numCols = 100; // Increased to show performance
@@ -52,6 +64,23 @@
}
}
function handleCellBlur(row: number, col: number) {
// This function runs when a cell loses focus.
// You can add your logic here, e.g., validation, calculations, etc.
console.log(
`Cell (${row + 1}, ${columnLabels[col]}) lost focus. Value:`,
gridData[row][col],
);
socket.send(
JSON.stringify({
row: row + 1,
col: columnLabels[col],
value: gridData[row][col],
}),
);
}
// --- Reactive Calculations for Virtualization ---
// $: is a Svelte feature that re-runs code when its dependencies change.
@@ -89,7 +118,7 @@
--col-header-height: {colHeaderHeight}px;
"
>
<!-- The scrollable viewport -->
<!-- The scrollable viewport provides the scrollbars -->
<div
class="viewport"
bind:this={viewportElement}
@@ -100,19 +129,17 @@
scrollLeft = e.currentTarget.scrollLeft;
}}
>
<!-- Sizer div: creates the full scrollable area -->
<!-- Sizer div creates the full scrollable area -->
<div
class="total-sizer"
style:width="{numCols * colWidth}px"
style:height="{numRows * rowHeight}px"
/>
</div>
<!-- The Renderer sits on top of the viewport and handles drawing -->
{#if gridData}
<!-- Render Window: contains only the visible cells -->
<div
class="render-window"
style:transform="translate({scrollLeft}px, {scrollTop}px)"
>
<div class="renderer">
<!-- Top-left corner -->
<div
class="top-left-corner"
@@ -122,7 +149,7 @@
<!-- Visible Column Headers -->
<div
class="col-headers-container"
style:transform="translateX(-{scrollLeft % colWidth}px)"
style:transform="translateX(-{scrollLeft}px)"
>
{#each visibleCols as j (j)}
<div
@@ -138,7 +165,7 @@
<!-- Visible Row Headers -->
<div
class="row-headers-container"
style:transform="translateY(-{scrollTop % rowHeight}px)"
style:transform="translateY(-{scrollTop}px)"
>
{#each visibleRows as i (i)}
<div
@@ -154,8 +181,7 @@
<!-- Visible Grid Cells -->
<div
class="cells-container"
style:transform="translate(-{scrollLeft % colWidth}px, -{scrollTop %
rowHeight}px)"
style:transform="translate(-{scrollLeft}px, -{scrollTop}px)"
>
{#each visibleRows as i (i)}
{#each visibleCols as j (j)}
@@ -164,11 +190,13 @@
style:top="{i * rowHeight}px"
style:left="{j * colWidth}px"
>
<Input
<!-- Using a standard input to ensure styles are applied correctly -->
<input
type="text"
bind:value={gridData[i][j]}
class="cell-input"
on:focus={() => (activeCell = [i, j])}
on:blur={() => handleCellBlur(i, j)}
/>
<!-- Active cell indicator with fill handle -->
{#if activeCell && activeCell[0] === i && activeCell[1] === j}
@@ -184,7 +212,6 @@
{/if}
</div>
</div>
</div>
<style>
.grid-container {
@@ -199,20 +226,23 @@
width: 100%;
height: 100%;
overflow: auto;
position: relative;
position: absolute;
top: 0;
left: 0;
}
.total-sizer {
position: relative;
pointer-events: none;
}
.render-window {
.renderer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
pointer-events: none; /* Pass clicks through to elements below */
}
.top-left-corner,
@@ -291,42 +321,51 @@
border-right: 1px solid hsl(var(--border) / 0.7);
border-bottom: 1px solid hsl(var(--border) / 0.7);
pointer-events: auto;
background-color: hsl(var(--background));
}
.cell-input {
/* Overriding all default input styles */
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 100%;
height: 100%;
padding: 0 0.5rem;
margin: 0;
background-color: transparent;
border: none;
border-radius: 0;
border: 1px solid black;
font-size: 0.875rem;
font-family: inherit;
text-align: left;
outline: none;
box-shadow: none;
}
.cell-input:focus {
box-shadow: none; /* Removes shadcn default focus ring */
/* Ensure no focus styles are added by the browser or libraries */
.cell-input:focus,
.cell-input:focus-visible {
border: 1px solid red;
}
.active-cell-indicator {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
top: -1px;
left: -1px;
width: calc(100% + 2px);
height: calc(100% + 2px);
border: 2px solid hsl(var(--primary));
pointer-events: none;
z-index: 5;
}
.fill-handle {
position: absolute;
bottom: -3px;
right: -3px;
width: 5px;
height: 5px;
bottom: -4px;
right: -4px;
width: 6px;
height: 6px;
background: hsl(var(--primary));
border: 1px solid hsl(var(--primary-foreground));
cursor: crosshair;
}
</style>