@@ -221,6 +221,66 @@ const boxContent = (content, contentWidth, options) => {
221221 return top + LINE_SEPARATOR + middle + LINE_SEPARATOR + bottom ;
222222} ;
223223
224+ const determineDimensions = ( text , options ) => {
225+ const widthOverride = options . width !== undefined ;
226+ const columns = terminalColumns ( ) ;
227+ const maxWidth = columns - options . margin . left - options . margin . right - BORDERS_WIDTH ;
228+
229+ // If width is provided, make sure it's not below 1
230+ if ( options . width ) {
231+ options . width = Math . max ( 1 , options . width - BORDERS_WIDTH ) ;
232+ }
233+
234+ const widest = widestLine ( wrapAnsi ( text , columns - BORDERS_WIDTH , { hard : true , trim : false } ) ) + options . padding . left + options . padding . right ;
235+
236+ // If title and width are provided, title adheres to fixed width
237+ if ( options . title && widthOverride ) {
238+ options . title = options . title . slice ( 0 , Math . max ( 0 , options . width - 2 ) ) ;
239+ if ( options . title ) {
240+ options . title = ` ${ options . title } ` ;
241+ }
242+ } else if ( options . title ) {
243+ options . title = options . title . slice ( 0 , Math . max ( 0 , maxWidth - 2 ) ) ;
244+
245+ // Recheck if title isn't empty now
246+ if ( options . title ) {
247+ options . title = ` ${ options . title } ` ;
248+ // If the title is larger than content, box adheres to title width
249+ if ( stringWidth ( options . title ) > widest ) {
250+ options . width = stringWidth ( options . title ) ;
251+ }
252+ }
253+ }
254+
255+ // If fixed width is provided, use it or content width as reference
256+ options . width = options . width ? options . width : widest ;
257+
258+ if ( ! widthOverride ) {
259+ if ( ( options . margin . left && options . margin . right ) && options . width > maxWidth ) {
260+ // Let's assume we have margins: left = 3, right = 5, in total = 8
261+ const spaceForMargins = columns - options . width - BORDERS_WIDTH ;
262+ // Let's assume we have space = 4
263+ const multiplier = spaceForMargins / ( options . margin . left + options . margin . right ) ;
264+ // Here: multiplier = 4/8 = 0.5
265+ options . margin . left = Math . max ( 0 , Math . floor ( options . margin . left * multiplier ) ) ;
266+ options . margin . right = Math . max ( 0 , Math . floor ( options . margin . right * multiplier ) ) ;
267+ // Left: 3 * 0.5 = 1.5 -> 1
268+ // Right: 6 * 0.5 = 3
269+ }
270+
271+ // Re-cap width considering the margins after shrinking
272+ options . width = Math . min ( options . width , columns - BORDERS_WIDTH - options . margin . left - options . margin . right ) ;
273+ }
274+
275+ // Prevent padding overflow
276+ if ( options . width - ( options . padding . left + options . padding . right ) <= 0 ) {
277+ options . padding . left = 0 ;
278+ options . padding . right = 0 ;
279+ }
280+
281+ return options ;
282+ } ;
283+
224284const isHex = color => color . match ( / ^ # (?: [ 0 - f ] { 3 } ) { 1 , 2 } $ / i) ;
225285const isColorValid = color => typeof color === 'string' && ( ( chalk [ color ] ) || isHex ( color ) ) ;
226286const getColorFn = color => isHex ( color ) ? chalk . hex ( color ) : chalk [ color ] ;
@@ -253,39 +313,11 @@ export default function boxen(text, options) {
253313 options . padding = getObject ( options . padding ) ;
254314 options . margin = getObject ( options . margin ) ;
255315
256- const columns = terminalColumns ( ) ;
257-
258- let contentWidth = widestLine ( wrapAnsi ( text , columns - BORDERS_WIDTH , { hard : true , trim : false } ) ) + options . padding . left + options . padding . right ;
259-
260- // This prevents the title bar to exceed the console's width
261- options . title = options . title && options . title . slice ( 0 , columns - 4 - options . margin . left - options . margin . right ) ;
262-
263- if ( options . title ) {
264- options . title = ` ${ options . title } ` ;
265- // Make the box larger to fit a larger title
266- if ( stringWidth ( options . title ) > contentWidth ) {
267- contentWidth = stringWidth ( options . title ) ;
268- }
269- }
270-
271- if ( ( options . margin . left && options . margin . right ) && contentWidth + BORDERS_WIDTH + options . margin . left + options . margin . right > columns ) {
272- // Let's assume we have margins: left = 3, right = 5, in total = 8
273- const spaceForMargins = columns - contentWidth - BORDERS_WIDTH ;
274- // Let's assume we have space = 4
275- const multiplier = spaceForMargins / ( options . margin . left + options . margin . right ) ;
276- // Here: multiplier = 4/8 = 0.5
277- options . margin . left = Math . max ( 0 , Math . floor ( options . margin . left * multiplier ) ) ;
278- options . margin . right = Math . max ( 0 , Math . floor ( options . margin . right * multiplier ) ) ;
279- // Left: 3 * 0.5 = 1.5 -> 1
280- // Right: 6 * 0.5 = 3
281- }
282-
283- // Prevent content from exceeding the console's width
284- contentWidth = Math . min ( contentWidth , columns - BORDERS_WIDTH - options . margin . left - options . margin . right ) ;
316+ options = determineDimensions ( text , options ) ;
285317
286- text = makeContentText ( text , options . padding , contentWidth , options . textAlignment ) ;
318+ text = makeContentText ( text , options . padding , options . width , options . textAlignment ) ;
287319
288- return boxContent ( text , contentWidth , options ) ;
320+ return boxContent ( text , options . width , options ) ;
289321}
290322
291323export const _borderStyles = cliBoxes ;
0 commit comments