From bbf8ab0966498c1636fc73f40bdf85bd87afe5dc Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Mon, 22 Jan 2024 21:59:59 -0500 Subject: [PATCH] Highlighting text. --- src/Editor.css | 5 ++++ src/Editor.tsx | 17 ++++++++----- src/shadow.tsx | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/shadow.tsx diff --git a/src/Editor.css b/src/Editor.css index c0e6015..3a2f1a6 100644 --- a/src/Editor.css +++ b/src/Editor.css @@ -49,4 +49,9 @@ height: 100%; pointer-events: none; color: transparent; + white-space: pre-wrap; + + .highlighted { + background: #ffff00; + } } diff --git a/src/Editor.tsx b/src/Editor.tsx index f7cc5ee..b61c695 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -1,6 +1,7 @@ -import React, { useState } from "react"; +import React, { ReactNode, useState } from "react"; import "./Editor.css"; import { Highlight } from "./highlight"; +import { buildShadow } from "./shadow"; function Editor({ defaultValue = "I have a text value." }) { function handleChange(event: React.ChangeEvent) { @@ -13,17 +14,19 @@ function Editor({ defaultValue = "I have a text value." }) { const [highlights, setHighlights] = useState>([]); function addHighlight(start: number, end: number) { - setHighlights([...highlights, new Highlight(start, end)]); + let new_highlights = [...highlights, new Highlight(start, end)]; + new_highlights.sort(function (a,b) { + if (a.start < b.start) return -1; + if (a.start > b.start) return 1; + return 0; + }); + setHighlights(new_highlights); } function clearHighlights() { setHighlights([]); } - function buildShadow() { - // foo - } - if (highlights.length === 0) { addHighlight(1, 5); } @@ -36,7 +39,7 @@ function Editor({ defaultValue = "I have a text value." }) { className="Editor-textarea" value={value} /> -
{value}
+
{buildShadow(highlights, value)}
diff --git a/src/shadow.tsx b/src/shadow.tsx new file mode 100644 index 0000000..acc1e3d --- /dev/null +++ b/src/shadow.tsx @@ -0,0 +1,69 @@ +import React, { ReactNode, useState } from "react"; +import { Highlight } from "./highlight"; + +function buildShadow(highlights: Highlight[], text: string): ReactNode[] { + let remaining_highlights = highlights.slice(); + let output: ReactNode[] = []; + let i = 0; + let state = ShadowState.Text; + let buffer = ""; + for (let chr of text) { + if (state == ShadowState.Text) { + if (remaining_highlights.length > 0 && i == remaining_highlights[0].start) { + // Start a span + output.push(buffer); + buffer = chr; + state = ShadowState.Highlight; + } else { + // Add a character + buffer += chr; + } + } else if (state == ShadowState.Highlight) { + if (remaining_highlights.length > 0 && i == remaining_highlights[0].end) { + // End the span + output.push({buffer}); + buffer = chr; + state = ShadowState.Text; + remaining_highlights = remaining_highlights.slice(1); + } else { + // Add a character + buffer += chr; + } + } + ++i; + } + + if (buffer.length > 0) { + if (state == ShadowState.Text) { + output.push(buffer); + } else if (state == ShadowState.Highlight) { + output.push({buffer}); + } + } + return output; +} + +function unicodeAwareSlice(text: string, start: number, end: number): string { + // Boooo javascript + let i = 0; + let output = ""; + for (let chr of text) { + if (i >= end) { + break; + } + if (i >= start) { + output += chr; + } + ++i; + } + return output; +} + +const enum ShadowState { + Text, + Highlight, +} + +export { +buildShadow +}