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(rich-text-editor): Add tagLink, subscript, superscript, kbd, and spoiler input #158

Merged
merged 35 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2727669
saving progress on tag
Jul 6, 2022
c0c828b
Adding overflow dropdown for tag command
Jul 7, 2022
8350275
chore: fix minor typos (#156)
KyleMit Jul 7, 2022
9b01814
Updating localization for tagLink
Jul 7, 2022
9106c38
Adding new mark buttons for sup/sub and spoiler wrap.
Jul 7, 2022
9164fea
Undo commonmark changes
Jul 7, 2022
4ab904f
Fix broken active methods. Renamed test var.
Jul 7, 2022
f3b402c
Adding tests for wrapping node with spoiler/blockquote
Jul 8, 2022
201d174
Forgot docstring
Jul 8, 2022
c54909d
Merge branch 'main' into tmcentee/editor-51/new-rich-text-nodes
Jul 13, 2022
b7af4cc
Updated tagLink validation.
Jul 13, 2022
16edd14
Merge branch 'main' into tmcentee/editor-51/new-rich-text-nodes
b-kelly Jul 13, 2022
13993ee
readd items mistakenly removed during merge
b-kelly Jul 13, 2022
ad31959
tweak name
b-kelly Jul 13, 2022
33ef1d7
documentation
b-kelly Jul 13, 2022
61048ab
tag -> tagLink
Jul 14, 2022
da48d6c
overflow -> More formatting
Jul 14, 2022
8475514
Fix renamed SVG. Fixed tagname validation preventing toggle
Jul 14, 2022
950198b
adding basic docstrings for exported functions
Jul 14, 2022
836f92b
update strings in marks test
Jul 14, 2022
cad22e5
Created new NodeSelection test helper methods. Updated tagLink test t…
Jul 15, 2022
19419c4
Added more thorough tagLink target validation with tests
Jul 15, 2022
3d31a91
Adding <kbd> mark type to Rich Text input
Jul 15, 2022
2838e95
Changing keyboard shortcuts for sub/sup to match Google Docs
Jul 18, 2022
79b1113
Added trailing space logic to adjust text selection on tag creation
Jul 18, 2022
6b42611
Adding meta tag input. Reworked keyboard shortcuts
Jul 18, 2022
c4b2117
Merge branch 'main' into tmcentee/editor-51/new-rich-text-nodes
b-kelly Jul 19, 2022
75f443b
update to use TagLinkOptions.validate adding in main branch
b-kelly Jul 19, 2022
f1b0648
cleanup
b-kelly Jul 19, 2022
2582885
fix failing tests
b-kelly Jul 19, 2022
e49a2f8
cleanup
b-kelly Jul 19, 2022
802cec6
Merge branch 'main' into tmcentee/editor-51/new-rich-text-nodes
b-kelly Jul 25, 2022
8c1b168
Merge branch 'main' into tmcentee/editor-51/new-rich-text-nodes
b-kelly Jul 27, 2022
28e25d7
cleanup
b-kelly Jul 27, 2022
e682c75
move overflow dropdown
b-kelly Jul 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 163 additions & 1 deletion src/rich-text/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
EditorState,
TextSelection,
Transaction,
Selection,
} from "prosemirror-state";
import { liftTarget } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
Expand All @@ -23,7 +24,7 @@ import {
showImageUploader,
} from "../../shared/prosemirror-plugins/image-upload";
import { getCurrentTextNode, getShortcut } from "../../shared/utils";
import type { CommonViewOptions } from "../../shared/view";
import type { CommonViewOptions, TagLinkOptions } from "../../shared/view";
import { showLinkEditor } from "../plugins/link-editor";
import { insertParagraphIfAtDocEnd } from "./helpers";
import {
Expand Down Expand Up @@ -158,6 +159,92 @@ function getHeadingLevel(state: EditorState): number {
return level;
}

/**
* Creates a command that toggles tagLink 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
*/
export function toggleTagLinkCommand(
validate: TagLinkOptions["validate"],
isMetaTag: boolean
) {
return (state: EditorState, dispatch?: (tr: Transaction) => void) => {
if (state.selection.empty) {
return false;
}

if (!isValidTagLinkTarget(state.schema, state.selection)) {
return false;
}

if (!dispatch) {
return true;
}

let tr = state.tr;
const nodeCheck = nodeTypeActive(state.schema.nodes.tagLink);
if (nodeCheck(state)) {
const selectedText = state.selection.content().content.firstChild
tmcentee marked this conversation as resolved.
Show resolved Hide resolved
.attrs["tagName"] as string;

tr = state.tr.replaceSelectionWith(state.schema.text(selectedText));
} else {
const selectedText =
state.selection.content().content.firstChild?.textContent;

// If we have a trailing space, update the selection to not include it.
if (selectedText.endsWith(" ")) {
const { from, to } = state.selection;
state.selection = TextSelection.create(state.doc, from, to - 1);
}

if (!validate(selectedText.trim(), isMetaTag)) {
return false;
}

const newTagNode = state.schema.nodes.tagLink.create({
tagName: selectedText.trim(),
tagType: isMetaTag ? "meta-tag" : "tag",
});

tr = state.tr.replaceSelectionWith(newTagNode);
tmcentee marked this conversation as resolved.
Show resolved Hide resolved
}

dispatch(tr);

return true;
};
}

/**
* Validates whether the target of our selection is within a valid context. e.g. not in a link
* @param schema Current editor schema
* @param selection Current selection handle
*/
function isValidTagLinkTarget(schema: Schema, selection: Selection): boolean {
const invalidNodeTypes = [
schema.nodes.horizontal_rule,
schema.nodes.code_block,
schema.nodes.image,
];

const invalidNodeMarks = [schema.marks.link, schema.marks.code];

const hasInvalidMark =
selection.$head.marks().filter((f) => invalidNodeMarks.includes(f.type))
.length != 0;

return (
!invalidNodeTypes.includes(selection.$head.parent.type) &&
!hasInvalidMark
);
}

/**
* Creates a command that inserts a horizontal rule node
* @param state The current editor state
* @param dispatch The dispatch function to use
*/
export function insertHorizontalRuleCommand(
state: EditorState,
dispatch: (tr: Transaction) => void
Expand Down Expand Up @@ -191,6 +278,12 @@ export function insertHorizontalRuleCommand(
return true;
}

/**
* Opens the image uploader pane
* @param state The current editor state
* @param dispatch The dispatch function to use
* @param view The current editor view
*/
export function insertImageCommand(
state: EditorState,
dispatch: (tr: Transaction) => void,
Expand All @@ -208,6 +301,9 @@ export function insertImageCommand(

/**
* Inserts a link into the document and opens the link edit tooltip at the cursor
* @param state The current editor state
* @param dispatch The dispatch function to use
* @param view The current editor view
*/
export function insertLinkCommand(
state: EditorState,
Expand Down Expand Up @@ -309,6 +405,8 @@ function markActive(mark: MarkType) {
/**
* Exits an inclusive mark that has been marked as exitable by toggling the mark type
* and optionally adding a trailing space if the mark is at the end of the document
* @param state The current editor state
* @param dispatch The dispatch function to use
*/
export function exitInclusiveMarkCommand(
state: EditorState,
Expand Down Expand Up @@ -365,6 +463,9 @@ export function exitInclusiveMarkCommand(
return true;
}

/**
* Creates a dropdown menu for table edit functionality
*/
const tableDropdown = () =>
makeMenuDropdown(
"Table",
Expand Down Expand Up @@ -408,6 +509,10 @@ const tableDropdown = () =>
)
);

/**
* Creates a dropdown menu for heading formatting
* @param schema The finalized rich-text schema
*/
const headingDropdown = (schema: Schema) =>
makeMenuDropdown(
"Header",
Expand Down Expand Up @@ -438,6 +543,62 @@ const headingDropdown = (schema: Schema) =>
)
);

/**
* Creates a dropdown menu containing misc formatting tools
* @param schema The finalized rich-text schema
* @param options The options for the editor
*/
const moreFormattingDropdown = (schema: Schema, options: CommonViewOptions) =>
makeMenuDropdown(
"EllipsisHorizontal",
_t("commands.moreFormatting"),
"more-formatting-dropdown",
() => true,
() => false,
tmcentee marked this conversation as resolved.
Show resolved Hide resolved
dropdownItem(
_t("commands.tagLink", { shortcut: getShortcut("Mod-[") }),
toggleTagLinkCommand(
options.parserFeatures.tagLinks.validate,
false
),
"tag-btn",
nodeTypeActive(schema.nodes.tagLink)
),
dropdownItem(
_t("commands.metaTagLink", { shortcut: getShortcut("Mod-]") }),
toggleTagLinkCommand(
options.parserFeatures.tagLinks.validate,
true
),
"tag-btn",
nodeTypeActive(schema.nodes.tagLink)
),
dropdownItem(
_t("commands.spoiler", { shortcut: getShortcut("Mod-/") }),
toggleWrapIn(schema.nodes.spoiler),
"spoiler-btn",
nodeTypeActive(schema.nodes.spoiler)
),
dropdownItem(
_t("commands.sub", { shortcut: getShortcut("Mod-,") }),
toggleMark(schema.marks.sub),
"subscript-btn",
markActive(schema.marks.sub)
),
dropdownItem(
_t("commands.sup", { shortcut: getShortcut("Mod-.") }),
toggleMark(schema.marks.sup),
"superscript-btn",
markActive(schema.marks.sup)
),
dropdownItem(
_t("commands.kbd", { shortcut: getShortcut("Mod-'") }),
toggleMark(schema.marks.kbd),
"kbd-btn",
markActive(schema.marks.kbd)
)
);

// TODO ensure that all names and priorities match those found in the rich-text editor
/**
* Creates all menu entries for the commonmark editor
Expand Down Expand Up @@ -610,6 +771,7 @@ export const createMenuEntries = (
"horizontal-rule-btn"
),
},
moreFormattingDropdown(schema, options),
],
},
{
Expand Down
14 changes: 14 additions & 0 deletions src/rich-text/key-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
insertTableCommand,
exitInclusiveMarkCommand,
toggleHeadingLevel,
toggleTagLinkCommand,
} from "./commands";

export function allKeymaps(
Expand Down Expand Up @@ -70,6 +71,19 @@ export function allKeymaps(
...bindLetterKeymap("Mod-h", toggleHeadingLevel()),
...bindLetterKeymap("Mod-r", insertHorizontalRuleCommand),
...bindLetterKeymap("Mod-m", setBlockType(schema.nodes.code_block)),
...bindLetterKeymap(
"Mod-[",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewer's note: mod+[/] are commonly used in IDEs to (un)indent a line. This shouldn't pose too much of an issue though, as we can chain commands together, placing the code indent command first, falling back to creating a taglink.

No action necessary.

toggleTagLinkCommand(parserFeatures.tagLinks.validate, false)
),
...bindLetterKeymap(
"Mod-]",
toggleTagLinkCommand(parserFeatures.tagLinks.validate, true)
),
...bindLetterKeymap("Mod-/", wrapIn(schema.nodes.spoiler)),
...bindLetterKeymap("Mod-,", toggleMark(schema.marks.sub)),
...bindLetterKeymap("Mod-.", toggleMark(schema.marks.sup)),
...bindLetterKeymap("Mod-'", toggleMark(schema.marks.kbd)),

// users expect to be able to leave certain blocks/marks using the arrow keys
"ArrowRight": exitInclusiveMarkCommand,
"ArrowDown": exitCode,
Expand Down
7 changes: 7 additions & 0 deletions src/shared/localization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ export const defaultStrings = {
horizontal_rule: shortcut("Horizontal rule"),
image: shortcut("Image"),
inline_code: shortcut("Inline code"),
kbd: shortcut("Keyboard"),
link: shortcut("Link"),
metaTagLink: shortcut("Meta tag"),
moreFormatting: "More formatting",
ordered_list: shortcut("Numbered list"),
redo: shortcut("Redo"),
spoiler: shortcut("Spoiler"),
sub: shortcut("Subscript"),
sup: shortcut("Superscript"),
strikethrough: "Strikethrough",
table_edit: "Edit table",
table_insert: shortcut("Table"),
Expand All @@ -43,6 +49,7 @@ export const defaultStrings = {
insert_before: "Insert row before",
remove: "Remove row",
},
tagLink: shortcut("Tag"),
undo: shortcut("Undo"),
unordered_list: shortcut("Bulleted list"),
},
Expand Down
4 changes: 4 additions & 0 deletions src/styles/icons.less
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,7 @@
width: 21px;
--bg-icon: url("~@stackoverflow/stacks-icons/src/Icon/RichText.svg");
}

.icon-bg.iconEllipsisHorizontal {
--bg-icon: url("~@stackoverflow/stacks-icons/src/Icon/EllipsisHorizontal.svg");
}
Loading