diff --git a/svelte/src/datum-editor-common.svelte.ts b/svelte/src/datum-editor-common.svelte.ts new file mode 100644 index 0000000..b6cd4f8 --- /dev/null +++ b/svelte/src/datum-editor-common.svelte.ts @@ -0,0 +1,12 @@ +/** + * Propagating blur events upwards from the `` component is + * debounced so that switching focus between elements does not cause spurious + * `on_blur()` calls. + * + * The `` 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; diff --git a/svelte/src/datum-editor.svelte b/svelte/src/datum-editor.svelte index 4277a91..d3b3c85 100644 --- a/svelte/src/datum-editor.svelte +++ b/svelte/src/datum-editor.svelte @@ -86,7 +86,7 @@ example within the `` or ``. } 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. if (blur_timeout !== undefined) { clearTimeout(blur_timeout); diff --git a/svelte/src/table-viewer.webc.svelte b/svelte/src/table-viewer.webc.svelte index d3576cd..c9f7ba1 100644 --- a/svelte/src/table-viewer.webc.svelte +++ b/svelte/src/table-viewer.webc.svelte @@ -24,6 +24,7 @@ parse_datum_from_text, } from "./datum.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 FieldAdder from "./field-adder.svelte"; import FieldHeader from "./field-header.svelte"; @@ -672,18 +673,23 @@ region: "main", rows: lazy_data.rows, on_cell_click: (ev: MouseEvent, coords: Coords) => { - if (ev.metaKey || ev.ctrlKey) { - set_selections([ - coords, - ...selections - .filter((sel) => !coords_eq(sel.coords, coords)) - .map((sel) => sel.coords), - ]); - } else if (ev.shiftKey) { - move_cursor(coords, { additive: true }); - } else { - move_cursor(coords); - } + // Must wait out `BLUR_DEBOUNCE_MS` before switching selection to + // avoid committing updates to the wrong cells. Refer to docs for + // `BLUR_DEBOUNCE_MS`. + setTimeout(() => { + if (ev.metaKey || ev.ctrlKey) { + set_selections([ + coords, + ...selections + .filter((sel) => !coords_eq(sel.coords, coords)) + .map((sel) => sel.coords), + ]); + } else if (ev.shiftKey) { + move_cursor(coords, { additive: true }); + } else { + move_cursor(coords); + } + }, BLUR_DEBOUNCE_MS + 1); }, })} @@ -705,18 +711,23 @@ region: "inserter", rows: inserter_rows, on_cell_click: (ev: MouseEvent, coords: Coords) => { - if (ev.metaKey || ev.ctrlKey) { - set_selections([ - coords, - ...selections - .filter((sel) => !coords_eq(sel.coords, coords)) - .map((sel) => sel.coords), - ]); - } else if (ev.shiftKey) { - move_cursor(coords, { additive: true }); - } else { - move_cursor(coords); - } + // Must wait out `BLUR_DEBOUNCE_MS` before switching selection + // to avoid committing updates to the wrong cells. Refer to docs + // for `BLUR_DEBOUNCE_MS`. + setTimeout(() => { + if (ev.metaKey || ev.ctrlKey) { + set_selections([ + coords, + ...selections + .filter((sel) => !coords_eq(sel.coords, coords)) + .map((sel) => sel.coords), + ]); + } else if (ev.shiftKey) { + move_cursor(coords, { additive: true }); + } else { + move_cursor(coords); + } + }, BLUR_DEBOUNCE_MS + 1); }, })}