Refactor text display components to use ElipsisText for improved overflow handling

- Introduced a new ElipsisText component to manage text overflow and truncation more effectively.
- Replaced instances of the Text component with ElipsisText across various components including AddressDisplay, EmailDisplay, and FileList for consistent text handling.
- Updated styles in multiple components to ensure proper layout and responsiveness when displaying long text.
This commit is contained in:
Tom Butcher 2026-08-19 15:12:55 +01:00
parent 93bd485ae3
commit d8c13426d1
18 changed files with 330 additions and 69 deletions

View File

@ -330,6 +330,28 @@ code {
padding: 0 !important; padding: 0 !important;
} }
.iddisplay > *:first-child {
min-width: 0;
flex: 0 1 auto;
max-width: 100%;
overflow: hidden;
}
.iddisplay .ant-btn {
flex-shrink: 0;
}
.miscid > *:first-child {
min-width: 0;
flex: 0 1 auto;
max-width: 100%;
overflow: hidden;
}
.miscid .ant-btn {
flex-shrink: 0;
}
.ant-popover-inner:has(.spotlight-tooltip) { .ant-popover-inner:has(.spotlight-tooltip) {
padding: 0 !important; padding: 0 !important;
} }
@ -1139,3 +1161,133 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
.filter-sidebar-field-select .ant-select-selector { .filter-sidebar-field-select .ant-select-selector {
width: max-content !important; width: max-content !important;
} }
.elipsis-text-wrapper.ant-typography {
display: block;
min-width: 0;
max-width: 100%;
width: auto;
overflow: hidden;
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
}
.elipsis-text-wrapper.ant-typography .elipsis-text {
display: flex;
flex-direction: row;
align-items: baseline;
width: 100%;
max-width: 100%;
min-width: 0;
position: relative;
overflow: hidden;
white-space: pre;
}
.elipsis-text-wrapper.ant-typography code.elipsis-text-measure,
.elipsis-text-wrapper.ant-typography code.elipsis-text-full,
.elipsis-text-wrapper.ant-typography code.elipsis-text-truncated {
margin: 0;
box-sizing: border-box;
}
.elipsis-text-measure {
position: absolute;
visibility: hidden;
pointer-events: none;
white-space: pre;
}
.elipsis-text-full {
display: none;
position: absolute;
left: 0;
top: 0;
width: max-content;
max-width: 100%;
min-width: 0;
overflow: hidden;
white-space: pre;
}
.elipsis-text:not(.is-truncated) .elipsis-text-full {
display: inline-block;
}
.elipsis-text.is-truncated .elipsis-text-full {
display: none;
}
.elipsis-text:not(.is-truncated) .elipsis-text-truncated .elipsis-text-dots {
display: none;
}
.elipsis-text:not(.is-truncated) .elipsis-text-truncated {
visibility: hidden;
}
.elipsis-text-truncated {
display: flex;
flex: 1 1 0;
align-items: baseline;
min-width: 0;
width: 100%;
max-width: 100%;
overflow: hidden;
white-space: pre;
}
.elipsis-text-wrapper.ant-typography code.elipsis-text-truncated {
display: flex;
}
.elipsis-text-start,
.elipsis-text-end {
flex: 1 1 0;
min-width: 0;
overflow: hidden;
white-space: pre;
}
.elipsis-text-start {
mask-image: linear-gradient(
to right,
#000 0%,
#000 calc(100% - 0.8em),
transparent 100%
);
-webkit-mask-image: linear-gradient(
to right,
#000 0%,
#000 calc(100% - 0.8em),
transparent 100%
);
}
.elipsis-text-end {
display: flex;
justify-content: flex-end;
mask-image: linear-gradient(
to left,
#000 0%,
#000 calc(100% - 0.8em),
transparent 100%
);
-webkit-mask-image: linear-gradient(
to left,
#000 0%,
#000 calc(100% - 0.8em),
transparent 100%
);
}
.elipsis-text-end-inner {
flex: 0 0 auto;
white-space: pre;
}
.elipsis-text-dots {
flex: 0 0 auto;
user-select: none;
}

View File

