diff --git a/assets/icons/chevronlefticon.svg b/assets/icons/chevronlefticon.svg
new file mode 100644
index 00000000..d81c9ddd
--- /dev/null
+++ b/assets/icons/chevronlefticon.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/App.jsx b/src/App.jsx
index fc55d159..8dc98e86 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -19,6 +19,7 @@ import {
} from './components/Dashboard/context/SpotlightContext.jsx'
import { ActionsModalProvider } from './components/Dashboard/context/ActionsModalContext.jsx'
import { ActionsProvider } from './components/Dashboard/context/ActionsContext.jsx'
+import { DashboardObjectToolsProvider } from './components/Dashboard/context/DashboardObjectToolsContext.jsx'
import MissingPlaceholder from './components/Dashboard/common/MissingPlaceholder.jsx'
import {
@@ -93,6 +94,7 @@ const AppContent = () => {
+
{
}
/>
+
diff --git a/src/components/Dashboard/Layout.jsx b/src/components/Dashboard/Layout.jsx
index d4cac784..adf0353d 100644
--- a/src/components/Dashboard/Layout.jsx
+++ b/src/components/Dashboard/Layout.jsx
@@ -13,6 +13,7 @@ import DeveloperSidebar from './Developer/DeveloperSidebar'
import DashboardSidebarSplitter from './common/DashboardSidebarSplitter'
import { useThemeContext } from './context/ThemeContext'
import { MessageProvider } from './context/MessageContext'
+import { useDashboardObjectToolsContext } from './context/DashboardObjectToolsContext'
const { Content } = Layout
@@ -26,6 +27,7 @@ const DashboardLayout = ({ children }) => {
const isDeveloper = location.pathname.startsWith('/dashboard/developer')
const { isDarkMode } = useThemeContext()
+ const { currentObjectTools } = useDashboardObjectToolsContext()
const sidebar = isProduction ? (
@@ -57,8 +59,9 @@ const DashboardLayout = ({ children }) => {
>
-
+
+ {currentObjectTools}
{children}
diff --git a/src/components/Dashboard/common/KeyboardShortcut.jsx b/src/components/Dashboard/common/KeyboardShortcut.jsx
index c371541e..abd8fd41 100644
--- a/src/components/Dashboard/common/KeyboardShortcut.jsx
+++ b/src/components/Dashboard/common/KeyboardShortcut.jsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef, cloneElement, useMemo } from 'react'
+import { useEffect, useRef, useMemo } from 'react'
import PropTypes from 'prop-types'
import loglevel from 'loglevel'
import config from '../../../config'
@@ -50,7 +50,6 @@ const KeyboardShortcut = ({
onTrigger,
log = false
}) => {
- const childRef = useRef()
const pressedKeysRef = useRef(new Set())
const shortcutKeys = useMemo(() => parseShortcut(shortcut), [shortcut])
@@ -94,8 +93,6 @@ const KeyboardShortcut = ({
}
}, [shortcutKeys, onTrigger, log])
- const element = cloneElement(children, { ref: childRef })
-
if (hint) {
var osSpecificHint = hint.replaceAll('ALT', '⌥')
@@ -104,11 +101,11 @@ const KeyboardShortcut = ({
hideBorder
title={{osSpecificHint}}
>
- {element}
+ {children}
)
}
- return element
+ return children
}
KeyboardShortcut.propTypes = {
diff --git a/src/components/Dashboard/common/ObjectTableNavigationButtons.jsx b/src/components/Dashboard/common/ObjectTableNavigationButtons.jsx
index 4109c641..a8798e00 100644
--- a/src/components/Dashboard/common/ObjectTableNavigationButtons.jsx
+++ b/src/components/Dashboard/common/ObjectTableNavigationButtons.jsx
@@ -1,8 +1,7 @@
import { useContext, useEffect, useState, useCallback, useRef } from 'react'
import { Button, Divider, Flex, Space } from 'antd'
import { useSearchParams } from 'react-router-dom'
-import ArrowLeftIcon from '../../Icons/ArrowLeftIcon.jsx'
-import ArrowRightIcon from '../../Icons/ArrowRightIcon.jsx'
+
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
@@ -11,6 +10,8 @@ import {
useTableStatePersistence
} from '../context/TableStateContext'
import KeyboardShortcut from './KeyboardShortcut.jsx'
+import ChevronLeftIcon from '../../Icons/ChevronLeftIcon.jsx'
+import ChevronRightIcon from '../../Icons/ChevronRightIcon.jsx'
const ObjectTableNavigationButtons = ({
disabled,
@@ -25,10 +26,10 @@ const ObjectTableNavigationButtons = ({
const [loading, setLoading] = useState(false)
const { getPersistedFilter, getPersistedSorter } = useTableStatePersistence({
- scope: objectType,
- useFilterInSession: true,
- useSortInSession: true
- })
+ scope: objectType,
+ useFilterInSession: true,
+ useSortInSession: true
+ })
const getObjectNeighborsRef = useRef(getObjectNeighbors)
getObjectNeighborsRef.current = getObjectNeighbors
@@ -114,15 +115,21 @@ const ObjectTableNavigationButtons = ({
{showEndingDivider && (
)}
-
+
}
+ type='text'
+ icon={
+
+ }
onClick={handlePrevious}
+ style={{ padding: 0, height: '22px', width: '32px' }}
disabled={disabled || loading || !neighbors.previous?._id}
/>
@@ -132,12 +139,18 @@ const ObjectTableNavigationButtons = ({
onTrigger={handleNextShortcut}
>
}
+ type='text'
+ icon={
+
+ }
onClick={handleNext}
+ style={{ padding: 0, height: '22px', width: '32px' }}
disabled={disabled || loading || !neighbors.next?._id}
/>
-
+
)
}
diff --git a/src/components/Dashboard/common/Tooltip.jsx b/src/components/Dashboard/common/Tooltip.jsx
index da126863..24828442 100644
--- a/src/components/Dashboard/common/Tooltip.jsx
+++ b/src/components/Dashboard/common/Tooltip.jsx
@@ -7,6 +7,16 @@ const mergeHandler = (original, next) => (event) => {
original?.(event)
}
+const mergeRefs = (...refs) => (node) => {
+ refs.forEach((ref) => {
+ if (typeof ref === 'function') {
+ ref(node)
+ } else if (ref) {
+ ref.current = node
+ }
+ })
+}
+
const Tooltip = ({
children,
title,
@@ -17,7 +27,7 @@ const Tooltip = ({
const { showTooltip, hideTooltip } = useTooltipContext()
const id = useId()
const tooltipContent = title ?? content
- const spanRef = useRef(null)
+ const childRef = useRef(null)
const hoveredTargetsRef = useRef(new Set())
const hoveringRef = useRef(false)
const tooltipContentRef = useRef(tooltipContent)
@@ -60,8 +70,8 @@ const Tooltip = ({
onMouseLeaveRef.current = onMouseLeave
useEffect(() => {
- const span = spanRef.current
- if (!span || listenParents < 1) return
+ const child = childRef.current
+ if (!child || listenParents < 1) return
const hoveredTargets = hoveredTargetsRef.current
@@ -69,7 +79,7 @@ const Tooltip = ({
const handleLeave = (event) => onMouseLeaveRef.current(event)
const parents = []
- let current = span.parentElement
+ let current = child.parentElement
for (let i = 0; i < listenParents && current; i += 1) {
parents.push(current)
current = current.parentElement
@@ -89,30 +99,15 @@ const Tooltip = ({
}
}, [listenParents])
- const wrapWithSpan = (node) => (
-
- {node}
-
- )
-
- if (isValidElement(children)) {
- const trigger = cloneElement(children, {
- onMouseEnter: mergeHandler(children.props.onMouseEnter, onMouseEnter),
- onMouseLeave: mergeHandler(children.props.onMouseLeave, onMouseLeave)
- })
-
- if (listenParents > 0) {
- return wrapWithSpan(children)
- }
-
- if (children.props.disabled) {
- return wrapWithSpan(trigger)
- }
-
- return trigger
+ if (!isValidElement(children)) {
+ return children
}
- return wrapWithSpan(children)
+ return cloneElement(children, {
+ ref: mergeRefs(children.ref, childRef),
+ onMouseEnter: mergeHandler(children.props.onMouseEnter, onMouseEnter),
+ onMouseLeave: mergeHandler(children.props.onMouseLeave, onMouseLeave)
+ })
}
Tooltip.propTypes = {
diff --git a/src/components/Dashboard/context/DashboardObjectToolsContext.jsx b/src/components/Dashboard/context/DashboardObjectToolsContext.jsx
new file mode 100644
index 00000000..582b9857
--- /dev/null
+++ b/src/components/Dashboard/context/DashboardObjectToolsContext.jsx
@@ -0,0 +1,78 @@
+import {
+ createContext,
+ useCallback,
+ useContext,
+ useEffect,
+ useRef,
+ useState
+} from 'react'
+import PropTypes from 'prop-types'
+
+const DashboardObjectToolsContext = createContext()
+
+export const DashboardObjectToolsProvider = ({ children }) => {
+ const [currentObjectTools, setCurrentObjectToolsState] = useState(null)
+ const ownerRef = useRef(null)
+
+ const setCurrentObjectTools = useCallback((tools, ownerId) => {
+ ownerRef.current = ownerId
+ setCurrentObjectToolsState(tools)
+ }, [])
+
+ const clearCurrentObjectTools = useCallback((ownerId) => {
+ if (ownerRef.current !== ownerId) {
+ return
+ }
+ ownerRef.current = null
+ setCurrentObjectToolsState(null)
+ }, [])
+
+ return (
+
+ {children}
+
+ )
+}
+
+DashboardObjectToolsProvider.propTypes = {
+ children: PropTypes.node.isRequired
+}
+
+// eslint-disable-next-line react-refresh/only-export-components
+export const useDashboardObjectToolsContext = () => {
+ const context = useContext(DashboardObjectToolsContext)
+ if (!context) {
+ throw new Error(
+ 'useDashboardObjectToolsContext must be used within a DashboardObjectToolsProvider'
+ )
+ }
+ return context
+}
+
+// eslint-disable-next-line react-refresh/only-export-components
+export const useDashboardObjectTools = (tools) => {
+ const { setCurrentObjectTools, clearCurrentObjectTools } =
+ useDashboardObjectToolsContext()
+ const ownerIdRef = useRef(null)
+
+ if (ownerIdRef.current == null) {
+ ownerIdRef.current = Symbol('dashboardObjectTools')
+ }
+
+ useEffect(() => {
+ setCurrentObjectTools(tools, ownerIdRef.current)
+ }, [tools, setCurrentObjectTools])
+
+ useEffect(() => {
+ const ownerId = ownerIdRef.current
+ return () => clearCurrentObjectTools(ownerId)
+ }, [clearCurrentObjectTools])
+}
+
+export { DashboardObjectToolsContext }
diff --git a/src/components/Icons/ChevronLeftIcon.jsx b/src/components/Icons/ChevronLeftIcon.jsx
new file mode 100644
index 00000000..678a80d9
--- /dev/null
+++ b/src/components/Icons/ChevronLeftIcon.jsx
@@ -0,0 +1,6 @@
+import Icon from '@ant-design/icons'
+import CustomIconSvg from '../../../assets/icons/chevronlefticon.svg?react'
+
+const ChevronLeftIcon = (props) =>
+
+export default ChevronLeftIcon