Skip to content

Commit

Permalink
Implements missing features of the Codeblock component in NeetoWebsit…
Browse files Browse the repository at this point in the history
…e custom codeblock component. (#1278)

* Updated the codeblock highlight script to inject a copy button to the codeblock.

* Updated the codeblock component to render line numbers along with the line.

* Cleanedup the codeblock line and code spacing.

* Fixed the codeblock usability issues in the editor.

* Fixed code block line higlighting not working in the bigbinary website.

* Fixed missing line numbers when there was no line highlighting in the EditorContent component.

---------

Co-authored-by: Praveen Murali <[email protected]>
  • Loading branch information
deepakjosp and praveen-murali-ind authored Nov 29, 2024
1 parent f60cfb4 commit e501e7b
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 45 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bigbinary/neeto-editor",
"version": "1.43.26",
"version": "1.43.24",
"types": "./types.d.ts",
"description": "neetoEditor is the library that drives the rich text experience in all neeto products built at BigBinary",
"keywords": [
Expand Down
5 changes: 3 additions & 2 deletions src/components/Editor/CustomExtensions/CodeBlock/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function createLineDecoration(from, lineNumber) {

// Set the height of the highlight to match the line height
const lineElement = view.domAtPos(from).node;
if (lineElement) {
if (lineElement instanceof Element) {
const lineHeight = window.getComputedStyle(lineElement).lineHeight;
line.style.height = lineHeight;
}
Expand Down Expand Up @@ -46,7 +46,8 @@ const codeBlockHighlightPlugin = new Plugin({
if (tr.getMeta(codeBlockHighlightKey)) {
const decorations = [];
tr.doc.descendants((node, pos) => {
if (!(node.type.name === "codeBlock")) return;
if (node.type.name !== "codeBlock") return;

const highlightedLines = node.attrs.highlightedLines || [];
const lineRanges = getLineRanges(node, pos);

Expand Down
80 changes: 65 additions & 15 deletions src/components/EditorContent/codeBlockHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,72 @@ import hljs from "highlight.js/lib/common";
const highlightLinesScriptCDNPath =
"//cdn.jsdelivr.net/gh/TRSasasusu/[email protected]/highlightjs-highlight-lines.min.js";

function applyLineHighlighting(codeElement) {
hljs.highlightElement(codeElement);
const preElement = codeElement.closest("pre");
const getHighlightedLines = (preNode, codeNode) => {
const highlightedLines =
preNode?.getAttribute("data-highlighted-lines") ??
codeNode?.getAttribute("data-highlighted-lines");

return highlightedLines?.split(",")?.map(Number) ?? [0];
};

const copyToClipboard = text => {
try {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text);
} else {
const textArea = document.createElement("textarea");
textArea.value = text;

const highlightedLines = preElement.getAttribute("data-highlighted-lines");
if (highlightedLines) {
const linesToHighlight = highlightedLines.split(",").map(Number);
const highlightLinesOptions = linesToHighlight
.filter(line => line > 0)
.map(line => ({
start: line,
end: line,
color: "rgba(255, 255, 0, 0.2)",
}));
hljs.highlightLinesElement(codeElement, highlightLinesOptions);
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";

document.body.appendChild(textArea);
textArea.focus();
textArea.select();

document.execCommand("copy");
document.body.removeChild(textArea);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};

const addCopyToClipboardButton = codeElement => {
const copyButton = document.createElement("button");
copyButton.setAttribute(
"class",
"text-primary-grey absolute right-3 top-3 text-xs copy-button"
);
copyButton.innerText = "Copy";

copyButton.addEventListener("click", () => {
copyToClipboard(codeElement.textContent?.trim());
copyButton.innerText = "Copied";
setTimeout(() => {
copyButton.innerText = "Copy";
}, 2500);
});

codeElement.parentNode.appendChild(copyButton);
};

function applyCodeblockDecorations(codeElement) {
const preElement = codeElement.closest("pre");
hljs.highlightElement(codeElement);
addCopyToClipboardButton(codeElement);

const linesToHighlight = getHighlightedLines(preElement, codeElement);
const highlightLinesOptions = linesToHighlight
.filter(line => line > 0)
.map(line => ({
start: line,
end: line,
color: "rgba(255, 255, 0, 0.2)",
}));
hljs.highlightLinesElement(codeElement, highlightLinesOptions);
}

document.addEventListener("DOMContentLoaded", () => {
Expand All @@ -28,6 +78,6 @@ document.addEventListener("DOMContentLoaded", () => {
script.async = true;
document.head.appendChild(script);
script.addEventListener("load", () => {
document.querySelectorAll("pre code").forEach(applyLineHighlighting);
document.querySelectorAll("pre code").forEach(applyCodeblockDecorations);
});
});
9 changes: 7 additions & 2 deletions src/components/EditorContent/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ const buildReactElementFromAST = element => {
return element.value;
};

const highlightLinesCode = (code, options) => {
code.innerHTML = code.innerHTML.replace(
const splitCodeBlockIntoLines = codeBlock => {
codeBlock.innerHTML = codeBlock.innerHTML.replace(
/([ \S]*\n|[ \S]*$)/gm,
match => `<div class="highlight-line">${match}</div>`
);
};

const highlightLinesCode = (code, options) => {
if (options === undefined) {
return;
}
Expand Down Expand Up @@ -93,9 +95,12 @@ export const applyLineHighlighting = editorContent => {
const codeTags = editorContent?.querySelectorAll("pre code");

codeTags.forEach(codeTag => {
splitCodeBlockIntoLines(codeTag);

const highlightedLines = codeTag
.closest("pre")
.getAttribute("data-highlighted-lines");

if (highlightedLines) {
const linesToHighlight = highlightedLines
.split(",")
Expand Down
11 changes: 10 additions & 1 deletion src/styles/editor/_codeblock.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
.ne-codeblock-nodeview-wrapper {
pre {
padding-left: 0.75rem;
padding-right: 0.75rem;

&::before {
content: none;
}
}

.highlighted-line {
background-color: rgba(255, 255, 0, 0.2);
position: absolute;
Expand All @@ -7,4 +16,4 @@
z-index: 0;
pointer-events: none;
}
}
}
84 changes: 60 additions & 24 deletions src/styles/editor/editor-content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,14 @@
--neeto-editor-content-list-item-margin-top: 0.5rem;
--neeto-editor-content-code-font-size: 85%;
--neeto-editor-content-code-background-color: 246, 248, 250, 1;
--neeto-editor-content-code-line-number-color: 89, 99, 110, 1;
--neeto-editor-content-code-color: 31, 35, 40, 1;
--neeto-editor-content-code-padding: 0.2em 0.4em;
--neeto-editor-content-blockquote-color: 89, 99, 110, 1;
--neeto-editor-content-blockquote-border-color: 209, 217, 224, 1;
--neeto-editor-content-blockquote-border-left-width: 4px;
--neeto-editor-content-blockquote-margin-bottom: 1rem;
--neeto-editor-content-blockquote-padding-left: 12px;
--neeto-editor-content-blockquote-padding-left: 0.75rem;
}

.neeto-editor-content {
Expand Down Expand Up @@ -166,9 +167,7 @@
font-weight: var(--neeto-editor-content-heading-font-weight);

strong {
font-weight: var(
--neeto-editor-content-heading-font-weight-bold
) !important;
font-weight: var(--neeto-editor-content-heading-font-weight-bold) !important;
}
}

Expand Down Expand Up @@ -218,9 +217,9 @@

.header-link-icon-wrapper {
position: absolute;
left: -25px;
left: -1.5625rem;
height: 100%;
width: 25px;
width: 1.5625rem;
display: flex;
align-items: center;
opacity: 0;
Expand All @@ -233,6 +232,7 @@
}

&:hover {

h1,
h2,
h3,
Expand Down Expand Up @@ -312,13 +312,14 @@
color: rgba(var(--neeto-editor-content-code-color));
background-color: rgba(var(--neeto-editor-content-code-background-color));
border-color: rgba(var(--neeto-editor-content-code-background-color));
overflow-x: auto;
font-size: var(--neeto-editor-content-code-font-size);
max-width: 100%;
font-size: var(--neeto-editor-content-paragraph-font-size);
line-height: 1.5;
margin-top: 1rem;
margin-bottom: 1rem;
border-radius: var(--neeto-editor-rounded-sm);
padding: 12px;
padding: 0;
position: relative;

div {
color: rgba(var(--neeto-editor-content-code-color));
Expand All @@ -327,44 +328,79 @@
.neeto-editor-codeblock-options {
display: flex;
position: absolute;
gap: 4px;
gap: 0.25rem;
align-items: center;
top: 4px;
right: 4px;
top: 0.25rem;
right: 0.25rem;

&__menu {
display: flex;
flex-direction: column;
gap: 4px;
gap: 0.25rem;
}

&__input {
margin: 4px 4px;
margin: 0.25rem 0.25rem;
}
}

counter-reset: line-number;

.highlight-line {
position: relative;
padding-left: 3.125rem;

&::before {
color: rgba(var(--neeto-editor-content-code-line-number-color));
content: counter(line-number);
counter-increment: line-number;
font-size: 85%;
font-weight: 400;
position: absolute;
left: 0;
padding-left: 0.625rem;
padding-right: 1rem;
width: 3.125rem;
height: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}

&:last-child::before {
content: "";
}
}

.copy-button {
position: absolute;
top: 10px;
right: 10px;
}
}

pre > code {
pre>code {
font-size: var(--neeto-editor-content-code-font-size);
background-color: transparent;
border-width: 0;
border-radius: 0;
padding: 0;
font-weight: 400;
color: inherit;
font-size: inherit;
line-height: inherit;
white-space: pre-wrap;
white-space: pre !important;
display: block;
width: 100%;
overflow-x: auto;
padding: 2rem 0.75rem 0.75rem 0px;
}

// Blockquote
blockquote {
font-weight: var(--neeto-editor-font-medium);
color: rgba(var(--neeto-editor-content-blockquote-color));
border-left-width: var(--neeto-editor-content-blockquote-border-left-width);
border-left-color: rgba(
var(--neeto-editor-content-blockquote-border-color)
);
quotes: "\201C""\201D""\2018""\2019";
border-left-color: rgba(var(--neeto-editor-content-blockquote-border-color));
quotes: "\201C" "\201D" "\2018" "\2019";
margin-bottom: var(--neeto-editor-content-blockquote-margin-bottom);
padding-left: var(--neeto-editor-content-blockquote-padding-left);

Expand All @@ -373,11 +409,11 @@
color: inherit;
}

& > p::before {
&>p::before {
content: "" !important;
}

& > p::after {
&>p::after {
content: "" !important;
}
}
Expand Down

0 comments on commit e501e7b

Please sign in to comment.