Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

feat: Improved Keyboard and Mouse Control and selection capabilities #216

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"start": "node dev-server.js",
"test": "echo \"Error: no test specified\" && exit 0",
"lint": "yarn run eslint ./src"
},
},
"author": "Heartex, Inc.",
"license": "ISC",
"devDependencies": {
Expand Down Expand Up @@ -79,7 +79,7 @@
"react-datepicker": "^3.6.0",
"react-dom": "^17.0.2",
"react-hot-loader": "^4.12.20",
"react-hotkeys-hook": "^2.4.0",
"react-hotkeys-hook": "^4.0.0",
"react-icons": "^3.11.0",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.6",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/Table/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export const Table = observer(
const highlightedElement = tableWrapper.current?.children[highlightedIndex];

if (highlightedElement) highlightedElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, [tableWrapper.current]);
}, [tableWrapper.current, focusedItem]);

return (
<>
Expand Down
17 changes: 10 additions & 7 deletions src/components/Common/TableOld/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,22 +245,23 @@ export const Table = observer(

const cachedScrollOffset = useRef();

const initialScrollOffset = useCallback((height) => {
const initialScrollOffset = useMemo(() => {
if (isDefined(cachedScrollOffset.current)) {
return cachedScrollOffset.current;
}

const { rowHeight: h } = props;
const { rowHeight } = props;
const index = data.indexOf(focusedItem);


if (index >= 0) {
const scrollOffset = index * h - height / 2 + h / 2; // + headerHeight
const scrollOffset = (index+(index+1)/data.length) * rowHeight;

return cachedScrollOffset.current = scrollOffset;
} else {
return 0;
}
}, []);
}, [focusedItem, props.rowHeight]);

const itemKey = useCallback(
(index) => {
Expand All @@ -276,9 +277,11 @@ export const Table = observer(
const listComponent = listRef.current?._listRef;

if (listComponent) {
listComponent.scrollToItem(data.indexOf(focusedItem), "center");
const isLast = data[data.length-1].id === focusedItem?.id;

listComponent.scrollToItem(data.indexOf(focusedItem), isLast?"start":"center");
}
}, [data]);
}, [data, focusedItem]);
const tableWrapper = useRef();

const right = tableWrapper.current?.firstChild?.firstChild.offsetWidth -
Expand Down Expand Up @@ -410,7 +413,7 @@ const StickyList = observer(
itemData={itemData}
itemSize={itemSize}
onItemsRendered={onItemsRendered}
initialScrollOffset={initialScrollOffset?.(height) ?? 0}
initialScrollOffset={initialScrollOffset}
>
{ItemWrapper}
</TableElem>
Expand Down
93 changes: 86 additions & 7 deletions src/components/MainView/DataView/DataView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { inject, observer } from "mobx-react";
import { getRoot } from "mobx-state-tree";
import { useCallback, useMemo, useState } from "react";
import { FaQuestionCircle } from "react-icons/fa";
import { useShortcut } from "../../../sdk/hotkeys";
import { isShortcutPressed, useShortcut } from "../../../sdk/hotkeys";
import { Block, Elem } from "../../../utils/bem";
import { FF_DEV_2536, FF_DEV_4008, isFF } from '../../../utils/feature-flags';
import * as CellViews from "../../CellViews";
Expand All @@ -16,6 +16,7 @@ import { Tooltip } from "../../Common/Tooltip/Tooltip";
import { GridView } from "../GridView/GridView";
import { CandidateTaskView } from "../../CandidateTaskView";
import { modal } from "../../Common/Modal/Modal";
import { isDefined } from "../../../utils/utils";
import "./DataView.styl";

const injector = inject(({ store }) => {
Expand All @@ -36,7 +37,7 @@ const injector = inject(({ store }) => {
isLoading: dataStore?.loading ?? true,
isLocked: currentView?.locked ?? false,
hasData: (store.project?.task_count ?? store.project?.task_number ?? dataStore?.total ?? 0) > 0,
focusedItem: dataStore?.selected ?? dataStore?.highlighted,
focusedItem: dataStore?.scroll ?? dataStore?.selected ?? dataStore?.highlighted ?? dataStore?.list[0],
};

return props;
Expand Down Expand Up @@ -114,8 +115,32 @@ export const DataView = injector(
}, [view]);

const onRowSelect = useCallback((id) => {
view.toggleSelected(id);
}, [view]);
const { highlightedId } = dataStore;

const bulk_select = isShortcutPressed("dm.bulk-select-mouse");
const bulk_deselect = isShortcutPressed("dm.bulk-deselect-mouse");

if(!bulk_select && !bulk_deselect){
view.toggleSelected(id);
}
if (isDefined(highlightedId)){
const ids = dataStore.list.map(({ id })=>id);
const _id_high = ids.indexOf(highlightedId);
const _id_curr = ids.indexOf(id);
const range = ids.slice(
Math.min(_id_high, _id_curr),
Math.max(_id_high, _id_curr)+1,
);

if (bulk_select){
view.selected.selectItems(...range);
}
else if (bulk_deselect){
view.selected.deselectItems(...range);
}
}
dataStore.focusItem(id, false);
}, [view, dataStore.highlightedId, dataStore.list]);

const onRowClick = useCallback(
(item, e) => {
Expand Down Expand Up @@ -273,15 +298,15 @@ export const DataView = injector(
useShortcut("dm.focus-previous", () => {
if (document.activeElement !== document.body) return;

const task = dataStore.focusPrev();
const task = dataStore.focusPrev(true);

if (isFF(FF_DEV_4008)) getRoot(view).startLabeling(task);
});

useShortcut("dm.focus-next", () => {
if (document.activeElement !== document.body) return;

const task = dataStore.focusNext();
const task = dataStore.focusNext(true);

if (isFF(FF_DEV_4008)) getRoot(view).startLabeling(task);
});
Expand All @@ -298,7 +323,61 @@ export const DataView = injector(
const { highlighted } = dataStore;
// don't close QuickView by Enter

if (highlighted && !highlighted.isSelected) store.startLabeling(highlighted);
if (highlighted && !highlighted.isSelected){
store.startLabeling(highlighted);
}
});

useShortcut("dm.toggle-focused", ()=>{
if (document.activeElement !== document.body) return;

const { highlighted } = dataStore;

if (highlighted) onRowSelect(highlighted.id);
});

useShortcut("dm.select-next", ()=>{
if (document.activeElement !== document.body) return;

view.selected.selectItems(
dataStore.highlightedId,
dataStore.focusNext(true).id,
);
document.getSelection().removeAllRanges();
});

useShortcut("dm.select-prev", ()=>{
if (document.activeElement !== document.body) return;

view.selected.selectItems(
dataStore.highlightedId,
dataStore.focusPrev(true).id,
);
document.getSelection().removeAllRanges();
});

useShortcut("dm.deselect-next", ()=>{
if (document.activeElement !== document.body) return;

view.selected.deselectItems(
dataStore.highlightedId,
dataStore.focusNext(true).id,
);
});

useShortcut("dm.deselect-prev", ()=>{
if (document.activeElement !== document.body) return;

view.selected.deselectItems(
dataStore.highlightedId,
dataStore.focusPrev(true).id,
);
});

useShortcut("dm.select-all", ()=>{
if (document.activeElement !== document.body) return;

onSelectAll();
});

// Render the UI for the table
Expand Down
99 changes: 90 additions & 9 deletions src/components/MainView/DataViewOld/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { inject } from "mobx-react";
import { getRoot } from "mobx-state-tree";
import { useCallback, useMemo } from "react";
import { FaQuestionCircle } from "react-icons/fa";
import { useShortcut } from "../../../sdk/hotkeys";
import { isShortcutPressed, useShortcut } from "../../../sdk/hotkeys";
import { Block, Elem } from "../../../utils/bem";
import { FF_DEV_2536, FF_DEV_4008, isFF } from '../../../utils/feature-flags';
import * as CellViews from "../../CellViews";
Expand All @@ -16,6 +16,7 @@ import { GridView } from "../GridViewOld/GridView";
import { CandidateTaskView } from "../../CandidateTaskView";
import "./Table.styl";
import { modal } from "../../Common/Modal/Modal";
import { isDefined } from "../../../utils/utils";

const injector = inject(({ store }) => {
const { dataStore, currentView } = store;
Expand All @@ -35,7 +36,7 @@ const injector = inject(({ store }) => {
isLoading: dataStore?.loading ?? true,
isLocked: currentView?.locked ?? false,
hasData: (store.project?.task_count ?? store.project?.task_number ?? dataStore?.total ?? 0) > 0,
focusedItem: dataStore?.selected ?? dataStore?.highlighted,
focusedItem: dataStore?.scroll ?? dataStore?.selected ?? dataStore?.highlighted ?? dataStore?.list[0],
};

return props;
Expand Down Expand Up @@ -107,11 +108,37 @@ export const DataView = injector(
[],
);

const onSelectAll = useCallback(() => view.selectAll(), [view]);
const onSelectAll = useCallback(() => {
view.selectAll();
}, [view]);

const onRowSelect = useCallback((id) => view.toggleSelected(id), [
view,
]);
const onRowSelect = useCallback((id) => {
const { highlightedId } = dataStore;

const bulk_select = isShortcutPressed("dm.bulk-select-mouse");
const bulk_deselect = isShortcutPressed("dm.bulk-deselect-mouse");

if(!bulk_select && !bulk_deselect){
view.toggleSelected(id);
}
if (isDefined(highlightedId)){
const ids = dataStore.list.map(({ id })=>id);
const _id_high = ids.indexOf(highlightedId);
const _id_curr = ids.indexOf(id);
const range = ids.slice(
Math.min(_id_high, _id_curr),
Math.max(_id_high, _id_curr)+1,
);

if (bulk_select){
view.selected.selectItems(...range);
}
else if (bulk_deselect){
view.selected.deselectItems(...range);
}
}
dataStore.focusItem(id, false);
},[view, dataStore.highlightedId, dataStore.list]);

const onRowClick = useCallback(
(item, e) => {
Expand Down Expand Up @@ -274,15 +301,15 @@ export const DataView = injector(
useShortcut("dm.focus-previous", () => {
if (document.activeElement !== document.body) return;

const task = dataStore.focusPrev();
const task = dataStore.focusPrev(true);

if (isFF(FF_DEV_4008)) getRoot(view).startLabeling(task);
});

useShortcut("dm.focus-next", () => {
if (document.activeElement !== document.body) return;

const task = dataStore.focusNext();
const task = dataStore.focusNext(true);

if (isFF(FF_DEV_4008)) getRoot(view).startLabeling(task);
});
Expand All @@ -299,7 +326,61 @@ export const DataView = injector(
const { highlighted } = dataStore;
// don't close QuickView by Enter

if (highlighted && !highlighted.isSelected) store.startLabeling(highlighted);
if (highlighted && !highlighted.isSelected){
store.startLabeling(highlighted);
}
});

useShortcut("dm.toggle-focused", ()=>{
if (document.activeElement !== document.body) return;

const { highlighted } = dataStore;

if (highlighted) onRowSelect(highlighted.id);
});

useShortcut("dm.select-next", ()=>{
if (document.activeElement !== document.body) return;

view.selected.selectItems(
dataStore.highlightedId,
dataStore.focusNext(true).id,
);
document.getSelection().removeAllRanges();
});

useShortcut("dm.select-prev", ()=>{
if (document.activeElement !== document.body) return;

view.selected.selectItems(
dataStore.highlightedId,
dataStore.focusPrev(true).id,
);
document.getSelection().removeAllRanges();
});

useShortcut("dm.deselect-next", ()=>{
if (document.activeElement !== document.body) return;

view.selected.deselectItems(
dataStore.highlightedId,
dataStore.focusNext(true).id,
);
});

useShortcut("dm.deselect-prev", ()=>{
if (document.activeElement !== document.body) return;

view.selected.deselectItems(
dataStore.highlightedId,
dataStore.focusPrev(true).id,
);
});

useShortcut("dm.select-all", ()=>{
if (document.activeElement !== document.body) return;

onSelectAll();
});

// Render the UI for your table
Expand Down
Loading