@poppinss/cliui
Version:
Opinionated UI KIT for Command Line apps
107 lines (106 loc) • 3.51 kB
JavaScript
import stringWidth from "string-width";
import cliTruncate from "cli-truncate";
import terminalSize from "terminal-size";
//#region src/utils.ts
function createWordWrapper(start, stop) {
const mode = "soft";
const re = mode === "hard" ? /\b/ : /(\S+\s+)/;
return function(text) {
return text.toString().split(re).reduce(function(acc, x) {
if (mode === "hard") for (let i = 0; i < x.length; i += stop - start) acc.push(x.slice(i, i + stop - start));
else acc.push(x);
return acc;
}, []).reduce(function(lines, rawChunk) {
if (rawChunk === "") return lines;
const chunk = rawChunk.replace(/\t/g, " ");
const i = lines.length - 1;
if (lines[i].length + chunk.length > stop) {
lines[i] = lines[i].replace(/\s+$/, "");
chunk.split(/\n/).forEach(function(c) {
lines.push(new Array(start + 1).join(" ") + c.replace(/^\s+/, ""));
});
} else if (chunk.match(/\n/)) {
const xs = chunk.split(/\n/);
lines[i] += xs.shift();
xs.forEach(function(c) {
lines.push(new Array(start + 1).join(" ") + c.replace(/^\s+/, ""));
});
} else lines[i] += chunk;
return lines;
}, [new Array(start + 1).join(" ")]).join("\n");
};
}
//#endregion
//#region src/helpers.ts
/**
* Total number of columns for the terminal
*/
const TERMINAL_SIZE = terminalSize().columns;
/**
* Applies padding to the left or the right of the string by repeating
* a given char.
*
* The method is not same as `padLeft` or `padRight` from JavaScript STD lib,
* since it repeats a char regardless of the max width.
*/
function applyPadding(value, options) {
if (options.paddingLeft) value = `${options.paddingChar.repeat(options.paddingLeft)}${value}`;
if (options.paddingRight) value = `${value}${options.paddingChar.repeat(options.paddingRight)}`;
return value;
}
/**
* Justify the columns to have the same width by filling
* the empty slots with a padding char.
*
* Optionally, the column can be aligned left or right.
*/
function justify(columns, options) {
const normalizedOptions = {
align: "left",
paddingChar: " ",
...options
};
return columns.map((column) => {
const columnWidth = stringWidth(column);
/**
* Column is already same or greater than the maxWidth
*/
if (columnWidth >= normalizedOptions.maxWidth) return column;
/**
* Fill empty space on the right
*/
if (normalizedOptions.align === "left") return applyPadding(column, {
paddingChar: normalizedOptions.paddingChar,
paddingRight: normalizedOptions.maxWidth - columnWidth
});
/**
* Fill empty space on the left
*/
return applyPadding(column, {
paddingChar: normalizedOptions.paddingChar,
paddingLeft: normalizedOptions.maxWidth - columnWidth
});
});
}
/**
* Wrap the text under the starting and the ending column.
* The first line will start at 1st column. However, from
* the 2nd line onwards, the columns before the start
* column are filled with white space.
*/
function wrap(columns, options) {
const wrapper = createWordWrapper(options.startColumn, options.endColumn);
if (options.trimStart) return columns.map((column) => wrapper(column).trimStart());
return columns.map((column) => wrapper(column));
}
/**
* Truncates the text after a certain width.
*/
function truncate(columns, options) {
return columns.map((column) => cliTruncate(column, options.maxWidth, {
truncationCharacter: options.truncationChar || "…",
position: options.position || "end"
}));
}
//#endregion
export { wrap as i, justify as n, truncate as r, TERMINAL_SIZE as t };