diff --git a/frontend/pages/recipe/_slug/ocr-editor.vue b/frontend/pages/recipe/_slug/ocr-editor.vue index 9936dc61885..0b97f69f93d 100644 --- a/frontend/pages/recipe/_slug/ocr-editor.vue +++ b/frontend/pages/recipe/_slug/ocr-editor.vue @@ -10,87 +10,23 @@ - - Toolbar - - - {{ $t("ocr-editor.selection-mode") }} - - - - {{ $t("ocr-editor.pan-and-zoom-picture") }} - - - {{ $t("ocr-editor.split-text") }} - - - {{ $t("ocr-editor.preserve-line-breaks") }} - - - - {{ $t("ocr-editor.split-by-block") }} - - + + + {{ section.sectionTitle }} + + - {{ $t("ocr-editor.flatten") }} + {{ icon.tooltip }} - - + + + {{ $t("general.save") }} @@ -289,6 +225,7 @@ import { toRefs, useRouter, nextTick, + useContext, } from "@nuxtjs/composition-api"; import { until } from "@vueuse/core"; import { invoke } from "@vueuse/shared"; @@ -364,6 +301,17 @@ type CanvasModes = "selection" | "panAndZoom"; type SelectedTextSplitModes = keyof OcrTsvResponse | "flatten"; +type ToolbarIcons = { + sectionTitle: string; + eventHandler(mode: T): void; + highlight: T; + icons: { + name: T; + icon: string; + tooltip: string; + }[]; +}[]; + export default defineComponent({ components: { RecipeIngredientEditor, @@ -378,6 +326,8 @@ export default defineComponent({ const slug = route.value.params.slug; const api = useUserApi(); + const { $globals, i18n } = useContext(); + const tsv = ref([]); const { recipe, loading, fetchRecipe } = useRecipe(slug); @@ -432,6 +382,48 @@ export default defineComponent({ showHelp: false, }); + const toolbarIcons = ref>([ + { + sectionTitle: "Toolbar", + eventHandler: switchCanvasMode, + highlight: state.canvasMode, + icons: [ + { + name: "selection", + icon: $globals.icons.selectMode, + tooltip: i18n.t("ocr-editor.selection-mode") as string, + }, + { + name: "panAndZoom", + icon: $globals.icons.panAndZoom, + tooltip: i18n.t("ocr-editor.pan-and-zoom-picture") as string, + }, + ], + }, + { + sectionTitle: i18n.t("ocr-editor.split-text") as string, + eventHandler: switchSplitTextMode, + highlight: state.selectedTextSplitMode, + icons: [ + { + name: "lineNum", + icon: $globals.icons.preserveLines, + tooltip: i18n.t("ocr-editor.preserve-line-breaks") as string, + }, + { + name: "blockNum", + icon: $globals.icons.preserveBlocks, + tooltip: i18n.t("ocr-editor.split-by-block") as string, + }, + { + name: "flatten", + icon: $globals.icons.flatten, + tooltip: i18n.t("ocr-editor.flatten") as string, + }, + ], + }, + ]); + const setPropertyValueByPath = function (object: T, path: Paths, value: any) { const a = path.split("."); let nextProperty: any = object; @@ -462,10 +454,10 @@ export default defineComponent({ } /** - * This function will find the title of a recipe with the assumption that the title - * has the biggest ratio of surface area / number of words on the image. - * @return Returns the text parts of the block with the highest score. - */ + * This function will find the title of a recipe with the assumption that the title + * has the biggest ratio of surface area / number of words on the image. + * @return Returns the text parts of the block with the highest score. + */ function findRecipeTitle() { const filtered = tsv.value.filter((element) => element.level === 2 || element.level === 5); const blocks = [[]] as OcrTsvResponse[][]; @@ -560,6 +552,7 @@ export default defineComponent({ function switchCanvasMode(mode: CanvasModes) { if (state.canvasRect === null || state.canvas === null) return; state.canvasMode = mode; + toolbarIcons.value[0].highlight = mode; if (mode === "panAndZoom") { state.canvas.style.cursor = "pointer"; } else { @@ -570,6 +563,7 @@ export default defineComponent({ function switchSplitTextMode(mode: SelectedTextSplitModes) { if (state.canvasRect === null) return; state.selectedTextSplitMode = mode; + toolbarIcons.value[1].highlight = mode; state.selectedText = getWordsInSelection(tsv.value, state.rect); } @@ -753,10 +747,10 @@ export default defineComponent({ } /** - * Returns rectangle coordinates with positive dimensions - * @param rect A rectangle - * @returns An equivalent rectangle with width and height > 0 - */ + * Returns rectangle coordinates with positive dimensions + * @param rect A rectangle + * @returns An equivalent rectangle with width and height > 0 + */ function correctRectCoordinates(rect: CanvasRect) { if (rect.w < 0) { rect.startX = rect.startX + rect.w; @@ -788,13 +782,13 @@ export default defineComponent({ } /** - * Using rectangle coordinates, filters the tsv to get text elements contained - * inside the rectangle - * Additionaly adds newlines depending on the current "text split" mode - * @param tsv An Object containing tesseracts tsv fields - * @param rect Coordinates of a rectangle - * @returns Text from tsv contained in the rectangle - */ + * Using rectangle coordinates, filters the tsv to get text elements contained + * inside the rectangle + * Additionaly adds newlines depending on the current "text split" mode + * @param tsv An Object containing tesseracts tsv fields + * @param rect Coordinates of a rectangle + * @returns Text from tsv contained in the rectangle + */ function getWordsInSelection(tsv: OcrTsvResponse[], rect: CanvasRect) { const correctedRect = correctRectCoordinates(rect); @@ -908,6 +902,7 @@ export default defineComponent({ setSingleIngredient, setSingleStep, switchSplitTextMode, + toolbarIcons, }; }, }); diff --git a/frontend/utils/icons/icon-type.ts b/frontend/utils/icons/icon-type.ts index a3377620463..46f585267ab 100644 --- a/frontend/utils/icons/icon-type.ts +++ b/frontend/utils/icons/icon-type.ts @@ -125,4 +125,11 @@ export interface Icon { back: string; slotMachine: string; chevronDown: string; + + // Ocr toolbar + selectMode: string; + panAndZoom: string; + preserveLines: string; + preserveBlocks: string; + flatten: string; }