Skip to content

Commit

Permalink
editor: convert google docs checklists on paste
Browse files Browse the repository at this point in the history
  • Loading branch information
alihamuh authored and thecodrr committed Mar 5, 2024
1 parent 7d57055 commit 78fafdc
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
13 changes: 13 additions & 0 deletions packages/editor/src/extensions/clipboard/clipboard-dom-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class ClipboardDOMParser extends ProsemirrorDOMParser {

parseSlice(dom: Node, options?: ParseOptions | undefined): Slice {
if (dom instanceof HTMLElement || dom instanceof Document) {
convertGoogleDocsChecklist(dom);
formatCodeblocks(dom);
convertBrToSingleSpacedParagraphs(dom);
}
Expand Down Expand Up @@ -121,6 +122,18 @@ export function convertBrToSingleSpacedParagraphs(dom: HTMLElement | Document) {
}
}

export function convertGoogleDocsChecklist(dom: HTMLElement | Document) {
for (const li of dom.querySelectorAll(`ul li[role="checkbox"]`)) {
if (!li.parentElement?.classList.contains("checklist"))
li.parentElement!.classList.add("checklist");
li.className = "checklist--item";
if (li.firstElementChild?.tagName === "IMG") li.firstElementChild.remove();
if (li.getAttribute("aria-checked") === "true") {
li.classList.add("checked");
}
}
}

function splitOn(bound: Element, cutElement: Element) {
let grandparent: ParentNode | null = null;
for (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ exports[`convert br tags to paragraphs 12`] = `
</div>"
`;
exports[`convert google docs checklist 1`] = `
"<div>
<!--StartFragment--><meta charset=\\"utf-8\\"><ul id=\\"docs-internal-guid-0d9a5db3-7fff-ab55-e7ca-b178e1031970\\" class=\\"checklist\\"><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"1\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Adsjkfhasdf</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"1\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Asdfsadf</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"true\\" aria-level=\\"1\\" class=\\"checklist--item checked\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Asdfsda</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"true\\" aria-level=\\"1\\" class=\\"checklist--item checked\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Fasd</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"true\\" aria-level=\\"1\\" class=\\"checklist--item checked\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Fasd</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"1\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>F</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"true\\" aria-level=\\"1\\" class=\\"checklist--item checked\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>akcasb</span></p></li><ul class=\\"checklist\\"><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"true\\" aria-level=\\"2\\" class=\\"checklist--item checked\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Asdf</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"2\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Asdcasdc</span></p></li><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"2\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>sdac</span></p></li></ul><li dir=\\"ltr\\" role=\\"checkbox\\" aria-checked=\\"false\\" aria-level=\\"1\\" class=\\"checklist--item\\"><p dir=\\"ltr\\" role=\\"presentation\\"><span>Asdfsda</span></p></li></ul><!--EndFragment-->
</div>"
`;
exports[`properly format codeblocks 1`] = `
"<div>
<!--StartFragment--><p>Sure! Here's an implementation of a word counter for Thai that considers each syllable consisting of a consonant sound followed by a vowel sound as a word:</p><pre><code>javascript</code></pre><pre class=\\"language-javascript\\"><code>function countThaiWords(text) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { test } from "vitest";
import {
formatCodeblocks,
convertBrToSingleSpacedParagraphs
convertBrToSingleSpacedParagraphs,
convertGoogleDocsChecklist
} from "../clipboard-dom-parser";

const cases = [
Expand Down Expand Up @@ -101,7 +102,7 @@ and yet, we somehow manage to get by.<br>
];

for (const testCase of cases) {
const [html, expected] = testCase;
const [html] = testCase;
test(`convert br tags to paragraphs`, (t) => {
const element = new DOMParser().parseFromString(html, "text/html");
convertBrToSingleSpacedParagraphs(element);
Expand Down Expand Up @@ -139,3 +140,17 @@ for (const codeBlock of codeBlocks) {
t.expect(element.body.innerHTML.trim()).toMatchSnapshot();
});
}

const checkLists = [
`<div>
<!--StartFragment--><meta charset="utf-8"><ul id="docs-internal-guid-0d9a5db3-7fff-ab55-e7ca-b178e1031970"><li dir="ltr" role="checkbox" aria-checked="false" aria-level="1"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Adsjkfhasdf</span></p></li><li dir="ltr" role="checkbox" aria-checked="false" aria-level="1"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Asdfsadf</span></p></li><li dir="ltr" role="checkbox" aria-checked="true" aria-level="1"><img alt="checked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Asdfsda</span></p></li><li dir="ltr" role="checkbox" aria-checked="true" aria-level="1"><img alt="checked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Fasd</span></p></li><li dir="ltr" role="checkbox" aria-checked="true" aria-level="1"><img alt="checked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Fasd</span></p></li><li dir="ltr" role="checkbox" aria-checked="false" aria-level="1"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >F</span></p></li><li dir="ltr" role="checkbox" aria-checked="true" aria-level="1"><img alt="checked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >akcasb</span></p></li><ul ><li dir="ltr" role="checkbox" aria-checked="true" aria-level="2"><img alt="checked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Asdf</span></p></li><li dir="ltr" role="checkbox" aria-checked="false" aria-level="2"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Asdcasdc</span></p></li><li dir="ltr" role="checkbox" aria-checked="false" aria-level="2"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >sdac</span></p></li></ul><li dir="ltr" role="checkbox" aria-checked="false" aria-level="1"><img alt="unchecked" aria-roledescription="checkbox" ><p dir="ltr" role="presentation"><span >Asdfsda</span></p></li></ul><!--EndFragment-->
</div>`
];
for (const checkList of checkLists) {
test(`convert google docs checklist`, (t) => {
const element = new DOMParser().parseFromString(checkList, "text/html");
convertGoogleDocsChecklist(element);
t.expect(element.body.innerHTML.trim()).toMatchSnapshot();
});
}

0 comments on commit 78fafdc

Please sign in to comment.