Skip to content

Commit 36f3c00

Browse files
authored
Merge pull request #101 from LuCEresearchlab/keyboard-controls
Keyboard controls
2 parents b78ab0f + f19240e commit 36f3c00

File tree

56 files changed

+6124
-93
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+6124
-93
lines changed

frontend/src/components/diff_component/AnswersMerger.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from "react";
22
import {Grid} from "@material-ui/core";
3-
import {useFetch} from "../../helpers/LoaderHelper";
43
import {TaggedAnswer} from "../../interfaces/TaggedAnswer";
54
import MisconceptionTagComparer from "./MisconceptionTagComparer";
65
import {MisconceptionElement} from "../../interfaces/MisconceptionElement";
76
import stringEquals from "../../util/StringEquals";
7+
import {useFetch} from "../../hooks/useFetch";
88

99

1010
const {TAGGING_SERVICE_URL} = require('../../../config.json')

frontend/src/components/tagger_component/MisconceptionInfoButton.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, {useState} from "react"
1+
import React, {useRef, useState} from "react"
22
import HelpIcon from "@material-ui/icons/Help";
33
import {Button, Popover} from "@material-ui/core";
44
import {createStyles, makeStyles} from "@material-ui/core/styles";
55
import stringEquals from "../../util/StringEquals";
66
import NoMisconception from "../../util/NoMisconception";
7+
import withKeyboard from "../../hooks/withKeyboard";
78

