diff --git a/frontend/src/hooks/useResizableColWidth.tsx b/frontend/src/hooks/useResizableColWidth.tsx new file mode 100644 index 000000000..f2ad80625 --- /dev/null +++ b/frontend/src/hooks/useResizableColWidth.tsx @@ -0,0 +1,71 @@ +import { MouseEvent, useCallback, useEffect, useRef, useState } from "react"; + +type Params = { + minWidth: number; + maxWidth: number; + initialWidth: number; +}; + +export const useResizableColWidth = ({ minWidth, maxWidth, initialWidth }: Params) => { + const [colWidth, setColWidth] = useState(initialWidth); + const [isResizing, setIsResizing] = useState(false); + const startX = useRef(0); + const startWidth = useRef(0); + + const handleMouseDown = useCallback( + (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsResizing(true); + startX.current = e.clientX; + startWidth.current = colWidth; + }, + [colWidth] + ); + + const handleMouseMove = useCallback( + (e: MouseEvent) => { + if (!isResizing) return; + + const deltaX = e.clientX - startX.current; + const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth.current + deltaX)); + + setColWidth(newWidth); + }, + [isResizing] + ); + + const handleMouseUp = useCallback(() => { + setIsResizing(false); + }, []); + + useEffect(() => { + if (isResizing) { + document.addEventListener( + "mousemove", + // @ts-expect-error native discrepancy + handleMouseMove + ); + document.addEventListener("mouseup", handleMouseUp); + document.body.style.cursor = "ew-resize"; + document.body.style.userSelect = "none"; + } + + return () => { + document.removeEventListener( + "mousemove", + // @ts-expect-error native discrepancy + handleMouseMove + ); + document.removeEventListener("mouseup", handleMouseUp); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + }; + }, [isResizing, handleMouseMove, handleMouseUp]); + + return { + colWidth, + handleMouseDown, + isResizing + }; +}; diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx index c11ff454d..40b09a94c 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-case-declarations */ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Helmet } from "react-helmet"; import { useTranslation } from "react-i18next"; import { subject } from "@casl/ability"; @@ -53,6 +53,7 @@ import { PendingAction } from "@app/hooks/api/secretFolders/types"; import { useCreateCommit } from "@app/hooks/api/secrets/mutations"; import { SecretV3RawSanitized } from "@app/hooks/api/types"; import { usePathAccessPolicies } from "@app/hooks/usePathAccessPolicies"; +import { useResizableColWidth } from "@app/hooks/useResizableColWidth"; import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission"; import { RequestAccessModal } from "@app/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/RequestAccessModal"; import { SecretRotationListView } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretRotationListView"; @@ -104,6 +105,8 @@ const Page = () => { const { permission } = useProjectPermission(); const { mutateAsync: createCommit } = useCreateCommit(); + const tableRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); const { isBatchMode, pendingChanges } = useBatchMode(); const { loadPendingChanges, setExistingKeys } = useBatchModeActions(); @@ -483,6 +486,14 @@ const Page = () => { handlePopUpClose("snapshots"); }, []); + const { handleMouseDown, isResizing, colWidth } = useResizableColWidth({ + initialWidth: 320, + minWidth: 100, + maxWidth: tableRef.current + ? tableRef.current.clientWidth - 148 // ensure value column can't collapse completely + : 800 + }); + useEffect(() => { // restore filters for path if set const restore = filterHistory.get(secretPath); @@ -787,6 +798,7 @@ const Page = () => { } />
{ />
-
{ - if (evt.key === "Enter") handleSortToggle(); - }} - > - Key - +
+
+
+
+
{ + if (evt.key === "Enter") handleSortToggle(); + }} + > + Key + +
Value
@@ -927,6 +953,7 @@ const Page = () => { )} {canReadSecret && Boolean(mergedSecrets?.length) && ( {isPending ? ( -
+
{ const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([ "editSecret" @@ -383,7 +385,10 @@ export const SecretItem = memo( )}
-
+
{ const queryClient = useQueryClient(); const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([ @@ -554,6 +556,7 @@ export const SecretListView = ({ ))} {secrets.map((secret) => (