From 9be42ce53254cc3da265541e27393910d8a756ac Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Tue, 29 Apr 2025 12:47:16 -0500 Subject: [PATCH] Don't select text when shift is held --- .../components/tables/CustomTable/index.jsx | 62 +++++- frontend/src/components/tables/table.css | 207 ++++++++++++------ 2 files changed, 194 insertions(+), 75 deletions(-) diff --git a/frontend/src/components/tables/CustomTable/index.jsx b/frontend/src/components/tables/CustomTable/index.jsx index 3303dffe..0b1d824f 100644 --- a/frontend/src/components/tables/CustomTable/index.jsx +++ b/frontend/src/components/tables/CustomTable/index.jsx @@ -7,7 +7,7 @@ import { getCoreRowModel, flexRender, } from '@tanstack/react-table'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo, useState, useEffect } from 'react'; import { ChevronDown, ChevronRight } from 'lucide-react'; const useTable = ({ @@ -22,6 +22,63 @@ const useTable = ({ const [selectedTableIds, setSelectedTableIds] = useState([]); const [expandedRowIds, setExpandedRowIds] = useState([]); const [lastClickedId, setLastClickedId] = useState(null); + const [isShiftKeyDown, setIsShiftKeyDown] = useState(false); + + // Event handlers for shift key detection with improved handling + const handleKeyDown = useCallback((e) => { + if (e.key === 'Shift') { + setIsShiftKeyDown(true); + // Apply the class to disable text selection immediately + document.body.classList.add('shift-key-active'); + // Set a style attribute directly on body for extra assurance + document.body.style.userSelect = 'none'; + document.body.style.webkitUserSelect = 'none'; + document.body.style.msUserSelect = 'none'; + document.body.style.cursor = 'pointer'; + } + }, []); + + const handleKeyUp = useCallback((e) => { + if (e.key === 'Shift') { + setIsShiftKeyDown(false); + // Remove the class when shift is released + document.body.classList.remove('shift-key-active'); + // Reset the style attributes + document.body.style.removeProperty('user-select'); + document.body.style.removeProperty('-webkit-user-select'); + document.body.style.removeProperty('-ms-user-select'); + document.body.style.removeProperty('cursor'); + } + }, []); + + // Add global event listeners for shift key detection with improved cleanup + useEffect(() => { + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + + // Also detect blur/focus events to handle cases where shift is held and window loses focus + window.addEventListener('blur', () => { + setIsShiftKeyDown(false); + document.body.classList.remove('shift-key-active'); + document.body.style.removeProperty('user-select'); + document.body.style.removeProperty('-webkit-user-select'); + document.body.style.removeProperty('-ms-user-select'); + document.body.style.removeProperty('cursor'); + }); + + return () => { + window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('keyup', handleKeyUp); + window.removeEventListener('blur', () => { + setIsShiftKeyDown(false); + document.body.classList.remove('shift-key-active'); + document.body.style.removeProperty('user-select'); + document.body.style.removeProperty('-webkit-user-select'); + document.body.style.removeProperty('-ms-user-select'); + document.body.style.removeProperty('cursor'); + }); + }; + }, [handleKeyDown, handleKeyUp]); const rowCount = allRowIds.length; @@ -175,8 +232,9 @@ const useTable = ({ expandedRowIds, expandedRowRenderer, setSelectedTableIds, + isShiftKeyDown, // Include shift key state in the table instance }), - [selectedTableIdsSet, expandedRowIds, allRowIds] + [selectedTableIdsSet, expandedRowIds, allRowIds, isShiftKeyDown] ); return { diff --git a/frontend/src/components/tables/table.css b/frontend/src/components/tables/table.css index 00198499..c3651246 100644 --- a/frontend/src/components/tables/table.css +++ b/frontend/src/components/tables/table.css @@ -1,94 +1,155 @@ * { - /* box-sizing: border-box; */ - } + /* box-sizing: border-box; */ +} - html { - font-family: sans-serif; - /* font-size: 14px; */ - } +html { + font-family: sans-serif; + /* font-size: 14px; */ +} - .divTable { - /* border: 1px solid lightgray; */ - /* width: fit-content; */ - /* display: flex; +.divTable { + /* border: 1px solid lightgray; */ + /* width: fit-content; */ + /* display: flex; flex-direction: column; */ - } +} - .tr { - display: flex; - } +.tr { + display: flex; +} - .table-striped .tbody .tr:hover { - background-color: rgb(68,68,68); - } +.table-striped .tbody .tr:hover { + background-color: rgb(68, 68, 68); +} - .tr { - /* width: fit-content; +.tr { + /* width: fit-content; width: 100%; */ - /* height: 30px; */ - } + /* height: 30px; */ +} - .th, - .td { - /* box-shadow: inset 0 0 0 1px lightgray; */ - /* padding: 0.25rem; */ - padding-left: 4px; - padding-right: 4px; - } +.th, +.td { + /* box-shadow: inset 0 0 0 1px lightgray; */ + /* padding: 0.25rem; */ + padding-left: 4px; + padding-right: 4px; +} - .th { - /* padding: 2px 4px; */ - position: relative; - font-weight: bold; - text-align: center; - /* height: 30px; */ - } +.th { + /* padding: 2px 4px; */ + position: relative; + font-weight: bold; + text-align: center; + /* height: 30px; */ +} - .td { - height: 28px; - border-bottom: solid 1px rgb(68,68,68); - } +.td { + height: 28px; + border-bottom: solid 1px rgb(68, 68, 68); +} +.resizer { + position: absolute; + top: 0; + height: 100%; + width: 5px; + background: rgba(0, 0, 0, 0.5); + cursor: col-resize; + user-select: none; + touch-action: none; +} + +.resizer.ltr { + right: 0; +} + +.resizer.rtl { + left: 0; +} + +.resizer.isResizing { + background: blue; + opacity: 1; +} + +@media (hover: hover) { .resizer { - position: absolute; - top: 0; - height: 100%; - width: 5px; - background: rgba(0, 0, 0, 0.5); - cursor: col-resize; - user-select: none; - touch-action: none; + opacity: 0; } - .resizer.ltr { - right: 0; - } - - .resizer.rtl { - left: 0; - } - - .resizer.isResizing { - background: blue; + *:hover>.resizer { opacity: 1; } +} - @media (hover: hover) { - .resizer { - opacity: 0; - } +/* .table-striped .tbody .tr:nth-child(odd), */ +.table-striped .tbody .tr-odd { + background-color: #18181b; +} - *:hover > .resizer { - opacity: 1; - } - } +/* .table-striped .tbody .tr:nth-child(even), */ +.table-striped .tbody .tr-even { + background-color: #27272A; +} - /* .table-striped .tbody .tr:nth-child(odd), */ - .table-striped .tbody .tr-odd { - background-color: #18181b; - } +/* Prevent text selection when shift key is pressed */ +.shift-key-active { + cursor: pointer !important; +} - /* .table-striped .tbody .tr:nth-child(even), */ - .table-striped .tbody .tr-even { - background-color: #27272A; - } +.shift-key-active *, +.shift-key-active .tr, +.shift-key-active .td, +.shift-key-active .tbody { + user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; +} + +/* Always allow text selection in editable elements */ +.shift-key-active input, +.shift-key-active textarea, +.shift-key-active [contenteditable="true"], +.shift-key-active .table-input-header input { + user-select: text !important; + -webkit-user-select: text !important; + -moz-user-select: text !important; + -ms-user-select: text !important; + cursor: text !important; +} + +/* Improve specificity and ensure text selection is disabled when shift is pressed */ +.shift-key-active, +.shift-key-active * { + user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + cursor: pointer !important; +} + +/* Add a visual indicator when shift is pressed */ +.shift-key-active .tbody .tr { + transition: background-color 0.1s; +} + +.shift-key-active .tbody .tr:hover { + background-color: rgba(68, 68, 68, 0.7) !important; +} + +/* Always allow text selection in inputs even when shift is pressed */ +.shift-key-active input, +.shift-key-active textarea, +.shift-key-active [contenteditable="true"], +.shift-key-active select, +.shift-key-active .mantine-Select-input, +.shift-key-active .mantine-MultiSelect-input, +.shift-key-active .table-input-header input { + user-select: text !important; + -webkit-user-select: text !important; + -moz-user-select: text !important; + -ms-user-select: text !important; + cursor: text !important; +} \ No newline at end of file