UNPKG

@visulima/string

Version:

Functions for manipulating strings.

97 lines (94 loc) 3.3 kB
import { getStringTruncatedWidth } from './get-string-truncated-width.mjs'; import { slice } from './slice.mjs'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); const DEFAULT_ELLIPSIS = "…"; const MAX_SPACE_SEARCH_DISTANCE = 3; const findNearestSpace = /* @__PURE__ */ __name((string_, startIndex, searchRight = false) => { if (string_.charAt(startIndex) === " ") { return startIndex; } const direction = searchRight ? 1 : -1; const limit = Math.min(MAX_SPACE_SEARCH_DISTANCE, searchRight ? string_.length - startIndex : startIndex); for (let offset = 1; offset <= limit; offset++) { const index = startIndex + offset * direction; if (string_.charAt(index) === " ") { return index; } } return startIndex; }, "findNearestSpace"); const truncate = /* @__PURE__ */ __name((input, limit, options = {}) => { if (typeof input !== "string") { throw new TypeError(`Expected \`input\` to be a string, got ${typeof input}`); } if (typeof limit !== "number") { throw new TypeError(`Expected \`limit\` to be a number, got ${typeof limit}`); } if (input === "" || limit <= 0) { return ""; } const { ellipsis = DEFAULT_ELLIPSIS, position = "end", preferTruncationOnSpace = false } = options; let ellipsisWidth = options.ellipsisWidth ?? ellipsis === DEFAULT_ELLIPSIS ? 1 : void 0; if (ellipsisWidth === void 0) { ellipsisWidth = getStringTruncatedWidth(ellipsis, { ...options.width, ellipsis: "", ellipsisWidth: 0, limit: Number.POSITIVE_INFINITY }).width; } if (limit === 1 && ellipsisWidth === 1) { return ellipsis; } if (limit === 1) { return ""; } const { width } = getStringTruncatedWidth(input, { ...options.width, ellipsis, ellipsisWidth }); if (width <= limit) { return input; } switch (position) { case "start": { if (preferTruncationOnSpace) { const nearestSpace = findNearestSpace(input, width - limit + 1, true); return ellipsis + slice(input, nearestSpace, width).trim(); } return ellipsis + slice(input, width - limit + ellipsisWidth, width, { width: options.width }); } case "middle": { const half = Math.floor(limit / 2); if (preferTruncationOnSpace) { const firstBreak = findNearestSpace(input, half); const secondBreak = findNearestSpace(input, width - (limit - half) + 1, true); return slice(input, 0, firstBreak) + ellipsis + slice(input, secondBreak, width).trim(); } return slice(input, 0, half, { width: options.width }) + ellipsis + slice(input, width - (limit - half) + ellipsisWidth, width, { width: options.width }); } case "end": { if (preferTruncationOnSpace) { const nearestSpace = findNearestSpace(input, limit - 1); return slice(input, 0, nearestSpace, { width: options.width }) + ellipsis; } return slice(input, 0, limit - ellipsisWidth, { width: options.width }) + ellipsis; } default: { throw new Error(`Invalid position: expected 'start', 'middle' or 'end', got '${position}'`); } } }, "truncate"); export { truncate };