From c0818e35f5ad010a9c3e3a9f6d72e2c389d4b010 Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Mon, 18 Mar 2024 14:53:46 +0100 Subject: [PATCH] Final changes --- edit-context/html-editor/converter.js | 44 ++++++++++++------------ edit-context/html-editor/editor.js | 49 ++++++++++++++++----------- edit-context/html-editor/index.html | 3 ++ edit-context/html-editor/styles.css | 10 ------ 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/edit-context/html-editor/converter.js b/edit-context/html-editor/converter.js index 31cbc08b..4c0b844b 100644 --- a/edit-context/html-editor/converter.js +++ b/edit-context/html-editor/converter.js @@ -1,25 +1,3 @@ -// The EditContext object only knows about character offsets. But out editor -// view renders HTML tokens as DOM nodes. This function finds DOM node tokens -// that are in the provided EditContext offset range. -export function fromOffsetsToRenderedTokenNodes(renderedTokens, start, end) { - const tokenNodes = []; - - for (let offset = start; offset < end; offset++) { - const token = renderedTokens.find( - (token) => token.pos <= offset && token.pos + token.value.length > offset - ); - if (token) { - tokenNodes.push({ - node: token.node, - nodeOffset: token.pos, - charOffset: offset, - }); - } - } - - return tokenNodes; -} - // The EditContext object only knows about a plain text string and about // character offsets. However, our editor view renders the text by using // DOM nodes. So we sometimes need to convert between the two. @@ -94,3 +72,25 @@ export function fromOffsetsToSelection(start, end, editorEl) { return { anchorNode, anchorOffset, extentNode, extentOffset }; } + +// The EditContext object only knows about character offsets. But out editor +// view renders HTML tokens as DOM nodes. This function finds DOM node tokens +// that are in the provided EditContext offset range. +export function fromOffsetsToRenderedTokenNodes(renderedTokens, start, end) { + const tokenNodes = []; + + for (let offset = start; offset < end; offset++) { + const token = renderedTokens.find( + (token) => token.pos <= offset && token.pos + token.value.length > offset + ); + if (token) { + tokenNodes.push({ + node: token.node, + nodeOffset: token.pos, + charOffset: offset, + }); + } + } + + return tokenNodes; +} diff --git a/edit-context/html-editor/editor.js b/edit-context/html-editor/editor.js index 61d56b74..728b89ef 100644 --- a/edit-context/html-editor/editor.js +++ b/edit-context/html-editor/editor.js @@ -85,15 +85,16 @@ if (IS_CUSTOM_HIGHLIGHT_SUPPORTED) { // Tokenize the text. currentTokens = tokenizeHTML(text); - // Render each token. + // Render each token as a DOM node. for (const token of currentTokens) { const span = document.createElement("span"); span.classList.add(`token-${token.type}`); span.textContent = token.value; - span.dataset.tokenPos = token.pos; editorEl.appendChild(span); - // Store the node in the token so we can find it later. + // Store the new DOM node as a property of the token + // in the currentTokens array. We will need it again + // later in fromOffsetsToRenderedTokenNodes. token.node = span; } @@ -146,25 +147,35 @@ if (IS_CUSTOM_HIGHLIGHT_SUPPORTED) { const formats = e.getTextFormats(); for (const format of formats) { - const { rangeStart, rangeEnd, underlineStyle, underlineThickness } = - format; - - // Find the nodes in the view that are in the range. - const { anchorNode, anchorOffset, extentNode, extentOffset } = - fromOffsetsToSelection(rangeStart, rangeEnd, editorEl); - const highlight = - imeHighlights[ - `${format.underlineStyle.toLowerCase()}-${format.underlineThickness.toLowerCase()}` - ]; - if (highlight) { - const range = document.createRange(); - range.setStart(anchorNode, anchorOffset); - range.setEnd(extentNode, extentOffset); - highlight.add(range); - } + // Find the DOM selection that corresponds to the format's range. + const selection = fromOffsetsToSelection( + format.rangeStart, + format.rangeEnd, + editorEl + ); + + // Highlight the selection with the right style and thickness. + addHighlight(selection, format.underlineStyle, format.underlineThickness); } }); + function addHighlight(selection, underlineStyle, underlineThickness) { + // Get the right CSS custom Highlight object depending on the + // underline style and thickness. + const highlight = + imeHighlights[ + `${underlineStyle.toLowerCase()}-${underlineThickness.toLowerCase()}` + ]; + + if (highlight) { + // Add a range to the Highlight object. + const range = document.createRange(); + range.setStart(selection.anchorNode, selection.anchorOffset); + range.setEnd(selection.extentNode, selection.extentOffset); + highlight.add(range); + } + } + // Handle key presses that are not already handled by the EditContext. editorEl.addEventListener("keydown", (e) => { const start = Math.min( diff --git a/edit-context/html-editor/index.html b/edit-context/html-editor/index.html index b04070a6..9fc192a7 100644 --- a/edit-context/html-editor/index.html +++ b/edit-context/html-editor/index.html @@ -5,6 +5,8 @@ Edit Context API: HTML editor demo + + @@ -12,6 +14,7 @@
+ diff --git a/edit-context/html-editor/styles.css b/edit-context/html-editor/styles.css index 823f64be..b37ac938 100644 --- a/edit-context/html-editor/styles.css +++ b/edit-context/html-editor/styles.css @@ -35,11 +35,6 @@ body { box-shadow: 0 0 0 0.25rem red; } -[data-token-pos] { - margin: 0; - transition: margin 0.2s; -} - .token-openTagStart, .token-openTagEnd, .token-closeTagStart, @@ -129,8 +124,3 @@ body { ::highlight(ime-squiggle-thick) { text-decoration: underline wavy 2px; } - -.controls label { - display: flex; - align-items: center; -}