89
const useStyles = makeStyles(() =>
910
createStyles({
@@ -16,12 +17,14 @@ const useStyles = makeStyles(() =>
1617
interface Input {
1718
handled_element: number,
1819
tags: (string | null)[],
20+
keyboardIndex?: string
1921
}
2022

21-
function MisconceptionInfoButton({tags, handled_element}: Input){
22-
23+
function MisconceptionInfoButton({tags, handled_element, keyboardIndex}: Input) {
2324
const classes = useStyles()
2425

26+
const ref = useRef<HTMLButtonElement>(null)
27+
2528
const tag: (string | null) = tags[handled_element]
2629

2730
const should_display = () => {
@@ -43,14 +46,20 @@ function MisconceptionInfoButton({tags, handled_element}: Input){
4346
const id = open ? "simple-popover" : undefined;
4447
// end popup stuff
4548

49+
withKeyboard((command => {
50+
if (keyboardIndex != undefined && (command == '' + keyboardIndex + 'h')) {
51+
ref.current?.click()
52+
}
53+
}))
54+
4655
return (
4756
!should_display() ?
4857
<>
4958
<Button disabled={true} className={classes.root}>
5059
</Button>
5160
</> :
5261
<>
53-
<Button title={"Definition"} onClick={handle_click_popup} className={classes.root}>
62+
<Button title={"Definition"} ref={ref} onClick={handle_click_popup} className={classes.root}>
5463
<HelpIcon/>
5564
</Button>
5665
<Popover

frontend/src/components/v2/TaggingUI.tsx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, {useEffect, useState} from "react";
22
import {Grid, Paper, Tab} from "@material-ui/core";
3-
import {useFetch} from "../../helpers/LoaderHelper";
43
import {StyledPagination} from "../styled/StyledPagination";
54
import {createStyles, makeStyles} from "@material-ui/core/styles";
65
import {TabContext, TabList, TabPanel} from "@material-ui/lab";
@@ -10,13 +9,20 @@ import TagView from "./tagger_component/TagView";
109
import ClusterView from "./tagger_component/ClusterView";
1110

1211
import {LIGHT_GREY} from "../../util/Colors"
13-
import {setCurrentQuestion} from "../../model/TaggingSessionDispatch";
12+
import {nextQuestion, setCurrentQuestion} from "../../model/TaggingSessionDispatch";
1413
import {
1514
TaggingClusterSession, TaggingClusterSessionDispatch,
1615
} from "../../model/TaggingClusterSession";
1716
import {TaggingSession, TaggingSessionDispatch} from "../../model/TaggingSession";
18-
import {setAvailableMisconceptions, setClusters, setCurrentCluster} from "../../model/TaggingClusterSessionDispatch";
17+
import {
18+
nextCluster,
19+
setAvailableMisconceptions,
20+
setClusters,
21+
setCurrentCluster
22+
} from "../../model/TaggingClusterSessionDispatch";
1923
import ClusterHandler from "./cluster_handler_component/ClusterHandler";
24+
import withKeyboard from "../../hooks/withKeyboard";
25+
import {useFetch} from "../../hooks/useFetch";
2026

2127
const {TAGGING_SERVICE_URL} = require('../../../config.json')
2228

@@ -60,6 +66,37 @@ function TaggingUI({taggingSession, dispatchTaggingSession, taggingClusterSessio
6066
const [tab, setTab] = useState<string>('1')
6167

6268

69+
const total_clusters = taggingClusterSession.clusters.length
70+
71+
const [keyHistory] = withKeyboard((command: string) => {
72+
if (command == '') // two spaces or enter in a row
73+
{
74+
const current_cluster = taggingClusterSession.currentCluster
75+
if (current_cluster + 1 < total_clusters) {
76+
dispatchTaggingClusterSession(nextCluster())
77+
setPage(page + 1) // offset of 1
78+
} else {
79+
dispatchTaggingSession(nextQuestion())
80+
dispatchTaggingClusterSession(setCurrentCluster(0))
81+
setPage(1)
82+
}
83+
}
84+
if (command == 'c') setTab(tab == '1' ? '2' : '1')
85+
if (command == 'b') {
86+
const current_cluster = taggingClusterSession.currentCluster
87+
if (current_cluster - 1 >= 0) {
88+
dispatchTaggingClusterSession(setCurrentCluster(current_cluster - 1))
89+
setPage(page - 1)
90+
} else {
91+
const previousQuestion = taggingSession.currentQuestion == 0 ? 0 : taggingSession.currentQuestion - 1
92+
dispatchTaggingSession(setCurrentQuestion(previousQuestion))
93+
dispatchTaggingClusterSession(setCurrentCluster(0))
94+
setPage(1)
95+
}
96+
}
97+
})
98+
99+
63100
const paginationChange = (event: any, value: number) => {
64101
dispatchTaggingClusterSession(setCurrentCluster(value - 1))
65102
setPage(value);
@@ -79,8 +116,6 @@ function TaggingUI({taggingSession, dispatchTaggingSession, taggingClusterSessio
79116
const isLoadingClusters = clusterFetch.isLoading
80117

81118

82-
const total_clusters = taggingClusterSession.clusters.length
83-
84119
useEffect(() => {
85120
if (!isLoadingClusters) dispatchTaggingClusterSession(setClusters(clustersData.clusters))
86121
}, [clustersData, isLoadingClusters])
@@ -157,9 +192,17 @@ function TaggingUI({taggingSession, dispatchTaggingSession, taggingClusterSessio
157192
/>
158193
</TabPanel>
159194
</TabContext>
195+
<div>
196+
{
197+
keyHistory == '' ?
198+
'space: next cluster, c: cluster view / tagging view' :
199+
'command: ' + keyHistory
200+
}
201+
</div>
160202
</Grid>
161203
</Grid>
162204
)
205+
163206
}
164207

165208
export default TaggingUI

frontend/src/components/v2/tagger_component/ClusterView.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {GREY} from "../../../util/Colors"
99
import Highlightable from "highlightable";
1010

1111
import {TaggedAnswer} from "../../../interfaces/TaggedAnswer";
12-
import {useFetch} from "../../../helpers/LoaderHelper";
1312
import {
1413
getCurrentCluster, getCurrentMisconception,
1514
getRanges, isUsingDefaultColor,
@@ -20,7 +19,10 @@ import KeyIndication from "./KeyIndication";
2019
import {clusterSessionPost, setRanges, setTagsAndRanges} from "../../../model/TaggingClusterSessionDispatch";
2120
import TruthCircle from "../../tagger_component/TruthCircle";
2221
import {FormatColorReset} from "@material-ui/icons";
23-
import {highlightStyle} from "../../../helpers/Util";
22+
import {highlightStyle, nthIndex} from "../../../helpers/Util";
23+
import {useFetch} from "../../../hooks/useFetch";
24+
import withKeyboard from "../../../hooks/withKeyboard";
25+
import stringEquals from "../../../util/StringEquals";
2426

2527
const {TAGGING_SERVICE_URL} = require('../../../../config.json')
2628

@@ -29,10 +31,11 @@ interface Input {
2931
dispatchTaggingClusterSession: React.Dispatch<TaggingClusterSessionDispatch>
3032
}
3133

34+
const regExp = new RegExp(/^[1-9]h[1-9](:?-[1-9]\d*)?$/)
35+
3236
function ClusterView({taggingClusterSession, dispatchTaggingClusterSession}: Input) {
3337

3438
const currentCluster: Cluster = getCurrentCluster(taggingClusterSession)
35-
// TODO: use name field
3639

3740
return (
3841
<div>
@@ -110,10 +113,58 @@ function ClusterItem({answer, taggingClusterSession, dispatchTaggingClusterSessi
110113
}
111114

112115
const clear = () => {
113-
dispatchTaggingClusterSession(setRanges(answer, []))
116+
if (isUsingDefaultColor(taggingClusterSession)) dispatchTaggingClusterSession(setRanges(answer, []))
117+
else {
118+
dispatchTaggingClusterSession(setRanges(answer,
119+
ranges.filter(range => !stringEquals(range.color, taggingClusterSession.currentColor))
120+
))
121+
}
122+
114123
dispatchTaggingClusterSession(clusterSessionPost())
115124
}
116125

126+
127+
withKeyboard((command: string) => {
128+
if (command.startsWith("" + displayKey) && regExp.test(command)) {
129+
if (command.indexOf('-') == -1) {
130+
const from: number = parseInt(command.slice(2)) - 1
131+
132+
if (isNaN(from)) return
133+
134+
let relative_start = nthIndex(answer.data, ' ', from)
135+
relative_start = relative_start == -1 ? 0 : relative_start
136+
137+
let relative_end = nthIndex(answer.data, ' ', from + 1)
138+
relative_end = relative_end == -1 ? answer.data.length : relative_end - 1
139+
140+
onTextHighlighted({
141+
start: relative_start,
142+
end: relative_end
143+
})
144+
} else {
145+
const split_index: number = command.indexOf('-')
146+
const from: number = parseInt(command.slice(2, split_index)) - 1
147+
const to: number = parseInt(command.slice(split_index + 1))
148+
149+
if (isNaN(from) || isNaN(to)) return
150+
151+
let relative_start = nthIndex(answer.data, ' ', from)
152+
relative_start = relative_start == -1 ? 0 : relative_start
153+
154+
let relative_end = nthIndex(answer.data, ' ', to)
155+
relative_end = relative_end == -1 ? answer.data.length : relative_end - 1
156+
157+
onTextHighlighted({
158+
start: relative_start,
159+
end: relative_end
160+
})
161+
}
162+
}
163+
if (command == "" + displayKey + 'rc') {
164+
clear()
165+
}
166+
})
167+
117168
if (isLoading) return <div>Loading...</div>
118169

119170

frontend/src/components/v2/tagger_component/MisconceptionView.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import SingleTagSelector from "./SingleTagSelector";
33
import {
44
filteredMisconceptions,
55
getColor,
6-
highlightRangesColorUpdating
6+
highlightRangesColorUpdating, NO_COLOR
77
} from "../../../helpers/Util";
88
import {MisconceptionElement} from "../../../interfaces/MisconceptionElement";
99
import {
@@ -23,6 +23,7 @@ import {
2323
import MisconceptionColorButton from "../../tagger_component/MisconceptionColorButton";
2424
import MisconceptionInfoButton from "../../tagger_component/MisconceptionInfoButton";
2525
import MisconceptionNoteButton from "../../tagger_component/MisconceptionNoteButton";
26+
import stringEquals from "../../../util/StringEquals";
2627

2728

2829
const useStyles = makeStyles((theme: Theme) =>
@@ -75,7 +76,12 @@ function MisconceptionView(
7576
const tags = taggingClusterSession.tags
7677

7778

78-
const set_current_color = (color: string) => dispatchTaggingClusterSession(setCurrentColor(color))
79+
const set_current_color = (color: string) => {
80+
if (stringEquals(taggingClusterSession.currentColor, color))
81+
dispatchTaggingClusterSession(setCurrentColor(NO_COLOR))
82+
else
83+
dispatchTaggingClusterSession(setCurrentColor(color))
84+
}
7985

8086

8187
const getNewRangesList = (element: (string | null), index: number) => {
@@ -106,7 +112,7 @@ function MisconceptionView(
106112
<div className={classes.root}>
107113
<>
108114
<div className={classes.divLine}>
109-
<KeyIndication displayKey={"1"}/>
115+
<KeyIndication displayKey={"t" + 1}/>
110116
<MisconceptionColorButton
111117
color={getColor(misconceptionsAvailable, tags[FIRST_DYNAMIC_INDEX])}
112118
current_color={currentColor}
@@ -116,6 +122,7 @@ function MisconceptionView(
116122
/>
117123
<SingleTagSelector
118124
key={"tag-selector-0"}
125+
taggingClusterSession={taggingClusterSession}
119126
misconceptions_available={
120127
filteredMisconceptions(tags, misconceptions_string_list, FIRST_DYNAMIC_INDEX)
121128
}
@@ -136,7 +143,7 @@ function MisconceptionView(
136143
const handled_element = PRE_DYNAMIC_SIZE + index + 2 // +2 = NoMisc and default add above
137144
return (
138145
<div key={"tag-selector-" + handled_element} className={classes.divLine}>
139-
<KeyIndication displayKey={"" + (handled_element - PRE_DYNAMIC_SIZE)}/>
146+
<KeyIndication displayKey={"t" + (handled_element - PRE_DYNAMIC_SIZE)}/>
140147
<MisconceptionColorButton
141148
color={(() => getColor(misconceptionsAvailable, tags[handled_element]))()}
142149
current_color={currentColor}
@@ -145,14 +152,15 @@ function MisconceptionView(
145152
staticColor={true}
146153
/>
147154
<SingleTagSelector
148-
misconceptions_available={
149-
filteredMisconceptions(tags, misconceptions_string_list, handled_element)
150-
}
151-
handled_element={handled_element}
152-
tags={tags}
153-
setTagElement={setTagElementHandle}
154-
enabled={true}
155-
/>
155+
taggingClusterSession={taggingClusterSession}
156+
misconceptions_available={
157+
filteredMisconceptions(tags, misconceptions_string_list, handled_element)
158+
}
159+
handled_element={handled_element}
160+
tags={tags}
161+
setTagElement={setTagElementHandle}
162+
enabled={true}
163+
/>
156164
<MisconceptionInfoButton
157165
tags={tags}
158166
handled_element={handled_element}

0 commit comments

Comments
 (0)