diff --git a/package.json b/package.json index 83118065..0cab5c41 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "component" ], "dependencies": { - "object-hash": "^2.0.3" + "object-hash": "^2.0.3", + "string-width": "^4.2.3" }, "devDependencies": { "@types/jest": "26.0.24", diff --git a/src/index.tsx b/src/index.tsx index 3284b2b8..c94f2c4f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react' import { Box, Text } from 'ink' import { sha1 } from 'object-hash' +import stringWidth from 'string-width' /* Table */ @@ -86,13 +87,13 @@ export default class Table extends React.Component< const { columns, padding } = this.getConfig() const widths: Column[] = columns.map((key) => { - const header = String(key).length + const header = stringWidth(String(key)) /* Get the width of each cell in the column */ const data = this.props.data.map((data) => { const value = data[key] if (value == undefined || value == null) return 0 - return String(value).length + return stringWidth(String(value)) }) const width = Math.max(...data, header) + padding * 2 @@ -314,7 +315,7 @@ function row( // margins const ml = config.padding - const mr = column.width - String(value).length - config.padding + const mr = column.width - stringWidth(String(value)) - config.padding return ( /* prettier-ignore */ diff --git a/tests/index.test.tsx b/tests/index.test.tsx index a88a1c2f..6f1b5da0 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -2,6 +2,8 @@ import React from 'react' import { Box, Text } from 'ink' import { render } from 'ink-testing-library' +import stringWidth from 'string-width'; + import Table, { Header, Skeleton, Cell } from '../src' // Helpers ------------------------------------------------------------------- @@ -485,4 +487,84 @@ test('Renders table with custom skeleton.', () => { expect(actual()).toBe(expected()) }) +test('Renders table with wide characters.', () => { + const data = [ + { name: '全角', width: 4 }, + { name: 'ハンカク', width: 4 }, + { name: '😀', width: 2 }, + ] + + const { lastFrame: actual } = render() + + const { lastFrame: expected } = render( + <> + + {custom('┌')} + {custom('──────')} + {custom('┬')} + {custom('───────')} + {custom('┐')} + + + {custom('│')} + {header(' name ')} + {custom('│')} + {header(' width ')} + {custom('│')} + + + {custom('├')} + {custom('──────')} + {custom('┼')} + {custom('───────')} + {custom('┤')} + + + {custom('│')} + {cell(' 全角 ')} + {custom('│')} + {cell(' 4 ')} + {custom('│')} + + + {custom('├')} + {custom('──────')} + {custom('┼')} + {custom('───────')} + {custom('┤')} + + + {custom('│')} + {cell(' ハンカク ')} + {custom('│')} + {cell(' 4 ')} + {custom('│')} + + + {custom('├')} + {custom('──────')} + {custom('┼')} + {custom('───────')} + {custom('┤')} + + + {custom('│')} + {cell(' 😀 ')} + {custom('│')} + {cell(' 2 ')} + {custom('│')} + + + {custom('└')} + {custom('──────')} + {custom('┴')} + {custom('───────')} + {custom('┘')} + + , + ) + + expect(actual()).toBe(expected()) +}) + // --------------------------------------------------------------------------- diff --git a/yarn.lock b/yarn.lock index d4939a70..7c75a0ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -675,6 +675,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -3667,6 +3672,15 @@ string-width@^4.2.2: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -3674,6 +3688,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"