@ -6,6 +6,7 @@ import CloudIcon from '../../../Icons/CloudIcon'
import HostIcon from '../../../Icons/HostIcon' import HostIcon from '../../../Icons/HostIcon'
import ReloadIcon from '../../../Icons/ReloadIcon' import ReloadIcon from '../../../Icons/ReloadIcon'
import HProgress from '../../common/HProgress' import HProgress from '../../common/HProgress'
import ElipsisText from '../../common/ElipsisText'
import CheckCircleIcon from '../../../Icons/CheckCircleIcon' import CheckCircleIcon from '../../../Icons/CheckCircleIcon'
import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon' import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon'
@ -143,9 +144,9 @@ const UpdateStage = ({ stage, status, percent, detail }) => {
style={{ flex: 1, margin: 0 }} style={{ flex: 1, margin: 0 }}
/> />
{detail && ( {detail && (
<Text type='secondary' ellipsis style={{ minWidth: 0 }}> <ElipsisText type='secondary' style={{ minWidth: 0 }}>
{detail} {detail}
</Text> </ElipsisText>
)} )}
</Flex> </Flex>
)} )}

View File

@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import { Flex, Typography, Input } from 'antd' import { Flex, Typography, Input } from 'antd'
import CountryDisplay from './CountryDisplay' import CountryDisplay from './CountryDisplay'
import CountrySelect from './CountrySelect' import CountrySelect from './CountrySelect'
import ElipsisText from './ElipsisText'
const { Text } = Typography const { Text } = Typography
@ -50,9 +51,9 @@ const AddressDisplay = ({
{isCountry ? ( {isCountry ? (
<CountryDisplay countryCode={fieldValue} /> <CountryDisplay countryCode={fieldValue} />
) : ( ) : (
<Text ellipsis alt={label}> <ElipsisText alt={label}>
{fieldValue} {fieldValue}
</Text> </ElipsisText>
)} )}
</Flex> </Flex>
) )

View File

@ -1,9 +1,8 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Flex, Typography } from 'antd' import { Flex } from 'antd'
import Flag from './Flag' import Flag from './Flag'
import countries from '../../../database/Countries' import countries from '../../../database/Countries'
import ElipsisText from './ElipsisText'
const { Text } = Typography
const CountryDisplay = ({ countryCode }) => { const CountryDisplay = ({ countryCode }) => {
const country = countries.find((c) => c.code === countryCode) const country = countries.find((c) => c.code === countryCode)
@ -17,7 +16,7 @@ const CountryDisplay = ({ countryCode }) => {
gap={'small'} gap={'small'}
align='center' align='center'
className='country-display' className='country-display'
style={{ minWidth: 0 }} style={{ minWidth: 0, width: '100%' }}
> >
<Flag <Flag
code={country.code} code={country.code}
@ -25,8 +24,8 @@ const CountryDisplay = ({ countryCode }) => {
hasBorder={true} hasBorder={true}
borderRadius={5} borderRadius={5}
/> />
<div style={{ minWidth: 0 }}> <div style={{ minWidth: 0, flex: 1, overflow: 'hidden' }}>
<Text ellipsis>{country.name}</Text> <ElipsisText>{country.name}</ElipsisText>
</div> </div>
</Flex> </Flex>
) )

View File

@ -1,9 +1,8 @@
import { Select, Flex, Typography } from 'antd' import { Select, Flex } from 'antd'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Flag from './Flag' import Flag from './Flag'
import countries from '../../../database/Countries' import countries from '../../../database/Countries'
import ElipsisText from './ElipsisText'
const { Text } = Typography
const CountrySelect = ({ const CountrySelect = ({
value, value,
@ -30,7 +29,7 @@ const CountrySelect = ({
label: ( label: (
<Flex gap='small' align='center' className='country-display'> <Flex gap='small' align='center' className='country-display'>
<Flag code={country.code} size='small' borderRadius={3} /> <Flag code={country.code} size='small' borderRadius={3} />
<Text elipsis>{country.name}</Text> <ElipsisText>{country.name}</ElipsisText>
</Flex> </Flex>
) )
}))} }))}

View File

