🙃
This commit is contained in:
@@ -23,8 +23,13 @@ impl Grid {
|
|||||||
&mut self,
|
&mut self,
|
||||||
cell_ref: CellRef,
|
cell_ref: CellRef,
|
||||||
raw_val: String,
|
raw_val: String,
|
||||||
|
do_propagation: bool,
|
||||||
|
force_propagation: bool,
|
||||||
) -> Result<Vec<CellRef>, String> {
|
) -> Result<Vec<CellRef>, String> {
|
||||||
if self.cells.contains_key(&cell_ref) && self.cells[&cell_ref].raw() == raw_val {
|
if self.cells.contains_key(&cell_ref)
|
||||||
|
&& self.cells[&cell_ref].raw() == raw_val
|
||||||
|
&& !force_propagation
|
||||||
|
{
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +48,14 @@ impl Grid {
|
|||||||
|
|
||||||
if self.cells.contains_key(&cell_ref) {
|
if self.cells.contains_key(&cell_ref) {
|
||||||
updated_cells = self
|
updated_cells = self
|
||||||
.update_exisiting_cell(raw_val, eval, precs, cell_ref)?
|
.update_exisiting_cell(
|
||||||
|
raw_val,
|
||||||
|
eval,
|
||||||
|
precs,
|
||||||
|
cell_ref,
|
||||||
|
do_propagation,
|
||||||
|
force_propagation,
|
||||||
|
)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(updated_cells)
|
.chain(updated_cells)
|
||||||
.collect();
|
.collect();
|
||||||
@@ -156,6 +168,8 @@ impl Grid {
|
|||||||
new_eval: Eval,
|
new_eval: Eval,
|
||||||
new_precs: HashSet<CellRef>,
|
new_precs: HashSet<CellRef>,
|
||||||
cell_ref: CellRef,
|
cell_ref: CellRef,
|
||||||
|
do_propagation: bool,
|
||||||
|
force_propagation: bool,
|
||||||
) -> Result<Vec<CellRef>, String> {
|
) -> Result<Vec<CellRef>, String> {
|
||||||
let (old_precs, old_eval) = match self.cells.get_mut(&cell_ref) {
|
let (old_precs, old_eval) = match self.cells.get_mut(&cell_ref) {
|
||||||
Some(cell) => {
|
Some(cell) => {
|
||||||
@@ -197,7 +211,7 @@ impl Grid {
|
|||||||
cell.set_precs(new_precs);
|
cell.set_precs(new_precs);
|
||||||
cell.set_eval(new_eval);
|
cell.set_eval(new_eval);
|
||||||
|
|
||||||
if eval_changed {
|
if (eval_changed && do_propagation) || force_propagation {
|
||||||
self.propagate(cell_ref)
|
self.propagate(cell_ref)
|
||||||
} else {
|
} else {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
|
|||||||
@@ -62,8 +62,16 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
MsgType::Set => {
|
MsgType::Set => {
|
||||||
let Some(cell_ref) = req.cell else { continue };
|
let Some(cell_ref) = req.cell else { continue };
|
||||||
let Some(raw) = req.raw else { continue };
|
let Some(raw) = req.raw else { continue };
|
||||||
|
let Some(config) = req.eval_config else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
match grid.update_cell(cell_ref.clone(), raw.to_owned()) {
|
match grid.update_cell(
|
||||||
|
cell_ref.clone(),
|
||||||
|
raw.to_owned(),
|
||||||
|
config.do_propagation,
|
||||||
|
config.force_propagation,
|
||||||
|
) {
|
||||||
Ok(updates) => {
|
Ok(updates) => {
|
||||||
let mut msgs = Vec::new();
|
let mut msgs = Vec::new();
|
||||||
|
|
||||||
@@ -75,6 +83,7 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
raw: Some(cell.raw()),
|
raw: Some(cell.raw()),
|
||||||
eval: Some(cell.eval()),
|
eval: Some(cell.eval()),
|
||||||
bulk_msgs: None,
|
bulk_msgs: None,
|
||||||
|
eval_config: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +97,7 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
cell: None,
|
cell: None,
|
||||||
raw: None,
|
raw: None,
|
||||||
eval: None,
|
eval: None,
|
||||||
|
eval_config: None,
|
||||||
bulk_msgs: Some(msgs),
|
bulk_msgs: Some(msgs),
|
||||||
msg_type: MsgType::Bulk,
|
msg_type: MsgType::Bulk,
|
||||||
};
|
};
|
||||||
@@ -103,6 +113,7 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
cell: Some(cell_ref),
|
cell: Some(cell_ref),
|
||||||
raw: Some(e.to_string()),
|
raw: Some(e.to_string()),
|
||||||
eval: None,
|
eval: None,
|
||||||
|
eval_config: None,
|
||||||
bulk_msgs: None,
|
bulk_msgs: None,
|
||||||
};
|
};
|
||||||
let _ = write
|
let _ = write
|
||||||
|
|||||||
@@ -11,11 +11,18 @@ pub enum MsgType {
|
|||||||
Bulk,
|
Bulk,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct EvalConfig {
|
||||||
|
pub do_propagation: bool,
|
||||||
|
pub force_propagation: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct LeadMsg {
|
pub struct LeadMsg {
|
||||||
pub msg_type: MsgType,
|
pub msg_type: MsgType,
|
||||||
pub cell: Option<CellRef>,
|
pub cell: Option<CellRef>,
|
||||||
pub raw: Option<String>,
|
pub raw: Option<String>,
|
||||||
pub eval: Option<Eval>,
|
pub eval: Option<Eval>,
|
||||||
|
pub eval_config: Option<EvalConfig>,
|
||||||
pub bulk_msgs: Option<Vec<LeadMsg>>,
|
pub bulk_msgs: Option<Vec<LeadMsg>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,22 @@
|
|||||||
style="width: {width}; height: {height}"
|
style="width: {width}; height: {height}"
|
||||||
class="relative rounded-none p-1 !transition-none delay-0 duration-0
|
class="relative rounded-none p-1 !transition-none delay-0 duration-0
|
||||||
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
||||||
onblur={(e) => {
|
bind:value={
|
||||||
|
() => {
|
||||||
|
return cell?.raw_val ?? '';
|
||||||
|
},
|
||||||
|
(v) => {
|
||||||
cell = {
|
cell = {
|
||||||
val: cell?.val,
|
val: cell?.val,
|
||||||
raw_val: (e.target as HTMLInputElement).value
|
raw_val: v
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onblur={(e) => {
|
||||||
|
// cell = {
|
||||||
|
// val: cell?.val,
|
||||||
|
// raw_val: (e.target as HTMLInputElement).value
|
||||||
|
// };
|
||||||
stopediting();
|
stopediting();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { Pencil } from '@lucide/svelte';
|
import {
|
||||||
|
Infinity,
|
||||||
|
Omega,
|
||||||
|
Parentheses,
|
||||||
|
Pyramid,
|
||||||
|
Radical,
|
||||||
|
Sigma,
|
||||||
|
SquareFunction,
|
||||||
|
Variable
|
||||||
|
} from '@lucide/svelte';
|
||||||
import Cell from '$lib/components/grid/cell.svelte';
|
import Cell from '$lib/components/grid/cell.svelte';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import CellHeader from './cell-header.svelte';
|
import CellHeader from './cell-header.svelte';
|
||||||
@@ -108,14 +117,15 @@
|
|||||||
editing_cell = [i, j];
|
editing_cell = [i, j];
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopEditing() {
|
function stopEditing(i: number, j: number) {
|
||||||
editing_cell = null;
|
editing_cell = null;
|
||||||
|
setCell(i, j, getCell(i, j), { do_propagation: true, force_propagation: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = (i: number, j: number) => `${i}:${j}`;
|
const key = (i: number, j: number) => `${i}:${j}`;
|
||||||
|
|
||||||
const getCell = (i: number, j: number) => grid_vals[key(i, j)];
|
const getCell = (i: number, j: number) => grid_vals[key(i, j)];
|
||||||
const setCell = (row: number, col: number, v: CellT) => {
|
const setCell = (row: number, col: number, v: CellT, eval_config: EvalConfig) => {
|
||||||
if (v?.raw_val == null || v.raw_val === '') {
|
if (v?.raw_val == null || v.raw_val === '') {
|
||||||
delete grid_vals[key(row, col)];
|
delete grid_vals[key(row, col)];
|
||||||
return;
|
return;
|
||||||
@@ -129,7 +139,8 @@
|
|||||||
let msg: LeadMsg = {
|
let msg: LeadMsg = {
|
||||||
msg_type: 'set',
|
msg_type: 'set',
|
||||||
cell: { row, col },
|
cell: { row, col },
|
||||||
raw: v.raw_val
|
raw: v.raw_val,
|
||||||
|
eval_config
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.send(JSON.stringify(msg));
|
socket.send(JSON.stringify(msg));
|
||||||
@@ -182,38 +193,56 @@
|
|||||||
|
|
||||||
if (grid_vals[key(active_cell[0], active_cell[1])]) {
|
if (grid_vals[key(active_cell[0], active_cell[1])]) {
|
||||||
let cell = grid_vals[key(active_cell[0], active_cell[1])];
|
let cell = grid_vals[key(active_cell[0], active_cell[1])];
|
||||||
setCell(active_cell[0], active_cell[1], {
|
setCell(
|
||||||
|
active_cell[0],
|
||||||
|
active_cell[1],
|
||||||
|
{
|
||||||
raw_val: raw,
|
raw_val: raw,
|
||||||
val: cell.val
|
val: cell.val
|
||||||
});
|
},
|
||||||
|
{ do_propagation: false, force_propagation: false }
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setCell(active_cell[0], active_cell[1], {
|
setCell(
|
||||||
|
active_cell[0],
|
||||||
|
active_cell[1],
|
||||||
|
{
|
||||||
raw_val: raw,
|
raw_val: raw,
|
||||||
val: undefined
|
val: undefined
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
do_propagation: false,
|
||||||
|
force_propagation: false
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const handler = (e: MouseEvent) => {
|
// const handler = (e: MouseEvent) => {
|
||||||
// optional: check if click target is outside grid container
|
// optional: check if click target is outside grid container
|
||||||
if (!(e.target as HTMLElement).closest('.grid-wrapper')) {
|
// if (!(e.target as HTMLElement).closest('.grid-wrapper')) {
|
||||||
active_cell = null;
|
// active_cell = null;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
window.addEventListener('click', handler);
|
// window.addEventListener('click', handler);
|
||||||
onDestroy(() => window.removeEventListener('click', handler));
|
// onDestroy(() => window.removeEventListener('click', handler));
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-5 ml-5 flex items-center gap-5">
|
<div class="relative mb-5 ml-5 flex items-center gap-[5px]">
|
||||||
<Pencil />
|
<div class="relative">
|
||||||
|
<Omega
|
||||||
|
size="20px"
|
||||||
|
class="absolute top-1/2 left-2 -translate-y-1/2 text-muted-foreground"
|
||||||
|
strokeWidth={1}
|
||||||
|
/>
|
||||||
<Input
|
<Input
|
||||||
bind:value={() => getActiveCell().raw_val, (raw) => setActiveCellRaw(raw)}
|
bind:value={() => getActiveCell().raw_val, (raw) => setActiveCellRaw(raw)}
|
||||||
class="relative w-[200px] rounded-none p-1 !transition-none delay-0 duration-0
|
class="relative w-[200px] pl-8"
|
||||||
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
|
||||||
></Input>
|
></Input>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={clsx('grid-wrapper relative h-full min-h-0 max-w-full min-w-0 overflow-auto', className)}
|
class={clsx('grid-wrapper relative h-full min-h-0 max-w-full min-w-0 overflow-auto', className)}
|
||||||
@@ -259,11 +288,14 @@
|
|||||||
width={getColWidth(j)}
|
width={getColWidth(j)}
|
||||||
editing={editing_cell?.[0] === i && editing_cell?.[1] === j}
|
editing={editing_cell?.[0] === i && editing_cell?.[1] === j}
|
||||||
startediting={() => startEditing(i, j)}
|
startediting={() => startEditing(i, j)}
|
||||||
stopediting={stopEditing}
|
stopediting={() => stopEditing(i, j)}
|
||||||
onmousedown={(e) => {
|
onmousedown={(e) => {
|
||||||
handleCellInteraction(i, j, e);
|
handleCellInteraction(i, j, e);
|
||||||
}}
|
}}
|
||||||
bind:cell={() => getCell(i, j), (v) => setCell(i, j, v)}
|
bind:cell={
|
||||||
|
() => getCell(i, j),
|
||||||
|
(v) => setCell(i, j, v, { do_propagation: false, force_propagation: false })
|
||||||
|
}
|
||||||
active={active_cell !== null && active_cell[0] === i && active_cell[1] === j}
|
active={active_cell !== null && active_cell[0] === i && active_cell[1] === j}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ interface LeadMsg {
|
|||||||
cell?: CellRef;
|
cell?: CellRef;
|
||||||
raw?: string;
|
raw?: string;
|
||||||
eval?: Eval;
|
eval?: Eval;
|
||||||
|
eval_config?: EvalConfig;
|
||||||
bulk_msgs?: Array<LeadMsg>;
|
bulk_msgs?: Array<LeadMsg>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +32,11 @@ interface LeadErr {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EvalConfig {
|
||||||
|
do_propagation: boolean;
|
||||||
|
force_propagation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// Tagged union
|
// Tagged union
|
||||||
type Eval =
|
type Eval =
|
||||||
| { literal: Literal }
|
| { literal: Literal }
|
||||||
|
|||||||
Reference in New Issue
Block a user