All files / src/draw/parts box.ts

100% Statements 44/44
100% Branches 9/9
100% Functions 4/4
100% Lines 44/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 761x 1x     1x   1x 1x 1x               1x 1x 1x 1x 1x   1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 1x 11x 11x 11x 11x 11x 11x 11x 11x                               1x           1x 1x 1x 10x 10x 1x 1x 1x 1x 1x  
import {pipe, type EndoOf} from '#Function'
import {filterDefined} from '#Record'
import type {HorizontalAlignment} from '../align.js'
import type {BorderSet} from '../glyph.js'
import {borderSet} from '../glyph.js'
import type {Part} from '../part.js'
import {stackText, padPart} from './atoms.js'
import {addBorder} from './borders.js'
import {noPadding, type DirectedPad} from './pad.js'
 
export interface BoxSettings {
  padding: DirectedPad
  margin: DirectedPad
  border: BorderSet
}
 
const defaultSettings: BoxSettings = {
  padding: noPadding,
  margin: noPadding,
  border: borderSet('thin'),
}
 
const normalizeSettings = (
  partial: Partial<BoxSettings> = defaultSettings,
): BoxSettings => ({
  padding: {
    ...noPadding,
    ...filterDefined(partial.padding ?? {}),
  },
  margin: {
    ...noPadding,
    ...filterDefined(partial.margin ?? {}),
  },
  border: partial.border ?? borderSet('thin'),
})
const _box = (part: Part, rawSettings?: Partial<BoxSettings>): Part => {
  const {padding, margin, border} = normalizeSettings(rawSettings)
  return pipe(
    part,
    padPart(padding),
    addBorder.curried(border),
    padPart(margin),
  )
}
 
/**
 * Wrap a part in a configurable bordered box.
 *
 * At the `curried` key you can find a curried version that takes the settings
 * as first argument.
 *
 * At the `lines` key you will find a version that takes an array of text lines
 * instead of a part. They will be added to a column and the column will become
 * the boxed part.
 * @param part Part to be boxed.
 * @param settings Optional box settings.
 * @category drawing
 * @function
 */
export const box: {
  (part: Part, settings?: Partial<BoxSettings>): Part
  curried: (settings?: Partial<BoxSettings>) => EndoOf<Part>
  lines: (
    settings?: Partial<BoxSettings>,
  ) => (lines: string[], hAlign?: HorizontalAlignment) => Part
} = Object.assign(_box, {
  curried:
    (settings?: Partial<BoxSettings>): EndoOf<Part> =>
    part =>
      _box(part, settings),
  lines:
    (settings?: Partial<BoxSettings>) =>
    (lines: string[], hAlign: HorizontalAlignment = 'center'): Part =>
      _box(stackText(lines, hAlign), settings),
})