@ -22,6 +22,7 @@ import { Header } from 'antd/es/layout/layout'
import { useMediaQuery } from 'react-responsive' import { useMediaQuery } from 'react-responsive'
import KeyboardShortcut from './KeyboardShortcut' import KeyboardShortcut from './KeyboardShortcut'
import UserProfilePopover from './UserProfilePopover' import UserProfilePopover from './UserProfilePopover'
import ElipsisText from './ElipsisText'
import FarmControlLogo from '../../Logos/FarmControlLogo' import FarmControlLogo from '../../Logos/FarmControlLogo'
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall' import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
@ -194,19 +195,17 @@ const DashboardNavigation = () => {
}} }}
> >
{selectedMenuItem.icon} {selectedMenuItem.icon}
<Text <ElipsisText
ellipsis
style={{ style={{
minWidth: 0, // CRUCIAL for flex children minWidth: 0,
maxWidth: '100%', // Responsive maxWidth: '100%',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', flex: 1
flex: 1 // Take available space, shrink as needed
}} }}
> >
{selectedMenuItem.label} {selectedMenuItem.label}
</Text> </ElipsisText>
</Flex> </Flex>
)} )}
<div style={{ flexGrow: 1 }}> <div style={{ flexGrow: 1 }}>

View File

@ -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
{...rest}
className={['elipsis-text-wrapper', className].filter(Boolean).join(' ')}
title={title ?? text}
style={style}
>
<span
className={
showTruncated ? 'elipsis-text is-truncated' : 'elipsis-text'
}
ref={containerRef}
>
<RootTag className='elipsis-text-measure' ref={measureRef}>
{text}
</RootTag>
<RootTag className='elipsis-text-full'>{text}</RootTag>
<RootTag className='elipsis-text-truncated'>
<span className='elipsis-text-start'>{start}</span>
<span className='elipsis-text-dots'>...</span>
<span className='elipsis-text-end'>
<span className='elipsis-text-end-inner'>{end}</span>
</span>
</RootTag>
</span>
</Text>
)
}
ElipsisText.propTypes = {
children: PropTypes.node,
style: PropTypes.object,
className: PropTypes.string,
title: PropTypes.string,
code: PropTypes.bool
}
export default ElipsisText

View File

