diff options
-rw-r--r-- | src/client/util/ProsemirrorKeymap.ts | 100 | ||||
-rw-r--r-- | src/client/util/RichTextSchema.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.scss | 6 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 5 |
4 files changed, 107 insertions, 5 deletions
diff --git a/src/client/util/ProsemirrorKeymap.ts b/src/client/util/ProsemirrorKeymap.ts new file mode 100644 index 000000000..7718b3c06 --- /dev/null +++ b/src/client/util/ProsemirrorKeymap.ts @@ -0,0 +1,100 @@ +import { Schema } from "prosemirror-model"; +import { + wrapIn, setBlockType, chainCommands, toggleMark, exitCode, + joinUp, joinDown, lift, selectParentNode +} from "prosemirror-commands"; +import { wrapInList, splitListItem, liftListItem, sinkListItem } from "prosemirror-schema-list"; +import { undo, redo } from "prosemirror-history"; +import { undoInputRule } from "prosemirror-inputrules"; +import { Transaction, EditorState } from "prosemirror-state"; + +const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; + +export type KeyMap = { [key: string]: any }; + +export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: KeyMap): KeyMap { + let keys: { [key: string]: any } = {}, type; + + function bind(key: string, cmd: any) { + if (mapKeys) { + let mapped = mapKeys[key]; + if (mapped === false) return; + if (mapped) key = mapped; + } + keys[key] = cmd; + } + + bind("Mod-z", undo); + bind("Shift-Mod-z", redo); + bind("Backspace", undoInputRule); + + if (!mac) { + bind("Mod-y", redo); + } + + bind("Alt-ArrowUp", joinUp); + bind("Alt-ArrowDown", joinDown); + bind("Mod-BracketLeft", lift); + bind("Escape", selectParentNode); + + if (type = schema.marks.strong) { + bind("Mod-b", toggleMark(type)); + bind("Mod-B", toggleMark(type)); + } + if (type = schema.marks.em) { + bind("Mod-i", toggleMark(type)); + bind("Mod-I", toggleMark(type)); + } + if (type = schema.marks.code) { + bind("Mod-`", toggleMark(type)); + } + + if (type = schema.nodes.bullet_list) { + bind("Shift-Ctrl-8", wrapInList(type)); + } + if (type = schema.nodes.ordered_list) { + bind("Shift-Ctrl-9", wrapInList(type)); + } + if (type = schema.nodes.blockquote) { + bind("Ctrl->", wrapIn(type)); + } + if (type = schema.nodes.hard_break) { + let br = type, cmd = chainCommands(exitCode, (state, dispatch) => { + if (dispatch) { + dispatch(state.tr.replaceSelectionWith(br.create()).scrollIntoView()); + return true; + } + return false; + }); + bind("Mod-Enter", cmd); + bind("Shift-Enter", cmd); + if (mac) { + bind("Ctrl-Enter", cmd); + } + } + if (type = schema.nodes.list_item) { + bind("Enter", splitListItem(type)); + bind("Mod-[", liftListItem(type)); + bind("Mod-]", sinkListItem(type)); + } + if (type = schema.nodes.paragraph) { + bind("Shift-Ctrl-0", setBlockType(type)); + } + if (type = schema.nodes.code_block) { + bind("Shift-Ctrl-\\", setBlockType(type)); + } + if (type = schema.nodes.heading) { + for (let i = 1; i <= 6; i++) { + bind("Shift-Ctrl-" + i, setBlockType(type, { level: i })); + } + } + if (type = schema.nodes.horizontal_rule) { + let hr = type; + bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { + dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()); + return true; + }); + } + + return keys; +}
\ No newline at end of file diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index dac4be3d6..42bcf2ae2 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -9,7 +9,6 @@ import { EditorView, } from "prosemirror-view"; const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; - // :: Object // [Specs](#model.NodeSpec) for the nodes defined in this schema. export const nodes: { [index: string]: NodeSpec } = { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index f2affbf55..4f940e094 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -48,15 +48,15 @@ .docContainer:hover { .delete-button { display: inline; - width: auto; + // width: auto; } } .delete-button { color: $intermediate-color; - float: right; + // float: right; margin-left: 15px; - margin-top: 3px; + // margin-top: 3px; display: inline; } }
\ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 08f94c820..41cb34416 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -7,6 +7,7 @@ import { EditorView } from "prosemirror-view"; import { FieldWaiting, Opt } from "../../../fields/Field"; import { RichTextField } from "../../../fields/RichTextField"; import { inpRules } from "../../util/RichTextRules"; +import { Schema } from "prosemirror-model"; import { schema } from "../../util/RichTextSchema"; import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { ContextMenu } from "../../views/ContextMenu"; @@ -14,6 +15,8 @@ import { Main } from "../Main"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); +import { undoItem } from "prosemirror-menu"; +import buildKeymap from "../../util/ProsemirrorKeymap"; const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); @@ -74,7 +77,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte inpRules, //these currently don't do anything, but could eventually be helpful plugins: this.props.isOverlay ? [ history(), - keymap({ "Mod-z": undo, "Mod-y": redo }), + keymap(buildKeymap(schema)), keymap(baseKeymap), this.tooltipMenuPlugin(), new Plugin({ |