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");
+}
|