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:
parent
93bd485ae3
commit
d8c13426d1
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
}))}
|
}))}
|
||||||
|
|||||||
@ -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 }}>
|
||||||
|
|||||||
110
src/components/Dashboard/common/ElipsisText.jsx
Normal file
110
src/components/Dashboard/common/ElipsisText.jsx
Normal 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
|
||||||
@ -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 />}
|
||||||
|
|||||||
@ -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 && (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 && (
|
||||||
|
|||||||
@ -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 ? (
|
||||||
|
|||||||
@ -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 (
|
||||||
|
|||||||
@ -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 />}
|
||||||
|
|||||||
@ -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 && (
|
||||||
|
|||||||
@ -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'>
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user