Ellipsis
Basic usage
You can enable ellipsis in Text by passing ellipsis settings to the ellipsis property.
To use the default settings, use ellipsis={true}.
Ellipsis can be enabled in all other components that are based on Text, such as Button.Text, Link.Text, Card.Title, and so on. To find out which components support the ellipsis property, refer to the API documentation.
import Tag from '@semcore/ui/tag';
import React from 'react';
const text = 'Intergalactic is a constantly developing system of UI components, guidelines and UX patterns.';
const Demo = () => {
return (
<Tag>
<Tag.Text w={180} ellipsis:cropPosition='middle'>
{text}
</Tag.Text>
</Tag>
);
};
export default Demo;
Performance optimization
If you have a lot of ellipsis instances on one screen, you can optimize the performance by using one observer for all instances.
import type { DataTableProps } from '@semcore/ui/data-table';
import { DataTable } from '@semcore/ui/data-table';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const columns = React.useMemo(() => {
return [
{ name: 'keyword', children: 'Keyword' },
{ name: 'kd', children: 'KD,%' },
{ name: 'cpc', children: 'CPC' },
{
name: 'vol',
children: 'Vol.',
gtcWidth: '100px',
},
];
}, []);
const renderCell: DataTableProps<any, any, any>['renderCell'] | undefined = React.useMemo(() => {
return (props) => {
const cellRef = React.useRef<HTMLDivElement | null>(null);
if (props.columnName === 'vol') {
return {
ref: cellRef,
children: (
<Text
ellipsis:cropPosition='middle'
hint:triggerRef={cellRef}
hint:placement='right'
flex={1}
>
{props.value}
</Text>
),
};
}
return props.defaultRender();
};
}, []);
return (
<DataTable
data={data}
aria-label='Table title'
columns={columns}
renderCell={renderCell}
/>
);
};
const data = [
{
keyword: 'ebay buy',
kd: '77.8',
cpc: '$1.25',
vol: '32,500,000,500,00032,500,000,500,00032,500,000,500,000',
},
{
keyword: 'www.ebay.com',
kd: '11.2',
cpc: '$3.4',
vol: '65,457,920,000,50032,500,000,500,00032,500,000,500,000',
},
{
keyword: 'www.ebay.com',
kd: '10',
cpc: '$0.65',
vol: '47,354,640,000,50032,500,000,500,00032,500,000,500,00032,500,000,500,000',
},
{
keyword: 'ebay buy',
kd: '-',
cpc: '$0',
vol: 'n/a',
},
{
keyword: 'ebay buy',
kd: '75.89',
cpc: '$0',
vol: '21,644,290,000,500',
},
];
export default Demo;
Search in cropped text
It's possible to implement text search in the cropped parts of the content.
You can use a CSSProperties object or a string with the class name to highlight the found text.
Note that you should calculate from/to indexes by yourself.
import SearchIcon from '@semcore/icon/Search/m';
import { Flex, Ellipsis } from '@semcore/ui/base-components';
import Input from '@semcore/ui/input';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const text = 'Intergalactic is a constantly developing system of UI components, guidelines and UX patterns.';
const highlightStyle = {
background: 'var(--intergalactic-bg-highlight-results, #ef980066)',
};
const minSearchLength = 3;
const Demo = () => {
const ref = React.useRef<HTMLSpanElement | null>(null);
const [ellipsis, setEllipsis] = React.useState<Ellipsis | undefined>(undefined);
const [search, setSearch] = React.useState('dev');
React.useEffect(() => {
if (ref.current) {
setEllipsis(new Ellipsis(ref.current, { cropPosition: 'middle' }));
}
}, []);
React.useEffect(() => {
const from = search.length >= minSearchLength
? text.indexOf(search)
: -1;
const to = from !== -1 ? from + search.length : -1;
if (ellipsis instanceof Ellipsis) {
ellipsis.setRequiredIndexes([from, to], highlightStyle);
}
}, [search, ellipsis]);
return (
<Flex direction='column' gap={4}>
<Text tag='label' size={200}>
{`Enter at least ${minSearchLength} characters to search:`}
<Input mt={2}>
<Input.Addon><SearchIcon /></Input.Addon>
<Input.Value value={search} onChange={setSearch} />
</Input>
</Text>
<Text ref={ref} ellipsis={ellipsis} w='300px' size={200}>
{text}
</Text>
</Flex>
);
};
export default Demo;
Precise ellipsis position
When using cropPosition: 'middle', you can position the ellipsis more precisely by defining how many characters should be visible after the ellipsis.
import { Text } from '@semcore/ui/typography';
import React from 'react';
const text = 'Intergalactic is a constantly developing system of UI components, guidelines and UX patterns.';
const Demo = () => {
return (
<Text
ellipsis:cropPosition='middle'
ellipsis:lastRequiredSymbols={5}
size={300}
w='300px'
>
{text}
</Text>
);
};
export default Demo;
Multiline paragraphs
You can truncate paragraphs of text with ellipsis using the maxLine property.
Note that maxLine can only be used with cropPosition: end, and the hint is automatically disabled in this case.
To enable hint for cropped multiline text, set some hintProps or just enable it via hintProps={true}.
import { Text } from '@semcore/ui/typography';
import React from 'react';
const text = 'Intergalactic is a constantly developing system of UI components, guidelines and UX patterns.';
const Demo = () => {
return (
<Text w={180} ellipsis:maxLine={3}>
{text}
</Text>
);
};
export default Demo;
Hint properties
You can customize the hint that appears on hover/focus by using the hintProps property.
import { Text } from '@semcore/ui/typography';
import React from 'react';
const text = 'Intergalactic is a constantly developing system of UI components, guidelines and UX patterns.';
const Demo = () => {
return (
<Text w={180} ellipsis hint:placement='bottom'>
{text}
</Text>
);
};
export default Demo;
FilterTrigger (dynamic children)
Due to performance considerations, we don't observe changes in the Text children property. So, if you have some dynamically changing children in the Text component, you should set observerChildrenMutations={true} in the ellipsis settings.
Alternatively, you can set a unique key on the Text, depending on the children content.
TIP
For some reason, only setting the key property works for FilterTrigger.Text right now.
import { FilterTrigger } from '@semcore/ui/base-trigger';
import Select from '@semcore/ui/select';
import React from 'react';
const Demo = () => {
const [material, setMaterial] = React.useState([]);
return (
<>
<Select onChange={setMaterial} multiselect>
<Select.Trigger
tag={FilterTrigger}
placeholder='Material'
aria-label='Material'
>
<FilterTrigger.Text
wMax={80}
ellipsis:cropPosition='middle'
key={material.length}
>
<span aria-hidden='true'>Material: </span>
{material.length === 1 ? material : `${material.length} selected`}
</FilterTrigger.Text>
</Select.Trigger>
<Select.Menu aria-label='Material'>
{materials.map((option, idx) => (
<Select.Option value={option} key={idx}>
<Select.Option.Checkbox />
{option}
</Select.Option>
))}
</Select.Menu>
</Select>
</>
);
};
const materials = ['Glass', 'Metal', 'Paper', 'Wood'];
export default Demo;