From 973a3bffecd6a76a222adf34e61aee3157867c99 Mon Sep 17 00:00:00 2001 From: WooJunKang <44981120+WooJunKang@users.noreply.github.com> Date: Sun, 10 Dec 2023 19:45:33 +0900 Subject: [PATCH] Enhance to Properly Handle Line Breaks Inside Quotes in CSV Pasting (#367) --- src/DataViewer.tsx | 10 +++++++++- src/Spreadsheet.css | 4 ++++ src/matrix.test.ts | 6 ++++++ src/matrix.ts | 16 +++++++++++++--- src/reducer.ts | 1 + src/util.ts | 4 ++++ 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/DataViewer.tsx b/src/DataViewer.tsx index 23673242..2a76bb37 100644 --- a/src/DataViewer.tsx +++ b/src/DataViewer.tsx @@ -1,5 +1,7 @@ import * as React from "react"; +import classNames from "classnames"; import * as Types from "./types"; +import { hasLineBreaker } from "./util"; export const TRUE_TEXT = "TRUE"; export const FALSE_TEXT = "FALSE"; @@ -16,7 +18,13 @@ const DataViewer = , Value>({ {convertBooleanToText(value)} ) : ( - {value} + + {value} + ); }; diff --git a/src/Spreadsheet.css b/src/Spreadsheet.css index 382ca671..c5906ddc 100755 --- a/src/Spreadsheet.css +++ b/src/Spreadsheet.css @@ -86,6 +86,10 @@ box-sizing: border-box; } +.Spreadsheet__data-viewer--preserve-breaks { + white-space: pre-wrap; +} + .Spreadsheet__data-editor, .Spreadsheet__data-editor input { width: 100%; diff --git a/src/matrix.test.ts b/src/matrix.test.ts index f3b352c0..53067339 100644 --- a/src/matrix.test.ts +++ b/src/matrix.test.ts @@ -64,6 +64,12 @@ describe("Matrix.split()", () => { test("Constructs a matrix from a CSV string", () => { expect(Matrix.split(CSV, Number)).toEqual(EXAMPLE_MATRIX); }); + + test("Keeps line breaks inside double quotes", () => { + const csv = '"Value\n1"\tValue2\t"Value\n3"'; + const result = Matrix.split(csv, (value) => value); + expect(result).toEqual([["Value\n1", "Value2", "Value\n3"]]); + }); }); describe("Matrix.set()", () => { diff --git a/src/matrix.ts b/src/matrix.ts index d79a40fe..e275104f 100644 --- a/src/matrix.ts +++ b/src/matrix.ts @@ -167,9 +167,19 @@ export function split( horizontalSeparator = "\t", verticalSeparator: string | RegExp = /\r\n|\n|\r/ ): Matrix { - return csv - .split(verticalSeparator) - .map((row) => row.split(horizontalSeparator).map(transform)); + // Temporarily replace line breaks inside quotes + const replaced = csv.replace(/"([^"]*?)"/g, (match, p1) => { + return p1.replace(/\n/g, "\\n"); + }); + return replaced.split(verticalSeparator).map((row) => + row + .split(horizontalSeparator) + .map((line) => { + // Restore original line breaks in each line + return line.replace(/\\n/g, "\n"); + }) + .map(transform) + ); } /** Returns whether the point exists in the matrix or not. */ diff --git a/src/reducer.ts b/src/reducer.ts index 2811d687..b9cc59c2 100644 --- a/src/reducer.ts +++ b/src/reducer.ts @@ -177,6 +177,7 @@ export default function reducer( if (!active) { return state; } + const copied = Matrix.split(text, (value) => ({ value })); const copiedSize = Matrix.getSize(copied); diff --git a/src/util.ts b/src/util.ts index 7c08bf2d..fa8c18c7 100644 --- a/src/util.ts +++ b/src/util.ts @@ -167,3 +167,7 @@ export function shouldHandleClipboardEvent( export function isFocusedWithin(element: Element): boolean { return element.matches(FOCUS_WITHIN_SELECTOR); } + +export function hasLineBreaker(value: unknown) { + return typeof value === "string" && value.includes("\n"); +}