From 45caea0bc73da6f219a063d0908db0ab9e20c5af Mon Sep 17 00:00:00 2001 From: "D. Ror" Date: Tue, 9 Jan 2024 11:41:58 -0500 Subject: [PATCH] [MergeDups] Refactor word column to prevent flag wrapping (#2841) --- .../MergeDupsStep/MergeDragDrop/DropWord.tsx | 230 ++++++++++-------- .../ReviewEntriesTable/index.tsx | 1 + 2 files changed, 135 insertions(+), 96 deletions(-) diff --git a/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx b/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx index 9b874ca0f1..672de4a3c3 100644 --- a/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx +++ b/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx @@ -1,5 +1,12 @@ import { WarningOutlined } from "@mui/icons-material"; -import { Grid, MenuItem, Paper, Select, Typography } from "@mui/material"; +import { + Card, + CardContent, + CardHeader, + MenuItem, + Select, + Typography, +} from "@mui/material"; import { ReactElement } from "react"; import { Droppable } from "react-beautiful-dnd"; import { useTranslation } from "react-i18next"; @@ -7,6 +14,7 @@ import { useTranslation } from "react-i18next"; import { Flag } from "api/models"; import { FlagButton, IconButtonWithTooltip } from "components/Buttons"; import DragSense from "goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DragSense"; +import { MergeTreeWord } from "goals/MergeDuplicates/MergeDupsTreeTypes"; import { flagWord, setVern, @@ -21,104 +29,42 @@ interface DropWordProps { } export default function DropWord(props: DropWordProps): ReactElement { - const dispatch = useAppDispatch(); - const data = useAppSelector( - (state: StoreState) => state.mergeDuplicateGoal.data + const dataSenses = useAppSelector( + (state: StoreState) => state.mergeDuplicateGoal.data.senses ); + // `treeWord` is undefined in the extra empty column. const treeWord = useAppSelector( (state: StoreState) => state.mergeDuplicateGoal.tree.words[props.wordId] ); const { t } = useTranslation(); - let protectedWithOneChild = false; - const verns: string[] = []; - if (treeWord) { - protectedWithOneChild = - treeWord.protected && Object.keys(treeWord.sensesGuids).length === 1; - verns.push( - ...new Set( - Object.values(treeWord.sensesGuids).flatMap((guids) => - guids.map((g) => data.words[data.senses[g].srcWordId].vernacular) - ) - ) - ); - } - - // reset vern if not in vern list - if (treeWord && !verns.includes(treeWord.vern)) { - dispatch(setVern({ wordId: props.wordId, vern: verns[0] || "" })); - } + const sensesGuids = treeWord?.sensesGuids ?? {}; + const protectedWithOneChild = + treeWord?.protected && Object.keys(sensesGuids).length === 1; return ( - - - {!!treeWord && ( - - - - - - {protectedWithOneChild && ( - } - side="top" - size="small" - textId="mergeDups.helpText.protectedWord" - buttonId={`word-${props.wordId}-protected`} - /> - )} - { - dispatch(flagWord({ wordId: props.wordId, flag: newFlag })); - }} - buttonId={`word-${props.wordId}-flag`} - /> - - - )} - - - {(provided): ReactElement => ( -
-
- {!!treeWord && - Object.keys(treeWord.sensesGuids).map((id, index) => { - const senses = treeWord.sensesGuids[id].map( - (g) => data.senses[g] - ); + + + + {(provided): ReactElement => ( +
+
+ {Object.keys(sensesGuids).map((id, index) => { + const senses = sensesGuids[id].map((g) => dataSenses[g]); return ( ); })} - {provided.placeholder} + {provided.placeholder} +
+
+ + {t("mergeDups.helpText.dragCard")} + +
-
- - {t("mergeDups.helpText.dragCard")} - -
-
- )} - - + )} + + + + ); +} + +interface DropWordCardHeaderProps { + protectedWithOneChild: boolean; + treeWord?: MergeTreeWord; + wordId: string; +} + +export function DropWordCardHeader( + props: DropWordCardHeaderProps +): ReactElement { + const dispatch = useAppDispatch(); + const { senses, words } = useAppSelector( + (state: StoreState) => state.mergeDuplicateGoal.data + ); + + const dispatchFlagWord = (flag: Flag): void => { + dispatch(flagWord({ wordId: props.wordId, flag })); + }; + const dispatchSetVern = (vern: string): void => { + dispatch(setVern({ wordId: props.wordId, vern })); + }; + + const treeWord = props.treeWord; + const guids = Object.values(treeWord?.sensesGuids ?? {}).flatMap((sg) => sg); + const verns = [ + ...new Set(guids.map((g) => words[senses[g].srcWordId].vernacular)), + ]; + + // Reset vern if not in vern list. + if (treeWord && !verns.includes(treeWord.vern)) { + dispatchSetVern(verns.length ? verns[0] : ""); + } + + const headerTitle = treeWord ? ( + verns.length > 1 && verns.includes(treeWord.vern) ? ( + + ) : ( + + {treeWord.vern} + + ) + ) : ( +
+ ); + + const headerAction = treeWord ? ( + <> + {props.protectedWithOneChild && ( + } + side="top" + size="small" + textId="mergeDups.helpText.protectedWord" + /> + )} + + + ) : ( +
+ ); + + return ( + ); } diff --git a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx index c0f2ebb518..c9bed9189d 100644 --- a/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx +++ b/src/goals/ReviewEntries/ReviewEntriesTable/index.tsx @@ -48,6 +48,7 @@ const tableRef: React.RefObject = createRef(); export default function ReviewEntriesTable( props: ReviewEntriesTableProps ): ReactElement { + // https://redux.js.org/usage/deriving-data-selectors#optimizing-selectors-with-memoization const wordsSelector = createSelector( [(state: StoreState) => state.reviewEntriesState.words], (words) => words.map((w) => new ReviewEntriesWord(w))