Skip to content

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.

tsx
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.

tsx
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.

tsx
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.

tsx
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}.

tsx
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.

tsx
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.

tsx
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;

Released under the MIT License.

Released under the MIT License.