Skip to content

Commit abbdba2

Browse files
committed
Add new editable RecentEntry
1 parent 58b13af commit abbdba2

File tree

3 files changed

+193
-5
lines changed

3 files changed

+193
-5
lines changed

src/components/DataEntry/DataEntryTable/RecentEntryCold.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import { firstGlossText } from "utilities/wordUtilities";
1111

1212
const idAffix = "recent-entry";
1313

14-
export interface RecentEntryProps {
14+
export interface RecentEntryColdProps {
1515
analysisLang: WritingSystem;
1616
entry: Word;
1717
rowIndex: number;
1818
senseGuid: string;
1919
}
2020

2121
/** Displays a recently entered word that a user cannot edit. */
22-
export function RecentEntryCold(props: RecentEntryProps): ReactElement {
22+
export function RecentEntryCold(props: RecentEntryColdProps): ReactElement {
2323
const sense = props.entry.senses.find((s) => s.guid === props.senseGuid);
2424
const gloss = sense ? firstGlossText(sense, props.analysisLang.bcp47) : "";
2525

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { Check } from "@mui/icons-material";
2+
import { Grid2, IconButton } from "@mui/material";
3+
import { ReactElement, memo, useState } from "react";
4+
5+
import { Pronunciation, Word, WritingSystem } from "api/models";
6+
import { NoteButton } from "components/Buttons";
7+
import {
8+
DeleteEntry,
9+
GlossWithSuggestions,
10+
VernWithSuggestions,
11+
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
12+
import PronunciationsBackend from "components/Pronunciations/PronunciationsBackend";
13+
import theme from "types/theme";
14+
import { FileWithSpeakerId, newGloss } from "types/word";
15+
import { firstGlossText } from "utilities/wordUtilities";
16+
17+
const idAffix = "recent-entry";
18+
19+
export interface RecentEntryHotProps {
20+
rowIndex: number;
21+
entry: Word;
22+
senseGuid: string;
23+
updateGloss: (index: number, gloss: string) => void;
24+
updateNote: (index: number, newText: string) => Promise<void>;
25+
updateVern: (index: number, newVern: string, targetWordId?: string) => void;
26+
removeEntry: (index: number) => void;
27+
addAudioToWord: (wordId: string, file: FileWithSpeakerId) => void;
28+
delAudioFromWord: (wordId: string, fileName: string) => void;
29+
repAudioInWord: (wordId: string, audio: Pronunciation) => void;
30+
focusNewEntry: () => void;
31+
analysisLang: WritingSystem;
32+
vernacularLang: WritingSystem;
33+
disabled?: boolean;
34+
close: () => void;
35+
}
36+
37+
/**
38+
* Displays a recently entered word that a user can still edit
39+
*/
40+
export function RecentEntryHot(props: RecentEntryHotProps): ReactElement {
41+
const sense = props.entry.senses.find((s) => s.guid === props.senseGuid)!;
42+
if (sense.glosses.length < 1) {
43+
sense.glosses.push(newGloss("", props.analysisLang.bcp47));
44+
}
45+
const [editing, setEditing] = useState(false);
46+
const [gloss, setGloss] = useState(
47+
firstGlossText(sense, props.analysisLang.bcp47)
48+
);
49+
const [vernacular, setVernacular] = useState(props.entry.vernacular);
50+
51+
const updateGlossField = (gloss: string): void => {
52+
setEditing(gloss !== firstGlossText(sense, props.analysisLang.bcp47));
53+
setGloss(gloss);
54+
};
55+
const updateVernField = (vern: string): void => {
56+
setEditing(vern !== props.entry.vernacular);
57+
setVernacular(vern);
58+
};
59+
60+
function conditionallyUpdateGloss(): void {
61+
if (firstGlossText(sense, props.analysisLang.bcp47) !== gloss) {
62+
props.updateGloss(props.rowIndex, gloss);
63+
props.close();
64+
}
65+
}
66+
67+
function conditionallyUpdateVern(): void {
68+
if (vernacular.trim()) {
69+
if (props.entry.vernacular !== vernacular) {
70+
props.updateVern(props.rowIndex, vernacular);
71+
props.close();
72+
}
73+
} else {
74+
setVernacular(props.entry.vernacular);
75+
}
76+
}
77+
78+
const handleRemoveEntry = (): void => props.removeEntry(props.rowIndex);
79+
const handleUpdateNote = async (noteText: string): Promise<void> => {
80+
await props.updateNote(props.rowIndex, noteText);
81+
props.close();
82+
};
83+
84+
return (
85+
<Grid2
86+
alignItems="center"
87+
container
88+
id={`${idAffix}-${props.rowIndex}`}
89+
rowSpacing={1}
90+
>
91+
<Grid2
92+
size={11}
93+
style={{ paddingInline: theme.spacing(2), position: "relative" }}
94+
>
95+
<VernWithSuggestions
96+
vernacular={vernacular}
97+
isNew
98+
isDisabled={props.disabled || props.entry.senses.length > 1}
99+
updateVernField={updateVernField}
100+
onBlur={() => conditionallyUpdateVern()}
101+
handleEnter={() => {
102+
vernacular && props.focusNewEntry();
103+
}}
104+
vernacularLang={props.vernacularLang}
105+
textFieldId={`${idAffix}-${props.rowIndex}-vernacular`}
106+
/>
107+
</Grid2>
108+
109+
<Grid2 size={1}>
110+
<IconButton onClick={props.close}>
111+
<Check sx={{ color: (t) => t.palette.success.main }} />
112+
</IconButton>
113+
</Grid2>
114+
115+
<Grid2
116+
size={11}
117+
style={{ paddingInline: theme.spacing(2), position: "relative" }}
118+
>
119+
<GlossWithSuggestions
120+
gloss={gloss}
121+
isDisabled={props.disabled}
122+
isNew
123+
updateGlossField={updateGlossField}
124+
onBlur={() => conditionallyUpdateGloss()}
125+
handleEnter={() => {
126+
gloss && props.focusNewEntry();
127+
}}
128+
analysisLang={props.analysisLang}
129+
textFieldId={`${idAffix}-${props.rowIndex}-gloss`}
130+
/>
131+
</Grid2>
132+
133+
<Grid2 size={1} />
134+
135+
<Grid2
136+
size={2}
137+
style={{ paddingInline: theme.spacing(1), position: "relative" }}
138+
>
139+
<NoteButton
140+
disabled={editing || props.disabled}
141+
noteText={props.entry.note.text}
142+
updateNote={handleUpdateNote}
143+
buttonId={`${idAffix}-${props.rowIndex}-note`}
144+
/>
145+
</Grid2>
146+
147+
<Grid2
148+
size={8}
149+
style={{ paddingInline: theme.spacing(1), position: "relative" }}
150+
>
151+
<PronunciationsBackend
152+
audio={props.entry.audio}
153+
disabled={editing || props.disabled}
154+
wordId={props.entry.id}
155+
deleteAudio={(fileName) => {
156+
props.delAudioFromWord(props.entry.id, fileName);
157+
}}
158+
replaceAudio={(audio) => props.repAudioInWord(props.entry.id, audio)}
159+
uploadAudio={(file) => {
160+
props.addAudioToWord(props.entry.id, file);
161+
}}
162+
/>
163+
</Grid2>
164+
165+
<Grid2 size={1} />
166+
167+
<Grid2
168+
size={1}
169+
style={{ paddingInline: theme.spacing(1), position: "relative" }}
170+
>
171+
<DeleteEntry
172+
removeEntry={handleRemoveEntry}
173+
buttonId={`${idAffix}-${props.rowIndex}-delete`}
174+
confirmId={"addWords.deleteRowWarning"}
175+
disabled={editing || props.disabled}
176+
/>
177+
</Grid2>
178+
</Grid2>
179+
);
180+
}
181+
182+
export default memo(RecentEntryHot);

src/components/DataEntry/DataEntryTable/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import {
2626
import * as backend from "backend";
2727
import { getCurrentUser, getUserId } from "backend/localStorage";
2828
import NewEntry from "components/DataEntry/DataEntryTable/NewEntry";
29-
import RecentEntry from "components/DataEntry/DataEntryTable/RecentEntry";
30-
import { RecentEntryCold } from "components/DataEntry/DataEntryTable/RecentEntryCold";
29+
import RecentEntryCold from "components/DataEntry/DataEntryTable/RecentEntryCold";
30+
import RecentEntryHot from "components/DataEntry/DataEntryTable/RecentEntryHot";
3131
import {
3232
filterWordsWithSenses,
3333
focusInput,
@@ -1038,7 +1038,7 @@ export default function DataEntryTable(
10381038
sx={{ borderBottom: "1px solid #eee" }}
10391039
>
10401040
{index === state.recentWordEditingIndex ? (
1041-
<RecentEntry
1041+
<RecentEntryHot
10421042
rowIndex={index}
10431043
entry={wordAccess.word}
10441044
senseGuid={wordAccess.senseGuid}
@@ -1055,6 +1055,12 @@ export default function DataEntryTable(
10551055
disabled={Object.keys(state.defunctWordIds).includes(
10561056
wordAccess.word.id
10571057
)}
1058+
close={() =>
1059+
setState((prev) => ({
1060+
...prev,
1061+
recentWordEditingIndex: undefined,
1062+
}))
1063+
}
10581064
/>
10591065
) : (
10601066
<div

0 commit comments

Comments
 (0)