Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
44b57f1
cosmetic
schloerke Jun 4, 2024
6b954a6
Allow for editable and selection modes to co exist
schloerke Jun 4, 2024
c064eb7
Merge branch 'epic/df' into df/cell_navigation
schloerke Jun 5, 2024
d83f043
First pass at row selection with row indexes; Turn useSelection into …
schloerke Jun 5, 2024
5095fa1
Merge branch 'epic/df' into df/cell_navigation
schloerke Jun 10, 2024
062c36c
Disable sorting via keyboard for html columns
schloerke Jun 10, 2024
eb029f2
Update TS to allow for cell edit and selection at same time
schloerke Jun 10, 2024
722dfab
Use double click to go into edit mode
schloerke Jun 10, 2024
3f0e9e3
Add TODOs
schloerke Jun 11, 2024
b573400
Update row index to start from 1, not 0
schloerke Jun 11, 2024
df7416b
Fix header label check
schloerke Jun 12, 2024
cba62df
Update cell locator method to exclude row-number td's
schloerke Jun 12, 2024
7bd54be
Update bug test to use controls where possible
schloerke Jun 12, 2024
19bcb63
Update app.py
schloerke Jun 12, 2024
48d9071
Update test_0676_row_selection.py
schloerke Jun 12, 2024
6c97f77
Fix mouse down on editing cell bug where row was focused. On "enter",…
schloerke Jun 12, 2024
b56b5bf
Update index.tsx
schloerke Jun 12, 2024
b3e9657
Use `useMemo` instead of `useState` for simple reactive calc
schloerke Jun 12, 2024
f6782a2
Only allow enter to start edit mode when edit is possible
schloerke Jun 13, 2024
1a575a0
Merge branch 'main' into df/cell_navigation
schloerke Jun 13, 2024
20f422a
Update CHANGELOG.md
schloerke Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `.update_sort(sort=)` allows app authors to programmatically update the sorting of the data frame. (#1374)
* `.update_filter(filter=)` allows app authors to programmatically update the filtering of the data frame. (#1374)

* `@render.data_frame` now accepts both a non-`"none"` `selection_mode` value and `editable=True`. (#1454)

* `@render.data_frame`'s `<ID>.cell_selection()` no longer returns a `None` value and now always returns a dictionary containing both the `rows` and `cols` keys. This is done to achieve more consistent author code when working with cell selection. When the value's `type="none"`, both `rows` and `cols` are empty tuples. When `type="row"`, `cols` represents all column numbers of the data. In the future, when `type="col"`, `rows` will represent all row numbers of the data. These extra values are not available in `input.<ID>_cell_selection()` as they are independent of cells being selected and are removed to reduce information being sent to and from the browser. (#1376)


Expand Down
10 changes: 9 additions & 1 deletion js/data-frame/cell-edit-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { CellState } from "./cell";
// enableMapSet();

export type CellEdit = {
// rowIndex: number;
// columnIndex: number;
value?: string;
state?: CellState;
errorTitle?: string;
Expand Down Expand Up @@ -37,10 +39,16 @@ export const useCellEditMap = () => {
const key = makeCellEditMapKey(rowIndex, columnIndex);
const obj = draft.get(key) ?? ({} as CellEdit);
obj_fn(obj);
// obj.rowIndex = rowIndex;
// obj.columnIndex = columnIndex;
draft.set(key, obj);
});
};
return { cellEditMap, setCellEditMap, setCellEditMapAtLoc } as const;
return {
cellEditMap,
// setCellEditMap,
setCellEditMapAtLoc,
} as const;
};

export const makeCellEditMapKey = (rowIndex: number, columnIndex: number) => {
Expand Down
43 changes: 32 additions & 11 deletions js/data-frame/cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import React, {
} from "react";
import { CellEdit, SetCellEditMapAtLoc } from "./cell-edit-map";
import { updateCellsData } from "./data-update";
import { SelectionSet } from "./selection";
import type { PatchInfo } from "./types";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -36,7 +37,7 @@ export const CellStateEnum = {
Editing: "Editing",
Ready: "Ready",
} as const;
const CellStateClassEnum = {
export const CellStateClassEnum = {
EditSaving: "cell-edit-saving",
EditSuccess: "cell-edit-success",
EditFailure: "cell-edit-failure",
Expand Down Expand Up @@ -82,6 +83,7 @@ interface TableBodyCellProps {
setData: (fn: (draft: unknown[][]) => void) => void;
cellEditInfo: CellEdit | undefined;
setCellEditMapAtLoc: SetCellEditMapAtLoc;
selection: SelectionSet<string, HTMLTableRowElement>;
}

export const TableBodyCell: FC<TableBodyCellProps> = ({
Expand All @@ -98,6 +100,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
cellEditInfo,
setData,
setCellEditMapAtLoc,
selection,
}) => {
const initialValue = cell.getValue() as
| string
Expand All @@ -121,11 +124,11 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
// * When editing a cell:
// * On esc key:
// * √ Restore prior value / state / error
// * Move focus from input to td
// * Move focus from input to td
// * On enter key:
// * √ Save value
// * √ Move to the cell below (or above w/ shift) and edit the new cell
// * Should shift+enter add a newline in a cell?
// * X Should shift+enter add a newline in a cell?
// * On tab key:
// * √ Save value
// * √ Move to the cell to the right (or left w/ shift) and edit the new cell
Expand All @@ -137,8 +140,8 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
// * When focused on a td:
// * Allow for arrow key navigation
// * Have enter key enter edit mode for a cell
// * When a td is focused, Have esc key move focus to the table
// * When table is focused, Have esc key blur the focus
// * When a td is focused, Have esc key move focus to the table
// * X When table is focused, Have esc key blur the focus
// TODO-barret-future; Combat edit mode being independent of selection mode
// * In row / column selection mode, allow for arrow key navigation by focusing on a single cell, not a TR
// * If a cell is focused,
Expand Down Expand Up @@ -168,14 +171,17 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
if (e.key !== "Escape") return;
// Prevent default behavior
e.preventDefault();
e.stopPropagation();

// Turn off editing and the _temp_ edit value
resetEditing();
selection.focusOffset(rowId, 0);
};
const handleTab = (e: ReactKeyboardEvent<HTMLTextAreaElement>) => {
if (e.key !== "Tab") return;
// Prevent default behavior
e.preventDefault();
e.stopPropagation();

const hasShift = e.shiftKey;

Expand Down Expand Up @@ -209,6 +215,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
if (e.key !== "Enter") return;
// Prevent default behavior
e.preventDefault();
e.stopPropagation();

const hasShift = e.shiftKey;

Expand Down Expand Up @@ -309,25 +316,39 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({

// When editing a cell, set up a global click listener to reset edit info when
// clicking outside of the cell
// Use MouseDown event to match how selection is performed to prevent the click from bubbling up
useEffect(() => {
if (!isEditing) return;
if (!tdRef.current) return;
if (!inputRef.current) return;

// TODO-barret; Restore cursor position and text selection here

const onEdtingCellMouseDown = (e: MouseEvent) => {
if (!tdRef.current?.contains(e.target as Node)) return;
// Prevent the click from bubbling up to the body click listener
e.stopPropagation();

// Do not stop the event from preventing default as we need the click to work for the text area!
// e.preventDefault();
};
const curRef = tdRef.current; // Capture the current ref
curRef.addEventListener("mousedown", onEdtingCellMouseDown);

// Set up global click listener to reset edit info
const onBodyClick = (e: MouseEvent) => {
const onBodyMouseDown = (e: MouseEvent) => {
if (e.target === inputRef.current) return;

attemptUpdate();
// Turn off editing for this cell
resetEditing();
};
document.body.addEventListener("click", onBodyClick);
document.body.addEventListener("mousedown", onBodyMouseDown);

// Tear down global click listener when we're done
return () => {
document.body.removeEventListener("click", onBodyClick);
curRef.removeEventListener("mousedown", onEdtingCellMouseDown);
document.body.removeEventListener("mousedown", onBodyMouseDown);
};
}, [
cellState,
Expand Down Expand Up @@ -373,7 +394,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
// };
// useAutosizeTextArea(inputRef.current, value as string);

let onClick:
let onCellDoubleClick:
| ((e: ReactMouseEvent<HTMLTableCellElement>) => void)
| undefined = undefined;
let content: ReactElement | ReturnType<typeof flexRender> | undefined =
Expand Down Expand Up @@ -417,7 +438,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
// Only allow transition to edit mode if the cell can be edited
if (editCellsIsAllowed && !isHtmlColumn) {
addToTableCellClass("cell-editable");
onClick = (e: ReactMouseEvent<HTMLTableCellElement>) => {
onCellDoubleClick = (e: ReactMouseEvent<HTMLTableCellElement>) => {
// Do not prevent default or stop propagation here!
// Other methods need to be able to handle the event as well. e.g. `onBodyClick` above.
// e.preventDefault();
Expand Down Expand Up @@ -463,7 +484,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({
return (
<td
ref={tdRef}
onClick={onClick}
onDoubleClick={onCellDoubleClick}
title={cellTitle}
className={tableCellClass}
>
Expand Down
6 changes: 1 addition & 5 deletions js/data-frame/data-update.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { ResponseValue, makeRequestPromise } from "./request";

import { CellStateEnum } from "./cell";
import {
CellEdit,
SetCellEditMapAtLoc,
makeCellEditMapKey,
} from "./cell-edit-map";
import { SetCellEditMapAtLoc } from "./cell-edit-map";
import type { PatchInfo } from "./types";

export type CellPatch = {
Expand Down
2 changes: 1 addition & 1 deletion js/data-frame/dom-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// scroll container
export function findFirstItemInView(
scrollContainer: HTMLElement,
items: NodeList,
items: NodeList | ChildNode[] | Element[] | HTMLElement[],
extraPadding?: {
top?: number;
right?: number;
Expand Down
10 changes: 1 addition & 9 deletions js/data-frame/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ import {
ColumnFiltersState,
FiltersOptions,
Header,
TableOptions,
getFacetedMinMaxValues,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
} from "@tanstack/react-table";
import React, {
FC,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react";
import React, { FC, useState } from "react";
import { FilterNumeric } from "./filter-numeric";

type FilterValueString = string;
Expand Down
Loading