diff --git a/webapp/src/views/projects/translations/CellKey.tsx b/webapp/src/views/projects/translations/CellKey.tsx index ab0a480e5f..9f0f8ab10c 100644 --- a/webapp/src/views/projects/translations/CellKey.tsx +++ b/webapp/src/views/projects/translations/CellKey.tsx @@ -8,6 +8,7 @@ import { Zap } from '@untitled-ui/icons-react'; import { LimitedHeightText } from 'tg.component/LimitedHeightText'; import { components } from 'tg.service/apiSchema.generated'; import { stopBubble } from 'tg.fixtures/eventHandler'; +import { wrapIf } from 'tg.fixtures/wrapIf'; import { Tags } from './Tags/Tags'; import { ScreenshotsPopover } from './Screenshots/ScreenshotsPopover'; @@ -126,12 +127,11 @@ export const CellKey: React.FC = ({ editEnabled, active, simple, - onSaveSuccess, className, }) => { const cellRef = useRef(null); const [screenshotsOpen, setScreenshotsOpen] = useState(false); - const { toggleSelect, addTag } = useTranslationsActions(); + const { toggleSelect, groupToggleSelect, addTag } = useTranslationsActions(); const { t } = useTranslate(); const screenshotEl = useRef(null); @@ -139,12 +139,20 @@ export const CellKey: React.FC = ({ const isSelected = useTranslationsSelector((c) => c.selection.includes(data.keyId) ); + const somethingSelected = useTranslationsSelector((c) => + Boolean(c.selection.length) + ); // prevent blinking, when closing popup const [screenshotsOpenDebounced] = useDebounce(screenshotsOpen, 100); - const handleToggleSelect = () => { - toggleSelect(data.keyId); + const handleToggleSelect = (e: React.PointerEvent) => { + const shiftPressed = e.nativeEvent.shiftKey; + if (shiftPressed) { + groupToggleSelect(data.keyId); + } else { + toggleSelect(data.keyId); + } }; const handleAddTag = (name: string) => { @@ -176,15 +184,23 @@ export const CellKey: React.FC = ({ ref={cellRef} > <> - {!simple && ( - - )} + {!simple && + wrapIf( + somethingSelected && !isSelected, + , + // @ts-ignore + + )} {data.keyName} diff --git a/webapp/src/views/projects/translations/context/TranslationsContext.ts b/webapp/src/views/projects/translations/context/TranslationsContext.ts index e9e18702b1..1c675c643c 100644 --- a/webapp/src/views/projects/translations/context/TranslationsContext.ts +++ b/webapp/src/views/projects/translations/context/TranslationsContext.ts @@ -186,8 +186,11 @@ export const [ return positionService.updatePosition(edit); } }, - toggleSelect(index: number) { - return selectionService.toggle(index); + toggleSelect(keyId: number) { + return selectionService.toggle(keyId); + }, + groupToggleSelect(keyId: number) { + return selectionService.groupToggle(keyId); }, async selectAll() { const allItems = await translationService.getAllIds(); diff --git a/webapp/src/views/projects/translations/context/services/useSelectionService.tsx b/webapp/src/views/projects/translations/context/services/useSelectionService.tsx index 423d8f03be..3a084ef626 100644 --- a/webapp/src/views/projects/translations/context/services/useSelectionService.tsx +++ b/webapp/src/views/projects/translations/context/services/useSelectionService.tsx @@ -20,12 +20,42 @@ export const useSelectionService = ({ translations }: Props) => { setSelection(data); }; + const groupToggle = (keyId: number) => { + const lastSelected = selection[selection.length - 1]; + const lastIndex = + translations.fixedTranslations?.findIndex( + (t) => t.keyId === lastSelected + ) ?? -1; + const currentIndex = + translations.fixedTranslations?.findIndex((t) => t.keyId === keyId) ?? -1; + + if (lastIndex < 0 || currentIndex < 0) { + toggle(keyId); + return; + } + + let from = lastIndex; + let to = currentIndex; + + if (lastIndex > currentIndex) { + from = currentIndex; + to = lastIndex; + } + const keys = + translations.fixedTranslations + ?.filter((_, i) => i >= from && i <= to) + .map((k) => k.keyId) + .filter((id) => !selection.includes(id)) ?? []; + setSelection((selected) => [...selected, ...keys]); + }; + const clear = () => { setSelection([]); }; return { toggle, + groupToggle, clear, select, data: selection,