@ -3,6 +3,7 @@ import { Typography, Flex, Button, Tooltip } from 'antd'
import NewMailIcon from '../../Icons/NewMailIcon' import NewMailIcon from '../../Icons/NewMailIcon'
// import CopyIcon from './CopyIcon' // import CopyIcon from './CopyIcon'
import CopyButton from './CopyButton' import CopyButton from './CopyButton'
import ElipsisText from './ElipsisText'
const { Text, Link } = Typography const { Text, Link } = Typography
@ -18,9 +19,9 @@ const EmailDisplay = ({ email, showCopy = true, showLink = false }) => {
</Link> </Link>
) : ( ) : (
<> <>
<Text style={{ marginRight: 8 }} ellipsis> <ElipsisText style={{ marginRight: 8 }}>
{email} {email}
</Text> </ElipsisText>
<Tooltip title='Email' arrow={false}> <Tooltip title='Email' arrow={false}>
<Button <Button
icon={<NewMailIcon />} icon={<NewMailIcon />}

View File

@ -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 PropTypes from 'prop-types'
import FileIcon from '../../Icons/FileIcon' import FileIcon from '../../Icons/FileIcon'
import BinIcon from '../../Icons/BinIcon' import BinIcon from '../../Icons/BinIcon'
@ -11,8 +11,7 @@ import EyeSlashIcon from '../../Icons/EyeSlashIcon'
import { getModelByName } from '../../../database/ObjectModels' import { getModelByName } from '../../../database/ObjectModels'
import InfoCircleIcon from '../../Icons/InfoCircleIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import ElipsisText from './ElipsisText'
const { Text } = Typography
const FileList = ({ const FileList = ({
files, files,
@ -87,15 +86,15 @@ const FileList = ({
<Flex <Flex
gap={'small'} gap={'small'}
align='center' align='center'
style={{ minWidth: 0, flex: '0 1 auto', overflow: 'hidden' }} style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}
> >
<FileIcon <FileIcon
style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }} style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }}
/> />
<Text style={{ marginTop: '1px', minWidth: 0 }} ellipsis> <ElipsisText style={{ marginTop: '1px', minWidth: 0 }}>
{file.name || file.filename || 'Unknown file'} {file.name || file.filename || 'Unknown file'}
</Text> </ElipsisText>
{file.extension && <Tag style={{ margin: 0 }}>{file.extension}</Tag>} {file.extension && <Tag style={{ margin: 0 }}>{file.extension}</Tag>}
</Flex> </Flex>
{!minimal && ( {!minimal && (

View File

@ -4,6 +4,7 @@ import { Flex, Typography, Popover } from 'antd'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useMediaQuery } from 'react-responsive' import { useMediaQuery } from 'react-responsive'
import CopyButton from './CopyButton' import CopyButton from './CopyButton'
import ElipsisText from './ElipsisText'
import SpotlightTooltip from './SpotlightTooltip' import SpotlightTooltip from './SpotlightTooltip'
import { getModelByName } from '../../../database/ObjectModels' import { getModelByName } from '../../../database/ObjectModels'
import { buildActionUrl } from '../../../utils/modelActions' import { buildActionUrl } from '../../../utils/modelActions'
@ -68,13 +69,12 @@ const IdDisplay = ({
if (plainCode == false) { if (plainCode == false) {
textElement = ( textElement = (
<Text <ElipsisText
code code
ellipsis
style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined} style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined}
> >
{displayId} {displayId}
</Text> </ElipsisText>
) )
} }
@ -83,8 +83,12 @@ const IdDisplay = ({
const linkElement = ( const linkElement = (
<Link <Link
onClick={() => navigate(hyperlink)} onClick={() => navigate(hyperlink)}
ellipsis style={{
style={showCopy ? { marginRight: 6 } : undefined} minWidth: 0,
maxWidth: '100%',
display: 'block',
...(showCopy ? { marginRight: 6 } : null)
}}
> >
{textElement} {textElement}
</Link> </Link>

View File

@ -1,6 +1,7 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Flex, Typography } from 'antd' import { Flex, Typography } from 'antd'
import CopyButton from './CopyButton' import CopyButton from './CopyButton'
import ElipsisText from './ElipsisText'
const { Text } = Typography const { Text } = Typography
@ -15,13 +16,12 @@ const MiscId = ({ value, showCopy = true }) => {
className='miscid' className='miscid'
style={{ minWidth: '0px', width: '100%' }} style={{ minWidth: '0px', width: '100%' }}
> >
<Text <ElipsisText
code code
ellipsis
style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined} style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined}
> >
{value} {value}
</Text> </ElipsisText>
{showCopy && ( {showCopy && (
<CopyButton <CopyButton

View File

@ -23,6 +23,7 @@ import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
import CheckCircleIcon from '../../Icons/CheckCircleIcon' import CheckCircleIcon from '../../Icons/CheckCircleIcon'
import BellIcon from '../../Icons/BellIcon' import BellIcon from '../../Icons/BellIcon'
import { getModelByName } from '../../../database/ObjectModels' import { getModelByName } from '../../../database/ObjectModels'
import ElipsisText from './ElipsisText'
const { Text, Paragraph } = Typography const { Text, Paragraph } = Typography
@ -361,13 +362,12 @@ const Notification = ({
{inlineIcon && {inlineIcon &&
getNotificationIcon(notification.type, notification.metadata)} getNotificationIcon(notification.type, notification.metadata)}
<Text <ElipsisText
strong={!notification.read} strong={!notification.read}
ellipsis
style={{ minWidth: 0 }} style={{ minWidth: 0 }}
> >
{notification.title} {notification.title}
</Text> </ElipsisText>
</Flex> </Flex>
<Flex align='center' gap='5px' style={{ marginLeft: 10 }}> <Flex align='center' gap='5px' style={{ marginLeft: 10 }}>
{showDetails && ( {showDetails && (

View File

@ -9,6 +9,7 @@ import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext' import { AuthContext } from '../context/AuthContext'
import merge from 'lodash/merge' import merge from 'lodash/merge'
import IdDisplay from './IdDisplay' import IdDisplay from './IdDisplay'
import ElipsisText from './ElipsisText'
import SpotlightTooltip from './SpotlightTooltip' import SpotlightTooltip from './SpotlightTooltip'
import StateTag from './StateTag' import StateTag from './StateTag'
@ -183,13 +184,7 @@ const ObjectDisplay = ({
const objectId = getStringId(objectData) const objectId = getStringId(objectData)
if (defaultModelActions.length >= 1 && objectId) { if (defaultModelActions.length >= 1 && objectId) {
hyperlink = buildActionUrl( hyperlink = buildActionUrl(model, defaultModelActions[0], objectId, '', '')
model,
defaultModelActions[0],
objectId,
'',
''
)
} }
// Render name with hyperlink/spotlight support // Render name with hyperlink/spotlight support
@ -197,9 +192,9 @@ const ObjectDisplay = ({
if (!objectData?.name) return null if (!objectData?.name) return null
const textElement = ( const textElement = (
<Text ellipsis style={{ lineHeight: '1', paddingBottom: '2px' }}> <ElipsisText style={{ lineHeight: '1', paddingBottom: '2px' }}>
{objectData.name} {objectData.name}
</Text> </ElipsisText>
) )
// If hyperlink is enabled // If hyperlink is enabled
@ -268,7 +263,7 @@ const ObjectDisplay = ({
style={{ minWidth: 0, height: '24px' }} style={{ minWidth: 0, height: '24px' }}
> >
<Icon /> <Icon />
<Flex gap={'small'} align='center' style={{ minWidth: 0 }}> <Flex gap={'small'} align='center' style={{ minWidth: 0, flex: 1 }}>
{objectData?.color ? ( {objectData?.color ? (
<Badge <Badge
color={objectData?.color} color={objectData?.color}
@ -282,7 +277,7 @@ const ObjectDisplay = ({
style={{ marginBottom: '1.5px' }} style={{ marginBottom: '1.5px' }}
/> />
) : null} ) : null}
<div style={{ minWidth: 0 }}> <div style={{ minWidth: 0, flex: 1, overflow: 'hidden' }}>
{renderNameDisplay()} {renderNameDisplay()}
{objectId && !objectData?.name ? ( {objectId && !objectData?.name ? (

View File

@ -51,6 +51,7 @@ import MiscId from './MiscId'
import AddressDisplay from './AddressDisplay' import AddressDisplay from './AddressDisplay'
import ArrayDisplay from './ArrayDisplay' import ArrayDisplay from './ArrayDisplay'
import ArrayInput from './ArrayInput' import ArrayInput from './ArrayInput'
import ElipsisText from './ElipsisText'
import { round } from '../utils/Utils' import { round } from '../utils/Utils'
const { Text } = Typography const { Text } = Typography
@ -378,11 +379,11 @@ const ObjectProperty = ({
case 'text': case 'text':
if (value != null && value != '') { if (value != null && value != '') {
return ( return (
<Text ellipsis style={style}> <ElipsisText style={style}>
{prefix} {prefix}
{getTextValue(value)} {getTextValue(value)}
{suffix} {suffix}
</Text> </ElipsisText>
) )
} else { } else {
return ( return (

View File

@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import { Typography, Flex, Button, Tooltip } from 'antd' import { Typography, Flex, Button, Tooltip } from 'antd'
import LinkIcon from '../../Icons/LinkIcon' import LinkIcon from '../../Icons/LinkIcon'
import CopyButton from './CopyButton' import CopyButton from './CopyButton'
import ElipsisText from './ElipsisText'
const { Text, Link } = Typography const { Text, Link } = Typography
@ -24,15 +25,15 @@ const UrlDisplay = ({ url, showCopy = true, showLink = false }) => {
width: 0 width: 0
}} }}
> >
<Text ellipsis style={{ display: 'block' }}> <ElipsisText style={{ display: 'block' }}>
{url} {url}
</Text> </ElipsisText>
</Link> </Link>
) : ( ) : (
<> <>
<Text style={{ marginRight: 8, minWidth: 0 }} ellipsis> <ElipsisText style={{ marginRight: 8, minWidth: 0 }}>
{url} {url}
</Text> </ElipsisText>
<Tooltip title='Open URL' arrow={false}> <Tooltip title='Open URL' arrow={false}>
<Button <Button
icon={<LinkIcon />} icon={<LinkIcon />}

View File

@ -8,6 +8,7 @@ import { User } from '../../../database/models/User'
import { buildActionUrl } from '../../../utils/modelActions' import { buildActionUrl } from '../../../utils/modelActions'
import { AuthContext } from '../context/AuthContext' import { AuthContext } from '../context/AuthContext'
import ObjectActions from './ObjectActions' import ObjectActions from './ObjectActions'
import ElipsisText from './ElipsisText'
const { Text } = Typography const { Text } = Typography
@ -89,13 +90,12 @@ const UserProfilePopover = ({ onClose }) => {
)} )}
<Flex vertical style={{ minWidth: 0 }}> <Flex vertical style={{ minWidth: 0 }}>
{fullName && ( {fullName && (
<Text <ElipsisText
strong strong
ellipsis
style={{ fontSize: 18, lineHeight: 1, marginTop: 1 }} style={{ fontSize: 18, lineHeight: 1, marginTop: 1 }}
> >
{fullName} {fullName}
</Text> </ElipsisText>
)} )}
{email && ( {email && (
<Space> <Space>
@ -106,13 +106,12 @@ const UserProfilePopover = ({ onClose }) => {
> >
@{username} @{username}
</Text> </Text>
<Text <ElipsisText
type='secondary' type='secondary'
ellipsis
style={{ fontSize: 12, lineHeight: 1 }} style={{ fontSize: 12, lineHeight: 1 }}
> >
{email} {email}
</Text> </ElipsisText>
</Space> </Space>
)} )}
{!username && !fullName && !email && ( {!username && !fullName && !email && (

View File

@ -16,6 +16,7 @@ import {
buildActionUrl, buildActionUrl,
stripActionParams stripActionParams
} from '../../../utils/modelActions' } from '../../../utils/modelActions'
import ElipsisText from '../common/ElipsisText'
const ActionsModalContext = createContext() const ActionsModalContext = createContext()
@ -291,15 +292,14 @@ const ActionsModalProvider = ({ children }) => {
> >
{Icon ? <Icon style={{ fontSize: '18px' }} /> : null} {Icon ? <Icon style={{ fontSize: '18px' }} /> : null}
<Flex vertical style={{ flexGrow: 1, minWidth: 0 }}> <Flex vertical style={{ flexGrow: 1, minWidth: 0 }}>
<Text <ElipsisText
ellipsis
style={{ style={{
maxWidth: 320, maxWidth: 320,
width: '100%' width: '100%'
}} }}
> >
{action.fullLabel || action.label || action.name} {action.fullLabel || action.label || action.name}
</Text> </ElipsisText>
</Flex> </Flex>
</Flex> </Flex>
<Flex gap='small' align='center'> <Flex gap='small' align='center'>

View File

@ -26,6 +26,7 @@ import PropTypes from 'prop-types'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import StateDisplay from '../common/StateDisplay' import StateDisplay from '../common/StateDisplay'
import IdDisplay from '../common/IdDisplay' import IdDisplay from '../common/IdDisplay'
import ElipsisText from '../common/ElipsisText'
import config from '../../../config' import config from '../../../config'
import { import {
@ -501,8 +502,7 @@ const SpotlightContent = ({
{Icon ? <Icon style={{ fontSize: '20px' }} /> : null} {Icon ? <Icon style={{ fontSize: '20px' }} /> : null}
{item.name ? ( {item.name ? (
<Text <ElipsisText
ellipsis
style={{ style={{
maxWidth: 170, maxWidth: 170,
flexGrow: 1, flexGrow: 1,
@ -510,7 +510,7 @@ const SpotlightContent = ({
}} }}
> >
{item.name} {item.name}
</Text> </ElipsisText>
) : null} ) : null}
{item?.state ? ( {item?.state ? (
<StateDisplay <StateDisplay