-
{country.name}
+
+ {country.name}
)
diff --git a/src/components/Dashboard/common/CountrySelect.jsx b/src/components/Dashboard/common/CountrySelect.jsx
index abb1bd85..f16bcf6c 100644
--- a/src/components/Dashboard/common/CountrySelect.jsx
+++ b/src/components/Dashboard/common/CountrySelect.jsx
@@ -1,9 +1,8 @@
-import { Select, Flex, Typography } from 'antd'
+import { Select, Flex } from 'antd'
import PropTypes from 'prop-types'
import Flag from './Flag'
import countries from '../../../database/Countries'
-
-const { Text } = Typography
+import ElipsisText from './ElipsisText'
const CountrySelect = ({
value,
@@ -30,7 +29,7 @@ const CountrySelect = ({
label: (
- {country.name}
+ {country.name}
)
}))}
diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx
index f0ddfb1e..ef4b7653 100644
--- a/src/components/Dashboard/common/DashboardNavigation.jsx
+++ b/src/components/Dashboard/common/DashboardNavigation.jsx
@@ -22,6 +22,7 @@ import { Header } from 'antd/es/layout/layout'
import { useMediaQuery } from 'react-responsive'
import KeyboardShortcut from './KeyboardShortcut'
import UserProfilePopover from './UserProfilePopover'
+import ElipsisText from './ElipsisText'
import FarmControlLogo from '../../Logos/FarmControlLogo'
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
@@ -194,19 +195,17 @@ const DashboardNavigation = () => {
}}
>
{selectedMenuItem.icon}
-
{selectedMenuItem.label}
-
+
)}
diff --git a/src/components/Dashboard/common/ElipsisText.jsx b/src/components/Dashboard/common/ElipsisText.jsx
new file mode 100644
index 00000000..2fa75343
--- /dev/null
+++ b/src/components/Dashboard/common/ElipsisText.jsx
@@ -0,0 +1,110 @@
+import { useLayoutEffect, useRef, useState } from 'react'
+import PropTypes from 'prop-types'
+import { Typography } from 'antd'
+
+const { Text } = Typography
+
+const childrenToString = (children) => {
+ if (children == null || typeof children === 'boolean') return ''
+ if (typeof children === 'string' || typeof children === 'number') {
+ return String(children)
+ }
+ if (Array.isArray(children)) {
+ return children.map(childrenToString).join('')
+ }
+ if (typeof children === 'object' && children.props) {
+ return childrenToString(children.props.children)
+ }
+ return ''
+}
+
+const splitMiddle = (text) => {
+ const mid = Math.ceil(text.length / 2)
+ return {
+ start: text.slice(0, mid),
+ end: text.slice(mid)
+ }
+}
+
+const ElipsisText = ({ children, style, className, title, code, ...rest }) => {
+ const text = childrenToString(children)
+ const { start, end } = splitMiddle(text)
+
+ const containerRef = useRef(null)
+ const measureRef = useRef(null)
+ const truncatedRef = useRef(false)
+ const [truncated, setTruncated] = useState(false)
+
+ useLayoutEffect(() => {
+ const container = containerRef.current
+ const measure = measureRef.current
+ if (!container || !measure) return
+
+ const update = () => {
+ const available = container.getBoundingClientRect().width
+ if (available < 1) return
+
+ const textWidth = measure.getBoundingClientRect().width
+ const next = Boolean(end) && textWidth > available
+ if (truncatedRef.current === next) return
+ truncatedRef.current = next
+ setTruncated(next)
+ }
+
+ update()
+ const frame = requestAnimationFrame(update)
+
+ const resizeObserver = new ResizeObserver(update)
+ resizeObserver.observe(container)
+
+ const intersectionObserver = new IntersectionObserver(update)
+ intersectionObserver.observe(container)
+
+ return () => {
+ cancelAnimationFrame(frame)
+ resizeObserver.disconnect()
+ intersectionObserver.disconnect()
+ }
+ }, [text, end])
+
+ const showTruncated = truncated && Boolean(end)
+ const RootTag = code ? 'code' : 'span'
+
+ return (
+
+
+
+ {text}
+
+ {text}
+
+ {start}
+ ...
+
+ {end}
+
+
+
+
+ )
+}
+
+ElipsisText.propTypes = {
+ children: PropTypes.node,
+ style: PropTypes.object,
+ className: PropTypes.string,
+ title: PropTypes.string,
+ code: PropTypes.bool
+}
+
+export default ElipsisText
diff --git a/src/components/Dashboard/common/EmailDisplay.jsx b/src/components/Dashboard/common/EmailDisplay.jsx
index 58b22bcb..60550649 100644
--- a/src/components/Dashboard/common/EmailDisplay.jsx
+++ b/src/components/Dashboard/common/EmailDisplay.jsx
@@ -3,6 +3,7 @@ import { Typography, Flex, Button, Tooltip } from 'antd'
import NewMailIcon from '../../Icons/NewMailIcon'
// import CopyIcon from './CopyIcon'
import CopyButton from './CopyButton'
+import ElipsisText from './ElipsisText'
const { Text, Link } = Typography
@@ -18,9 +19,9 @@ const EmailDisplay = ({ email, showCopy = true, showLink = false }) => {
) : (
<>
-
+
{email}
-
+
}
diff --git a/src/components/Dashboard/common/FileList.jsx b/src/components/Dashboard/common/FileList.jsx
index d382d42f..c8e133f4 100644
--- a/src/components/Dashboard/common/FileList.jsx
+++ b/src/components/Dashboard/common/FileList.jsx
@@ -1,4 +1,4 @@
-import { Card, Flex, Typography, Button, Tag, Divider } from 'antd'
+import { Card, Flex, Button, Tag, Divider } from 'antd'
import PropTypes from 'prop-types'
import FileIcon from '../../Icons/FileIcon'
import BinIcon from '../../Icons/BinIcon'
@@ -11,8 +11,7 @@ import EyeSlashIcon from '../../Icons/EyeSlashIcon'
import { getModelByName } from '../../../database/ObjectModels'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import { useNavigate } from 'react-router-dom'
-
-const { Text } = Typography
+import ElipsisText from './ElipsisText'
const FileList = ({
files,
@@ -87,15 +86,15 @@ const FileList = ({
-
+
{file.name || file.filename || 'Unknown file'}
-
+
{file.extension && {file.extension}}
{!minimal && (
diff --git a/src/components/Dashboard/common/IdDisplay.jsx b/src/components/Dashboard/common/IdDisplay.jsx
index 2f07fa0a..132865fa 100644
--- a/src/components/Dashboard/common/IdDisplay.jsx
+++ b/src/components/Dashboard/common/IdDisplay.jsx
@@ -4,6 +4,7 @@ import { Flex, Typography, Popover } from 'antd'
import { useNavigate } from 'react-router-dom'
import { useMediaQuery } from 'react-responsive'
import CopyButton from './CopyButton'
+import ElipsisText from './ElipsisText'
import SpotlightTooltip from './SpotlightTooltip'
import { getModelByName } from '../../../database/ObjectModels'
import { buildActionUrl } from '../../../utils/modelActions'
@@ -68,13 +69,12 @@ const IdDisplay = ({
if (plainCode == false) {
textElement = (
-
{displayId}
-
+
)
}
@@ -83,8 +83,12 @@ const IdDisplay = ({
const linkElement = (
navigate(hyperlink)}
- ellipsis
- style={showCopy ? { marginRight: 6 } : undefined}
+ style={{
+ minWidth: 0,
+ maxWidth: '100%',
+ display: 'block',
+ ...(showCopy ? { marginRight: 6 } : null)
+ }}
>
{textElement}
diff --git a/src/components/Dashboard/common/MiscId.jsx b/src/components/Dashboard/common/MiscId.jsx
index dad222cf..413d9061 100644
--- a/src/components/Dashboard/common/MiscId.jsx
+++ b/src/components/Dashboard/common/MiscId.jsx
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types'
import { Flex, Typography } from 'antd'
import CopyButton from './CopyButton'
+import ElipsisText from './ElipsisText'
const { Text } = Typography
@@ -15,13 +16,12 @@ const MiscId = ({ value, showCopy = true }) => {
className='miscid'
style={{ minWidth: '0px', width: '100%' }}
>
-
{value}
-
+
{showCopy && (
{notification.title}
-
+
{showDetails && (
diff --git a/src/components/Dashboard/common/ObjectDisplay.jsx b/src/components/Dashboard/common/ObjectDisplay.jsx
index 2e1ab15c..81f00a12 100644
--- a/src/components/Dashboard/common/ObjectDisplay.jsx
+++ b/src/components/Dashboard/common/ObjectDisplay.jsx
@@ -9,6 +9,7 @@ import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import merge from 'lodash/merge'
import IdDisplay from './IdDisplay'
+import ElipsisText from './ElipsisText'
import SpotlightTooltip from './SpotlightTooltip'
import StateTag from './StateTag'
@@ -183,13 +184,7 @@ const ObjectDisplay = ({
const objectId = getStringId(objectData)
if (defaultModelActions.length >= 1 && objectId) {
- hyperlink = buildActionUrl(
- model,
- defaultModelActions[0],
- objectId,
- '',
- ''
- )
+ hyperlink = buildActionUrl(model, defaultModelActions[0], objectId, '', '')
}
// Render name with hyperlink/spotlight support
@@ -197,9 +192,9 @@ const ObjectDisplay = ({
if (!objectData?.name) return null
const textElement = (
-
+
{objectData.name}
-
+
)
// If hyperlink is enabled
@@ -268,7 +263,7 @@ const ObjectDisplay = ({
style={{ minWidth: 0, height: '24px' }}
>
-
+
{objectData?.color ? (
) : null}
-
+
{renderNameDisplay()}
{objectId && !objectData?.name ? (
diff --git a/src/components/Dashboard/common/ObjectProperty.jsx b/src/components/Dashboard/common/ObjectProperty.jsx
index be57872b..fd3e3cc3 100644
--- a/src/components/Dashboard/common/ObjectProperty.jsx
+++ b/src/components/Dashboard/common/ObjectProperty.jsx
@@ -51,6 +51,7 @@ import MiscId from './MiscId'
import AddressDisplay from './AddressDisplay'
import ArrayDisplay from './ArrayDisplay'
import ArrayInput from './ArrayInput'
+import ElipsisText from './ElipsisText'
import { round } from '../utils/Utils'
const { Text } = Typography
@@ -378,11 +379,11 @@ const ObjectProperty = ({
case 'text':
if (value != null && value != '') {
return (
-
+
{prefix}
{getTextValue(value)}
{suffix}
-
+
)
} else {
return (
diff --git a/src/components/Dashboard/common/UrlDisplay.jsx b/src/components/Dashboard/common/UrlDisplay.jsx
index 68f61e33..f82f3043 100644
--- a/src/components/Dashboard/common/UrlDisplay.jsx
+++ b/src/components/Dashboard/common/UrlDisplay.jsx
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import { Typography, Flex, Button, Tooltip } from 'antd'
import LinkIcon from '../../Icons/LinkIcon'
import CopyButton from './CopyButton'
+import ElipsisText from './ElipsisText'
const { Text, Link } = Typography
@@ -24,15 +25,15 @@ const UrlDisplay = ({ url, showCopy = true, showLink = false }) => {
width: 0
}}
>
-
+
{url}
-
+
) : (
<>
-
+
{url}
-
+
}
diff --git a/src/components/Dashboard/common/UserProfilePopover.jsx b/src/components/Dashboard/common/UserProfilePopover.jsx
index a932b4a2..9afc5bc7 100644
--- a/src/components/Dashboard/common/UserProfilePopover.jsx
+++ b/src/components/Dashboard/common/UserProfilePopover.jsx
@@ -8,6 +8,7 @@ import { User } from '../../../database/models/User'
import { buildActionUrl } from '../../../utils/modelActions'
import { AuthContext } from '../context/AuthContext'
import ObjectActions from './ObjectActions'
+import ElipsisText from './ElipsisText'
const { Text } = Typography
@@ -89,13 +90,12 @@ const UserProfilePopover = ({ onClose }) => {
)}
{fullName && (
-
{fullName}
-
+
)}
{email && (
@@ -106,13 +106,12 @@ const UserProfilePopover = ({ onClose }) => {
>
@{username}
-
{email}
-
+
)}
{!username && !fullName && !email && (
diff --git a/src/components/Dashboard/context/ActionsModalContext.jsx b/src/components/Dashboard/context/ActionsModalContext.jsx
index 7bc4d85a..3788a27d 100644
--- a/src/components/Dashboard/context/ActionsModalContext.jsx
+++ b/src/components/Dashboard/context/ActionsModalContext.jsx
@@ -16,6 +16,7 @@ import {
buildActionUrl,
stripActionParams
} from '../../../utils/modelActions'
+import ElipsisText from '../common/ElipsisText'
const ActionsModalContext = createContext()
@@ -291,15 +292,14 @@ const ActionsModalProvider = ({ children }) => {
>
{Icon ? : null}
-
{action.fullLabel || action.label || action.name}
-
+
diff --git a/src/components/Dashboard/context/SpotlightContext.jsx b/src/components/Dashboard/context/SpotlightContext.jsx
index eba668d2..125b0016 100644
--- a/src/components/Dashboard/context/SpotlightContext.jsx
+++ b/src/components/Dashboard/context/SpotlightContext.jsx
@@ -26,6 +26,7 @@ import PropTypes from 'prop-types'
import { useNavigate } from 'react-router-dom'
import StateDisplay from '../common/StateDisplay'
import IdDisplay from '../common/IdDisplay'
+import ElipsisText from '../common/ElipsisText'
import config from '../../../config'
import {
@@ -501,8 +502,7 @@ const SpotlightContent = ({
{Icon ? : null}
{item.name ? (
-
{item.name}
-
+
) : null}
{item?.state ? (