Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support copy/pasting tag link nodes in rich text mode #177

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/commonmark/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,9 @@ export function insertCommonmarkLinkCommand(
}

/**
* Inserts a tagLink at the cursor, optionally placing it around the currently selected text if able
* Inserts a tag_link at the cursor, optionally placing it around the currently selected text if able
* @param validate The validation method that will be used to validate the selected text
* @param isMetaTag Whether or not the inserted tagLink is for a meta tag
* @param isMetaTag Whether or not the inserted tag_link is for a meta tag
*/
export function insertTagLinkCommand(
validate: TagLinkOptions["validate"],
Expand Down
6 changes: 3 additions & 3 deletions src/rich-text/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ function getHeadingLevel(state: EditorState): number {
}

/**
* Creates a command that toggles tagLink formatting for a node
* Creates a command that toggles tag_link formatting for a node
* @param validate The function to validate the tagName with
* @param isMetaTag Whether the tag to be created is a meta tag or not
*/
Expand All @@ -294,7 +294,7 @@ export function toggleTagLinkCommand(
}

let tr = state.tr;
const nodeCheck = nodeTypeActive(state.schema.nodes.tagLink);
const nodeCheck = nodeTypeActive(state.schema.nodes.tag_link);
if (nodeCheck(state)) {
const selectedText = state.selection.content().content.firstChild
.attrs["tagName"] as string;
Expand All @@ -314,7 +314,7 @@ export function toggleTagLinkCommand(
return false;
}

const newTagNode = state.schema.nodes.tagLink.create({
const newTagNode = state.schema.nodes.tag_link.create({
tagName: selectedText.trim(),
tagType: isMetaTag ? "meta-tag" : "tag",
});
Expand Down
5 changes: 0 additions & 5 deletions src/rich-text/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { stackOverflowMarkdownSerializer } from "../shared/markdown-serializer";
import { CodeBlockView } from "./node-views/code-block";
import { HtmlBlock, HtmlBlockContainer } from "./node-views/html-block";
import { ImageView } from "./node-views/image";
import { TagLink } from "./node-views/tag-link";
import { richTextCodePasteHandler } from "../shared/prosemirror-plugins/code-paste-handler";
import { linkPasteHandler } from "./plugins/link-paste-handler";
import { linkPreviewPlugin, LinkPreviewProvider } from "./plugins/link-preview";
Expand Down Expand Up @@ -101,7 +100,6 @@ export class RichTextEditor extends BaseView {
EditorType.RichText
);

const tagLinkOptions = this.options.parserFeatures.tagLinks;
this.editorView = new EditorView(
(node: HTMLElement) => {
node.classList.add(...(this.options.classList || []));
Expand Down Expand Up @@ -162,9 +160,6 @@ export class RichTextEditor extends BaseView {
) {
return new ImageView(node, view, getPos);
},
tagLink(node: ProseMirrorNode) {
return new TagLink(node, tagLinkOptions);
},
html_block: function (node: ProseMirrorNode) {
return new HtmlBlock(node);
},
Expand Down
40 changes: 0 additions & 40 deletions src/rich-text/node-views/tag-link.ts

This file was deleted.

37 changes: 33 additions & 4 deletions src/rich-text/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,16 +425,45 @@ const nodes: {
},
},

// TODO should this be a mark instead?
tagLink: {
tag_link: {
content: "text*",
marks: "", // TODO should it accept marks?
marks: "",
atom: true, // TODO allow this to be editable
inline: true,
group: "inline",
attrs: {
tagName: { default: null },
tagType: { default: "tag" },
href: { default: null },
title: { default: null },
additionalClasses: { default: "" },
},
parseDOM: [
{
tag: "a.s-tag",
getAttrs(dom: HTMLElement) {
dom.classList.remove("s-tag");
return {
href: dom.getAttribute("href"),
title: dom.getAttribute("title"),
additionalClasses: Array.from(dom.classList).join(" "),
tagType: dom.getAttribute("tagtype"),
tagName: dom.textContent,
};
},
},
],
toDOM(node) {
return [
"a",
{
tagType: node.attrs.tagType as string,
href: node.attrs.href as string,
title: node.attrs.title as string,
class: `s-tag ${node.attrs.additionalClasses as string}`,
},
node.attrs.tagName,
];
},
},
};
Expand Down Expand Up @@ -481,7 +510,7 @@ const marks: {
},
parseDOM: [
{
tag: "a[href]",
tag: "a[href]:not(.s-tag)",
getAttrs(dom: HTMLElement) {
return {
href: dom.getAttribute("href"),
Expand Down
22 changes: 20 additions & 2 deletions src/shared/markdown-it/tag-link.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import MarkdownIt from "markdown-it/lib";
import StateInline from "markdown-it/lib/rules_inline/state_inline";
import { error } from "../logger";
import type { TagLinkOptions } from "../view";

function parse_tag_link(
Expand Down Expand Up @@ -33,7 +34,7 @@ function parse_tag_link(

const totalContent = state.src.slice(state.pos, labelEnd + 1);
const isMeta = totalContent.slice(0, 10) === "[meta-tag:";
const tagName = totalContent.slice(isMeta ? 10 : 5, -1);
const tagName = totalContent.slice(isMeta ? 10 : 5, -1).trim();

if (options.validate && !options.validate(tagName, isMeta, totalContent)) {
return false;
Expand All @@ -43,7 +44,24 @@ function parse_tag_link(
let token = state.push("tag_link_open", "a", 1);
token.attrSet("tagName", tagName);
token.attrSet("tagType", isMeta ? "meta-tag" : "tag");
token.content = totalContent;

// call the renderer as if it exists
if (options.render) {
const rendered = options.render(tagName, isMeta);

if (rendered && rendered.link) {
const additionalClasses = rendered.additionalClasses || [];
token.attrSet("href", rendered.link);
token.attrSet("title", rendered.linkTitle);
token.attrSet("additionalClasses", additionalClasses.join(" "));
} else {
// We don't want to crash the parsing process here since we can still display a passable version of the tag link.
// However, we should at least log a console error.
error(
`Unable to fully render taglink for [${tagName}] due to invalid response from options.renderer.`
);
}
}

token = state.push("text", "", 0);
token.content = tagName;
Expand Down
5 changes: 4 additions & 1 deletion src/shared/markdown-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,13 @@ const customMarkdownParserTokens: MarkdownParser["tokens"] = {
},

tag_link: {
block: "tagLink",
block: "tag_link",
getAttrs: (tok: Token) => ({
tagName: tok.attrGet("tagName"),
tagType: tok.attrGet("tagType"),
href: tok.attrGet("href"),
additionalClasses: tok.attrGet("additionalClasses"),
title: tok.attrGet("title"),
}),
},

Expand Down
2 changes: 1 addition & 1 deletion src/shared/markdown-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ const customMarkdownSerializerNodes: MarkdownSerializerNodes = {
state.closeBlock(node);
},

tagLink(state, node) {
tag_link(state, node) {
const isMeta = node.attrs.tagType === "meta-tag";
const prefix = isMeta ? "meta-tag" : "tag";
const tag = node.attrs.tagName as string;
Expand Down
4 changes: 2 additions & 2 deletions src/shared/menu/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const moreFormattingDropdown = (schema: Schema, options: CommonViewOptions) =>
options.parserFeatures?.tagLinks?.validate,
false
),
active: nodeTypeActive(schema.nodes.tagLink),
active: nodeTypeActive(schema.nodes.tag_link),
},
commonmark: insertTagLinkCommand(
options.parserFeatures?.tagLinks?.validate,
Expand All @@ -188,7 +188,7 @@ const moreFormattingDropdown = (schema: Schema, options: CommonViewOptions) =>
options.parserFeatures?.tagLinks?.validate,
true
),
active: nodeTypeActive(schema.nodes.tagLink),
active: nodeTypeActive(schema.nodes.tag_link),
},
commonmark: insertTagLinkCommand(
options.parserFeatures?.tagLinks?.validate,
Expand Down
14 changes: 7 additions & 7 deletions test/rich-text/commands/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ describe("commands", () => {
let containsTagLink = false;

newState.doc.nodesBetween(0, newState.doc.content.size, (node) => {
containsTagLink = node.type.name === "tagLink";
containsTagLink = node.type.name === "tag_link";

return !containsTagLink;
});
Expand All @@ -497,7 +497,7 @@ describe("commands", () => {
let containsTagLink = false;

newState.doc.nodesBetween(0, newState.doc.content.size, (node) => {
containsTagLink = node.type.name === "tagLink";
containsTagLink = node.type.name === "tag_link";

return !containsTagLink;
});
Expand Down Expand Up @@ -533,7 +533,7 @@ describe("commands", () => {
0,
tagLinkResult.newState.doc.content.size,
(node) => {
containsTagLink = node.type.name === "tagLink";
containsTagLink = node.type.name === "tag_link";

return !containsTagLink;
}
Expand All @@ -543,7 +543,7 @@ describe("commands", () => {
}
);

it("should replace selected text with tagLink", () => {
it("should replace selected text with tag_link", () => {
let state = createState("this is my state", []);

state = applySelection(state, 5, 7); //"is"
Expand All @@ -566,7 +566,7 @@ describe("commands", () => {
text: "this ",
},
{
"type.name": "tagLink",
"type.name": "tag_link",
},
{
isText: true,
Expand All @@ -578,7 +578,7 @@ describe("commands", () => {
});
});

it("should untoggle tagLink when selected", () => {
it("should untoggle tag_link when selected", () => {
let state = createState("someText", []);

state = applySelection(state, 0, 8); // cursor is inside the tag
Expand All @@ -597,7 +597,7 @@ describe("commands", () => {
"type.name": "paragraph",
"content": [
{
"type.name": "tagLink",
"type.name": "tag_link",
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion test/shared/markdown-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ console.log("test");
"childCount": 1,
"content": [
{
"type.name": "tagLink",
"type.name": "tag_link",
"childCount": 1,
"content": [{ text: "python" }],
},
Expand Down