UNPKG

@elastic/eui

Version:

Elastic UI Component Library

257 lines (254 loc) 12.2 kB
var _excluded = ["width", "onResize"], _excluded2 = ["width", "children", "text", "truncation", "truncationOffset", "truncationPosition", "ellipsis", "calculationDelayMs", "containerRef", "className"], _excluded3 = ["onResize"]; function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], t.indexOf(o) >= 0 || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.indexOf(n) >= 0) continue; t[n] = r[n]; } return t; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import React, { useState, useMemo, useCallback, useEffect } from 'react'; import PropTypes from "prop-types"; import classNames from 'classnames'; import { useCombinedRefs, useEuiMemoizedStyles } from '../../services'; import { EuiResizeObserver } from '../observer/resize_observer'; import { EuiToolTip } from '../tool_tip'; import { TruncationUtils } from './utils'; import { euiTextTruncateStyles } from './text_truncate.styles'; import { jsx as ___EmotionJSX } from "@emotion/react"; var TRUNCATION_TYPES = ['end', 'start', 'startEnd', 'middle']; export var EuiTextTruncate = function EuiTextTruncate(_ref) { var width = _ref.width, onResize = _ref.onResize, props = _objectWithoutProperties(_ref, _excluded); return width != null ? ___EmotionJSX(EuiTextTruncateWithWidth, _extends({ width: width }, props)) : ___EmotionJSX(EuiTextTruncateWithResizeObserver, _extends({ onResize: onResize }, props)); }; EuiTextTruncate.propTypes = { className: PropTypes.string, "aria-label": PropTypes.string, "data-test-subj": PropTypes.string, css: PropTypes.any, /** * The full text string to truncate */ text: PropTypes.string.isRequired, /** * The truncation type desired. Determines where the ellipses are placed. */ truncation: PropTypes.any, /** * This prop **only** applies to the `start` and `end` truncation types. * It allows preserving a certain number of characters of either the * starting or ending text. * * If the passed offset is greater than the total text length, * the offset will be ignored. */ truncationOffset: PropTypes.number, /** * This prop **only** applies to the `startEnd` truncation type. * It allows customizing the anchor position of the displayed text, * which otherwise defaults to the middle of the text string. * * The primary use case for this prop for is search highlighting - e.g., if * a user searches for a specific word in the text, pass the index of that * found word to ensure it is always visible. * * This behavior will intelligently detect when positions are close to the start * or end of the text, and omit leading or trailing ellipses when necessary. * If the passed position is greater than the total text length, * the truncation will simply default to `start` instead. */ truncationPosition: PropTypes.number, /** * Defaults to the horizontal ellipsis character. * Can be optionally configured to use other punctuation, * e.g. spaces, brackets, hyphens, asterisks, etc. */ ellipsis: PropTypes.string, /** * By default, EuiTextTruncate will render a resize observer to detect the * available width it has. For performance reasons (e.g. multiple truncated * text items within the same container), you may opt to pass in your own * container width, which will skip initializing a resize observer. */ width: PropTypes.number, /** * Optional callback that fires when the default resizer observer both mounts and * registers a size change. This callback will **not** fire if `width` is passed. */ onResize: PropTypes.func, /** * By default, EuiTextTruncate will render the truncated string directly. * You can optionally pass a render prop function to the component, which * allows for more flexible text rendering, e.g. adding custom markup * or highlighting */ children: PropTypes.func, /** * For some edge case scenarios, EuiTextTruncate's calculations may be off until * fonts are done loading or layout is done shifting or settling. Adding a delay * may help resolve any rendering issues. */ calculationDelayMs: PropTypes.number }; var EuiTextTruncateWithWidth = function EuiTextTruncateWithWidth(_ref2) { var width = _ref2.width, children = _ref2.children, text = _ref2.text, _ref2$truncation = _ref2.truncation, _truncation = _ref2$truncation === void 0 ? 'end' : _ref2$truncation, _ref2$truncationOffse = _ref2.truncationOffset, _truncationOffset = _ref2$truncationOffse === void 0 ? 0 : _ref2$truncationOffse, truncationPosition = _ref2.truncationPosition, _ref2$ellipsis = _ref2.ellipsis, ellipsis = _ref2$ellipsis === void 0 ? '…' : _ref2$ellipsis, calculationDelayMs = _ref2.calculationDelayMs, containerRef = _ref2.containerRef, className = _ref2.className, rest = _objectWithoutProperties(_ref2, _excluded2); // Note: This needs to be a state and not a ref to trigger a rerender on mount var _useState = useState(null), _useState2 = _slicedToArray(_useState, 2), containerEl = _useState2[0], setContainerEl = _useState2[1]; var refs = useCombinedRefs([setContainerEl, containerRef]); // If necessary, wait a tick on mount before truncating var _useState3 = useState(!calculationDelayMs), _useState4 = _slicedToArray(_useState3, 2), ready = _useState4[0], setReady = _useState4[1]; useEffect(function () { if (calculationDelayMs) { var timerId = setTimeout(function () { return setReady(true); }, calculationDelayMs); return function () { return clearTimeout(timerId); }; } }, [calculationDelayMs]); // Handle exceptions where we need to override the passed props var _useMemo = useMemo(function () { var truncation = _truncation; var truncationOffset = 0; if (_truncation === 'end' || _truncation === 'start') { if (0 < _truncationOffset && _truncationOffset < text.length) { truncationOffset = _truncationOffset; } } else if (_truncation === 'startEnd' && truncationPosition != null) { if (truncationPosition <= 0) { truncation = 'end'; } else if (truncationPosition >= text.length) { truncation = 'start'; } } return { truncation: truncation, truncationOffset: truncationOffset }; }, [_truncation, _truncationOffset, truncationPosition, text.length]), truncation = _useMemo.truncation, truncationOffset = _useMemo.truncationOffset; var truncatedText = useMemo(function () { var truncatedText = ''; if (!ready || !containerEl) return text; if (!width) return truncatedText; var utils = new TruncationUtils({ fullText: text, ellipsis: ellipsis, container: containerEl, availableWidth: width }); if (utils.checkIfTruncationIsNeeded() === false) { truncatedText = text; } else if (utils.checkSufficientEllipsisWidth(truncation) === false) { truncatedText = ''; } else { switch (truncation) { case 'end': truncatedText = utils.truncateEnd(truncationOffset); break; case 'start': truncatedText = utils.truncateStart(truncationOffset); break; case 'startEnd': if (truncationPosition == null) { truncatedText = utils.truncateStartEndAtMiddle(); } else { truncatedText = utils.truncateStartEndAtPosition(truncationPosition); } break; case 'middle': truncatedText = utils.truncateMiddle(); break; } } return truncatedText; }, [ready, width, text, truncation, truncationOffset, truncationPosition, ellipsis, containerEl]); var isTruncating = truncatedText !== text; var styles = useEuiMemoizedStyles(euiTextTruncateStyles); var content = ___EmotionJSX("div", _extends({ className: classNames('euiTextTruncate', className), css: styles.euiTextTruncate, ref: refs }, rest), isTruncating ? ___EmotionJSX(React.Fragment, null, ___EmotionJSX("span", { className: "euiTextTruncate__truncatedText", css: styles.euiTextTruncate__truncatedText, "aria-hidden": true, "data-test-subj": "truncatedText" }, children ? children(truncatedText) : truncatedText), ___EmotionJSX("span", { className: "euiTextTruncate__fullText", css: styles.euiTextTruncate__fullText, "data-test-subj": "fullText" }, text)) : ___EmotionJSX("span", { className: "euiTextTruncate__fullText", "data-test-subj": "fullText" }, children ? children(text) : text)); return isTruncating ? ___EmotionJSX(EuiToolTip, { content: text, disableScreenReaderOutput: true, display: "block" }, content) : content; }; EuiTextTruncateWithWidth.propTypes = { width: PropTypes.number.isRequired, containerRef: PropTypes.any }; var EuiTextTruncateWithResizeObserver = function EuiTextTruncateWithResizeObserver(_ref3) { var _onResize = _ref3.onResize, props = _objectWithoutProperties(_ref3, _excluded3); var _useState5 = useState(0), _useState6 = _slicedToArray(_useState5, 2), width = _useState6[0], setWidth = _useState6[1]; var onResize = useCallback(function (_ref4) { var width = _ref4.width; setWidth(width); _onResize === null || _onResize === void 0 || _onResize(width); }, [_onResize]); return ___EmotionJSX(EuiResizeObserver, { onResize: onResize }, function (ref) { return ___EmotionJSX(EuiTextTruncateWithWidth, _extends({ width: width, containerRef: ref }, props, { "data-resize-observer": "true" })); }); };