diff --git a/package-lock.json b/package-lock.json index 9fb736dff..63d03fb7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24819,15 +24819,6 @@ "react-dom": ">=16.14.0" } }, - "node_modules/react-virtuoso": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.17.0.tgz", - "integrity": "sha512-od3pi2v13v31uzn5zPXC2u3ouISFCVhjFVFch2VvS2Cx7pWA2F1aJa3XhNTN2F07M3lhfnMnsmGeH+7wZICr7w==", - "peerDependencies": { - "react": ">=16 || >=17 || >= 18 || >= 19", - "react-dom": ">=16 || >=17 || >= 18 || >=19" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -30084,7 +30075,7 @@ "react-player": "^2.16.0", "react-redux": "^7.2.9", "react-tooltip": "^5.5.2", - "react-virtuoso": "^4.17.0", + "react-virtuoso": "^4.18.1", "sass": "^1.46.0", "semver": "^7.7.1", "shell-quote": "^1.8.3", @@ -31525,6 +31516,16 @@ "url": "https://opencollective.com/express" } }, + "packages/bruno-app/node_modules/react-virtuoso": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.18.1.tgz", + "integrity": "sha512-KF474cDwaSb9+SJ380xruBB4P+yGWcVkcu26HtMqYNMTYlYbrNy8vqMkE+GpAApPPufJqgOLMoWMFG/3pJMXUA==", + "license": "MIT", + "peerDependencies": { + "react": ">=16 || >=17 || >= 18 || >= 19", + "react-dom": ">=16 || >=17 || >= 18 || >=19" + } + }, "packages/bruno-app/node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index e8341a0f7..528812fa2 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -84,7 +84,7 @@ "react-player": "^2.16.0", "react-redux": "^7.2.9", "react-tooltip": "^5.5.2", - "react-virtuoso": "^4.17.0", + "react-virtuoso": "^4.18.1", "sass": "^1.46.0", "semver": "^7.7.1", "shell-quote": "^1.8.3", diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index 0e10fe9b3..dee8e1e87 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -1,4 +1,5 @@ import React, { useCallback, useRef, useMemo, useEffect } from 'react'; +import { TableVirtuoso } from 'react-virtuoso'; import cloneDeep from 'lodash/cloneDeep'; import { get } from 'lodash'; import { IconTrash, IconAlertCircle, IconInfoCircle } from '@tabler/icons'; @@ -18,14 +19,28 @@ import { getGlobalEnvironmentVariables, flattenItems, isItemARequest } from 'uti import SensitiveFieldWarning from 'components/SensitiveFieldWarning'; import { sensitiveFields } from './constants'; +const TableRow = React.memo(({ children, item }) => {children}, (prevProps, nextProps) => { + const prevUid = prevProps?.item?.uid; + const nextUid = nextProps?.item?.uid; + return prevUid === nextUid && prevProps.children === nextProps.children; +}); + +const MIN_H = 35 * 2; // 2 rows worth of height + const EnvironmentVariables = ({ environment, setIsModified, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); const { globalEnvironments, activeGlobalEnvironmentUid } = useSelector((state) => state.globalEnvironments); + const [tableHeight, setTableHeight] = React.useState(MIN_H); + const environmentsDraft = collection?.environmentsDraft; const hasDraftForThisEnv = environmentsDraft?.environmentUid === environment.uid; + const handleTotalHeightChanged = React.useCallback((h) => { + setTableHeight(h); + }, []); + // Track environment changes for draft restoration const prevEnvUidRef = React.useRef(null); const mountedRef = React.useRef(false); @@ -384,111 +399,114 @@ const EnvironmentVariables = ({ environment, setIsModified, collection }) => { return ( -
- - - - - - - - - - - - {formik.values.map((variable, index) => { - const isLastRow = index === formik.values.length - 1; - const isEmptyRow = !variable.name || variable.name.trim() === ''; - const isLastEmptyRow = isLastRow && isEmptyRow; + ( + + + + + + + + )} + fixedItemHeight={35} + computeItemKey={(index, variable) => variable.uid} + itemContent={(index, variable) => { + const isLastRow = index === formik.values.length - 1; + const isEmptyRow = !variable.name || variable.name.trim() === ''; + const isLastEmptyRow = isLastRow && isEmptyRow; - return ( - - - - - - - - ); - })} - -
NameValueSecret
NameValueSecret
- {!isLastEmptyRow && ( - - )} - -
- handleNameChange(index, e)} - onBlur={() => handleNameBlur(index)} - onKeyDown={(e) => handleNameKeyDown(index, e)} - /> - -
-
-
- formik.setFieldValue(`${index}.value`, newValue, true)} - onSave={handleSave} - /> -
- {typeof variable.value !== 'string' && ( - - - - - )} - {!variable.secret && hasSensitiveUsage(variable.name) && ( - - )} -
- {!isLastEmptyRow && ( - - )} - - {!isLastEmptyRow && ( - - )} -
-
+ return ( + <> + + {!isLastEmptyRow && ( + + )} + + +
+ handleNameChange(index, e)} + onBlur={() => handleNameBlur(index)} + onKeyDown={(e) => handleNameKeyDown(index, e)} + /> + +
+ + +
+ formik.setFieldValue(`${index}.value`, newValue, true)} + onSave={handleSave} + /> +
+ {typeof variable.value !== 'string' && ( + + + + + )} + {!variable.secret && hasSensitiveUsage(variable.name) && ( + + )} + + + {!isLastEmptyRow && ( + + )} + + + {!isLastEmptyRow && ( + + )} + + + ); + }} + />
diff --git a/packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js index 95ea0bae4..6e96967dc 100644 --- a/packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js +++ b/packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js @@ -1,4 +1,5 @@ import React, { useCallback, useRef } from 'react'; +import { TableVirtuoso } from 'react-virtuoso'; import cloneDeep from 'lodash/cloneDeep'; import { IconTrash, IconAlertCircle, IconInfoCircle } from '@tabler/icons'; import { useTheme } from 'providers/Theme'; @@ -19,6 +20,14 @@ import { Tooltip } from 'react-tooltip'; import { getGlobalEnvironmentVariables } from 'utils/collections'; import Button from 'ui/Button'; +const MIN_H = 35 * 2; + +const TableRow = React.memo(({ children, item }) => {children}, (prevProps, nextProps) => { + const prevUid = prevProps?.item?.uid; + const nextUid = nextProps?.item?.uid; + return prevUid === nextUid && prevProps.children === nextProps.children; +}); + const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); @@ -28,6 +37,12 @@ const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentV const hasDraftForThisEnv = globalEnvironmentDraft?.environmentUid === environment.uid; + const [tableHeight, setTableHeight] = React.useState(MIN_H); + + const handleTotalHeightChanged = React.useCallback((h) => { + setTableHeight(h); + }, []); + // Track environment changes for draft restoration const prevEnvUidRef = React.useRef(null); const mountedRef = React.useRef(false); @@ -322,109 +337,107 @@ const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentV return ( -
- - - - - - - - - - - - {formik.values.map((variable, index) => { - const isLastRow = index === formik.values.length - 1; - const isEmptyRow = !variable.name || variable.name.trim() === ''; - const isLastEmptyRow = isLastRow && isEmptyRow; + ( + + + + + + + + )} + itemContent={(index, variable) => { + const isLastRow = index === formik.values.length - 1; + const isEmptyRow = !variable.name || variable.name.trim() === ''; + const isLastEmptyRow = isLastRow && isEmptyRow; - return ( - - - - - - - - ); - })} - -
NameValueSecret
NameValueSecret
- {!isLastEmptyRow && ( - - )} - -
- handleNameChange(index, e)} - onBlur={() => handleNameBlur(index)} - onKeyDown={(e) => handleNameKeyDown(index, e)} - /> - -
-
-
- formik.setFieldValue(`${index}.value`, newValue, true)} - onSave={handleSave} - /> -
- {typeof variable.value !== 'string' && ( - - - - - )} -
- {!isLastEmptyRow && ( - - )} - - {!isLastEmptyRow && ( - - )} -
-
+ return ( + <> + + {!isLastEmptyRow && ( + + )} + + +
+ handleNameChange(index, e)} + onBlur={() => handleNameBlur(index)} + onKeyDown={(e) => handleNameKeyDown(index, e)} + /> + +
+ + +
+ formik.setFieldValue(`${index}.value`, newValue, true)} + onSave={handleSave} + /> +
+ {typeof variable.value !== 'string' && ( + + + + + )} + + + {!isLastEmptyRow && ( + + )} + + + {!isLastEmptyRow && ( + + )} + + + ); + }} + />