Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove newlines when truncating text #13

Merged
merged 6 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 25 additions & 28 deletions src/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ import {
RowProps,
TableOptions,
} from './types.js'
import {allKeysInCollection, getColumns, getHeadings, intersperse, maybeStripAnsi, sortData} from './utils.js'
import {
allKeysInCollection,
determineWidthOfWrappedText,
getColumns,
getHeadings,
intersperse,
maybeStripAnsi,
sortData,
} from './utils.js'

/**
* Determines the configured width based on the provided width value.
Expand Down Expand Up @@ -63,11 +71,6 @@ function determineWidthToUse<T>(columns: Column<T>[], configuredWidth: number):
return tableWidth < configuredWidth ? configuredWidth : tableWidth
}

function determineWidthOfWrappedText(text: string): number {
const lines = text.split('\n')
return lines.reduce((max, line) => Math.max(max, line.length), 0)
}

function determineTruncatePosition(overflow: Overflow): 'start' | 'middle' | 'end' {
switch (overflow) {
case 'truncate-middle': {
Expand All @@ -88,7 +91,7 @@ function determineTruncatePosition(overflow: Overflow): 'start' | 'middle' | 'en
}
}

function formatTextWithMargins({
export function formatTextWithMargins({
horizontalAlignment,
overflow,
padding,
Expand Down Expand Up @@ -141,34 +144,26 @@ function formatTextWithMargins({
const {marginLeft, marginRight} = calculateMargins(width - determineWidthOfWrappedText(stripAnsi(wrappedText)))

const lines = wrappedText.split('\n').map((line, idx) => {
const {marginLeft: lineSpecificLeftMargin} = calculateMargins(width - stripAnsi(line).length)
const {marginLeft: lineSpecificLeftMargin, marginRight: lineSpecificRightMargin} = calculateMargins(
width - stripAnsi(line).length,
)

if (horizontalAlignment === 'left') {
if (idx === 0) {
// if it's the first line, only add margin to the right side (The left margin will be applied later)
return `${line}${' '.repeat(marginRight)}`
}

// if left alignment, add the overall margin to the left side and right sides
return `${' '.repeat(marginLeft)}${line}${' '.repeat(marginRight)}`
return idx === 0
? `${line}${' '.repeat(lineSpecificRightMargin - marginRight)}`
: `${' '.repeat(marginLeft)}${line}${' '.repeat(lineSpecificRightMargin - marginRight)}`
}

if (horizontalAlignment === 'center') {
if (idx === 0) {
// if it's the first line, only add margin to the right side (The left margin will be applied later)
return `${line}${' '.repeat(marginRight)}`
}

// if center alignment, add line specific margin to the left side and the overall margin to the right side
return `${' '.repeat(lineSpecificLeftMargin)}${line}${' '.repeat(marginRight)}`
return idx === 0
? `${' '.repeat(lineSpecificLeftMargin - marginLeft)}${line}${' '.repeat(lineSpecificRightMargin)}`
: `${' '.repeat(lineSpecificLeftMargin)}${line}${' '.repeat(lineSpecificRightMargin - marginRight)}`
}

// right alignment
if (idx === 0) {
return `${' '.repeat(Math.max(0, lineSpecificLeftMargin - marginLeft))}${line}${' '.repeat(marginRight)}`
}

return `${' '.repeat(lineSpecificLeftMargin)}${line}${' '.repeat(marginRight)}`
return idx === 0
? `${' '.repeat(Math.max(0, lineSpecificLeftMargin - marginLeft))}${line}${' '.repeat(lineSpecificRightMargin - marginRight)}`
: `${' '.repeat(lineSpecificLeftMargin)}${line}${' '.repeat(lineSpecificRightMargin - marginRight)}`
})

return {
Expand All @@ -178,7 +173,9 @@ function formatTextWithMargins({
}
}

const text = cliTruncate(valueWithNoZeroWidthChars, spaceForText, {position: determineTruncatePosition(overflow)})
const text = cliTruncate(valueWithNoZeroWidthChars.replaceAll('\n', ' '), spaceForText, {
position: determineTruncatePosition(overflow),
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to create a simple test for this newline replacement so we don't have a regression later.

Also, unrelated: this uncovered an issue with align-center where the first line is not centered.

Wrap (aligned center)
┌───────┬─────────┬─────┬───────────────────────────────────────────────────────────────────────────┐
│  Id   │  Name   │ Age │                                Description                                │
├───────┼─────────┼─────┼───────────────────────────────────────────────────────────────────────────┤
│ 36329 │  Alice  │ 20  │  Lorem ipsum dolor sit amet, consectetur                                  │
│       │         │     │                                   adipi                                   │
│       │         │     │  scing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna   │
│       │         │     │                                  aliqua.                                  │
├───────┼─────────┼─────┼───────────────────────────────────────────────────────────────────────────┤
│ 49032 │   Bob   │ 21  │  Lorem ipsum dolor sit amet, consectetur                                  │
│       │         │     │                                   adipi                                   │
│       │         │     │  scing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna   │
│       │         │     │                                  aliqua.                                  │
├───────┼─────────┼─────┼───────────────────────────────────────────────────────────────────────────┤
│ 51786 │ Charlie │ 22  │  Lorem ipsum dolor sit amet, consectetur                                  │
│       │         │     │                                   adipi                                   │
│       │         │     │  scing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna   │
│       │         │     │                                  aliqua.                                  │
└───────┴─────────┴─────┴───────────────────────────────────────────────────────────────────────────┘

const spaces = width - stripAnsi(text).length
return {
text,
Expand Down
7 changes: 6 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export function allKeysInCollection<T extends Record<string, unknown>>(data: T[]
return [...keys]
}

export function determineWidthOfWrappedText(text: string): number {
const lines = text.split('\n')
return lines.reduce((max, line) => Math.max(max, line.length), 0)
}

export function getColumns<T extends Record<string, unknown>>(config: Config<T>, headings: Partial<T>): Column<T>[] {
const {columns, horizontalAlignment, maxWidth, overflow, verticalAlignment} = config

Expand All @@ -58,7 +63,7 @@ export function getColumns<T extends Record<string, unknown>>(config: Config<T>,
const value = data[key]

if (value === undefined || value === null) return 0
return stripAnsi(String(value).replaceAll('​', ' ')).length
return determineWidthOfWrappedText(stripAnsi(String(value).replaceAll('​', ' ')))
})

const header = String(headings[key]).length
Expand Down
Loading