1
0
Fork 0
forked from 2sys/phonograph

fix selection confusion when clicking between cells

This commit is contained in:
Brent Schroeter 2026-01-20 18:53:58 +00:00
parent d575c058ee
commit c6ac79f1de
3 changed files with 48 additions and 25 deletions

View file

@ -0,0 +1,12 @@
/**
* Propagating blur events upwards from the `<DatumEditor>` component is
* debounced so that switching focus between elements does not cause spurious
* `on_blur()` calls.
*
* The `<TableViewer>` component must wait at least 1 tick longer than this
* before updating selections on cell click, or any updates to the previous
* selection may be applied to the new selection instead. (This approach is not
* technically airtight if click events are happening very quickly, but it's
* plenty close enough when we're working with a human user.)
*/
export const BLUR_DEBOUNCE_MS = 100;

View file

@ -86,7 +86,7 @@ example within the `<TableViewer />` or `<ExpressionEditor />`.
} }
function handle_blur(ev: FocusEvent) { function handle_blur(ev: FocusEvent) {
// Propagating of blur events upwards is debounced, so that switching focus // Propagating blur events upwards is debounced so that switching focus
// between elements does not cause spurious `on_blur()` calls. // between elements does not cause spurious `on_blur()` calls.
if (blur_timeout !== undefined) { if (blur_timeout !== undefined) {
clearTimeout(blur_timeout); clearTimeout(blur_timeout);

View file

@ -24,6 +24,7 @@
parse_datum_from_text, parse_datum_from_text,
} from "./datum.svelte"; } from "./datum.svelte";
import DatumEditor from "./datum-editor.svelte"; import DatumEditor from "./datum-editor.svelte";
import { BLUR_DEBOUNCE_MS } from "./datum-editor-common.svelte";
import { type Row, type FieldInfo, field_info_schema } from "./field.svelte"; import { type Row, type FieldInfo, field_info_schema } from "./field.svelte";
import FieldAdder from "./field-adder.svelte"; import FieldAdder from "./field-adder.svelte";
import FieldHeader from "./field-header.svelte"; import FieldHeader from "./field-header.svelte";
@ -672,18 +673,23 @@
region: "main", region: "main",
rows: lazy_data.rows, rows: lazy_data.rows,
on_cell_click: (ev: MouseEvent, coords: Coords) => { on_cell_click: (ev: MouseEvent, coords: Coords) => {
if (ev.metaKey || ev.ctrlKey) { // Must wait out `BLUR_DEBOUNCE_MS` before switching selection to
set_selections([ // avoid committing updates to the wrong cells. Refer to docs for
coords, // `BLUR_DEBOUNCE_MS`.
...selections setTimeout(() => {
.filter((sel) => !coords_eq(sel.coords, coords)) if (ev.metaKey || ev.ctrlKey) {
.map((sel) => sel.coords), set_selections([
]); coords,
} else if (ev.shiftKey) { ...selections
move_cursor(coords, { additive: true }); .filter((sel) => !coords_eq(sel.coords, coords))
} else { .map((sel) => sel.coords),
move_cursor(coords); ]);
} } else if (ev.shiftKey) {
move_cursor(coords, { additive: true });
} else {
move_cursor(coords);
}
}, BLUR_DEBOUNCE_MS + 1);
}, },
})} })}
</div> </div>
@ -705,18 +711,23 @@
region: "inserter", region: "inserter",
rows: inserter_rows, rows: inserter_rows,
on_cell_click: (ev: MouseEvent, coords: Coords) => { on_cell_click: (ev: MouseEvent, coords: Coords) => {
if (ev.metaKey || ev.ctrlKey) { // Must wait out `BLUR_DEBOUNCE_MS` before switching selection
set_selections([ // to avoid committing updates to the wrong cells. Refer to docs
coords, // for `BLUR_DEBOUNCE_MS`.
...selections setTimeout(() => {
.filter((sel) => !coords_eq(sel.coords, coords)) if (ev.metaKey || ev.ctrlKey) {
.map((sel) => sel.coords), set_selections([
]); coords,
} else if (ev.shiftKey) { ...selections
move_cursor(coords, { additive: true }); .filter((sel) => !coords_eq(sel.coords, coords))
} else { .map((sel) => sel.coords),
move_cursor(coords); ]);
} } else if (ev.shiftKey) {
move_cursor(coords, { additive: true });
} else {
move_cursor(coords);
}
}, BLUR_DEBOUNCE_MS + 1);
}, },
})} })}
</div> </div>