index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. 'use strict';
  2. const stringWidth = require('string-width');
  3. const chalk = require('chalk');
  4. const widestLine = require('widest-line');
  5. const cliBoxes = require('cli-boxes');
  6. const camelCase = require('camelcase');
  7. const ansiAlign = require('ansi-align');
  8. const termSize = require('term-size');
  9. const getObject = detail => {
  10. let object;
  11. if (typeof detail === 'number') {
  12. object = {
  13. top: detail,
  14. right: detail * 3,
  15. bottom: detail,
  16. left: detail * 3
  17. };
  18. } else {
  19. object = {
  20. top: 0,
  21. right: 0,
  22. bottom: 0,
  23. left: 0,
  24. ...detail
  25. };
  26. }
  27. return object;
  28. };
  29. const getBorderChars = borderStyle => {
  30. const sides = [
  31. 'topLeft',
  32. 'topRight',
  33. 'bottomRight',
  34. 'bottomLeft',
  35. 'vertical',
  36. 'horizontal'
  37. ];
  38. let chararacters;
  39. if (typeof borderStyle === 'string') {
  40. chararacters = cliBoxes[borderStyle];
  41. if (!chararacters) {
  42. throw new TypeError(`Invalid border style: ${borderStyle}`);
  43. }
  44. } else {
  45. for (const side of sides) {
  46. if (!borderStyle[side] || typeof borderStyle[side] !== 'string') {
  47. throw new TypeError(`Invalid border style: ${side}`);
  48. }
  49. }
  50. chararacters = borderStyle;
  51. }
  52. return chararacters;
  53. };
  54. const isHex = color => color.match(/^#[0-f]{3}(?:[0-f]{3})?$/i);
  55. const isColorValid = color => typeof color === 'string' && ((chalk[color]) || isHex(color));
  56. const getColorFn = color => isHex(color) ? chalk.hex(color) : chalk[color];
  57. const getBGColorFn = color => isHex(color) ? chalk.bgHex(color) : chalk[camelCase(['bg', color])];
  58. module.exports = (text, options) => {
  59. options = {
  60. padding: 0,
  61. borderStyle: 'single',
  62. dimBorder: false,
  63. align: 'left',
  64. float: 'left',
  65. ...options
  66. };
  67. if (options.borderColor && !isColorValid(options.borderColor)) {
  68. throw new Error(`${options.borderColor} is not a valid borderColor`);
  69. }
  70. if (options.backgroundColor && !isColorValid(options.backgroundColor)) {
  71. throw new Error(`${options.backgroundColor} is not a valid backgroundColor`);
  72. }
  73. const chars = getBorderChars(options.borderStyle);
  74. const padding = getObject(options.padding);
  75. const margin = getObject(options.margin);
  76. const colorizeBorder = border => {
  77. const newBorder = options.borderColor ? getColorFn(options.borderColor)(border) : border;
  78. return options.dimBorder ? chalk.dim(newBorder) : newBorder;
  79. };
  80. const colorizeContent = content => options.backgroundColor ? getBGColorFn(options.backgroundColor)(content) : content;
  81. text = ansiAlign(text, {align: options.align});
  82. const NL = '\n';
  83. const PAD = ' ';
  84. let lines = text.split(NL);
  85. if (padding.top > 0) {
  86. lines = new Array(padding.top).fill('').concat(lines);
  87. }
  88. if (padding.bottom > 0) {
  89. lines = lines.concat(new Array(padding.bottom).fill(''));
  90. }
  91. const contentWidth = widestLine(text) + padding.left + padding.right;
  92. const paddingLeft = PAD.repeat(padding.left);
  93. const {columns} = termSize();
  94. let marginLeft = PAD.repeat(margin.left);
  95. if (options.float === 'center') {
  96. const padWidth = Math.max((columns - contentWidth) / 2, 0);
  97. marginLeft = PAD.repeat(padWidth);
  98. } else if (options.float === 'right') {
  99. const padWidth = Math.max(columns - contentWidth - margin.right - 2, 0);
  100. marginLeft = PAD.repeat(padWidth);
  101. }
  102. const horizontal = chars.horizontal.repeat(contentWidth);
  103. const top = colorizeBorder(NL.repeat(margin.top) + marginLeft + chars.topLeft + horizontal + chars.topRight);
  104. const bottom = colorizeBorder(marginLeft + chars.bottomLeft + horizontal + chars.bottomRight + NL.repeat(margin.bottom));
  105. const side = colorizeBorder(chars.vertical);
  106. const middle = lines.map(line => {
  107. const paddingRight = PAD.repeat(contentWidth - stringWidth(line) - padding.left);
  108. return marginLeft + side + colorizeContent(paddingLeft + line + paddingRight) + side;
  109. }).join(NL);
  110. return top + NL + middle + NL + bottom;
  111. };
  112. module.exports._borderStyles = cliBoxes;