diff options
author | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-04-13 19:06:18 -0400 |
---|---|---|
committer | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-04-13 19:06:18 -0400 |
commit | 8c58ff299e371c70147aa8123ed9fb0ae1a9e880 (patch) | |
tree | 1592b23dc2da31e7c200cd1c4c8f01181c8bbf60 /src | |
parent | 79a0d8d66204868158849afda6518f6c641c826b (diff) | |
parent | 74411165f21b6c616fa98e0676c6f4568c2d4564 (diff) |
added typesheet for full prosemirror keymap
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/ProsemirrorKeymap.ts | 100 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 46 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 13 |
3 files changed, 141 insertions, 18 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/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 19affdf97..48f835a0f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -21,6 +21,7 @@ const SVG = "http://www.w3.org/2000/svg"; export class TooltipTextMenu { private tooltip: HTMLElement; + private num_icons = 0; constructor(view: EditorView) { this.tooltip = document.createElement("div"); @@ -40,20 +41,29 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough") }, { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") }, { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") }, - //this doesn't work currently - look into notion of active block { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, + { command: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") }, + { command: lift, dom: this.icon("<", "lift") }, ]; - items.forEach(({ dom }) => this.tooltip.appendChild(dom)); + //add menu items + items.forEach(({ dom, command }) => { + this.tooltip.appendChild(dom); + }); + + //add dropdowns //pointer down handler to activate button effects this.tooltip.addEventListener("pointerdown", e => { e.preventDefault(); view.focus(); + //update view of icons + this.num_icons = 0; items.forEach(({ command, dom }) => { - if (dom.contains(e.target as Node)) { + if (e.srcElement && dom.contains(e.srcElement as Node)) { + //let active = command(view.state, view.dispatch, view); let active = command(view.state, view.dispatch, view); //uncomment this if we want the bullet button to disappear if current selection is bulleted - // dom.style.display = active ? "" : "none" + //dom.style.display = active ? "" : "none"; } }); }); @@ -91,9 +101,24 @@ export class TooltipTextMenu { //this doesn't currently work but could be used to use icons for buttons unorderedListIcon(): HTMLSpanElement { let span = document.createElement("span"); - let icon = document.createElement("FontAwesomeIcon"); - icon.className = "menuicon fa fa-smile-o"; - span.appendChild(icon); + //let icon = document.createElement("FontAwesomeIcon"); + //icon.className = "menuicon"; + //icon.style.color = "white"; + //span.appendChild(<i style={{ color: "white" }} icon="list-ul" size="lg" />); + let i = document.createElement("i"); + i.className = "fa falist-ul"; + span.appendChild(i); + //span.appendChild(icon); + //return liftItem.spec.icon.sty + + //let sym = document.createElementNS(SVG, "symbol") + // sym.id = name + //sym.style.color = "white"; + //width then height + //sym.setAttribute("viewBox", "0 0 " + 1024 + " " + 1024); + //let path = sym.appendChild(document.createElementNS(SVG, "path")); + //path.setAttribute("d", "M219 310v329q0 7-5 12t-12 5q-8 0-13-5l-164-164q-5-5-5-13t5-13l164-164q5-5 13-5 7 0 12 5t5 12zM1024 749v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12zM1024 530v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 310v109q0 7-5 12t-12 5h-621q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h621q7 0 12 5t5 12zM1024 91v109q0 7-5 12t-12 5h-987q-7 0-12-5t-5-12v-109q0-7 5-12t12-5h987q7 0 12 5t5 12z"); + //span.appendChild(sym); return span; } @@ -128,13 +153,14 @@ export class TooltipTextMenu { // crossing lines, end may be more to the left) let left = Math.max((start.left + end.left) / 2, start.left + 3); this.tooltip.style.left = (left - box.left) + "px"; - let width = Math.abs(start.left - end.left) / 2; + //let width = Math.abs(start.left - end.left) / 2; + let width = 8 * 16 + 15; let mid = Math.min(start.left, end.left) + width; //THIS WIDTH IS 15 * NUMBER OF ICONS + 15 - this.tooltip.style.width = 122 + "px"; + this.tooltip.style.width = width + "px"; this.tooltip.style.bottom = (box.bottom - start.top) + "px"; } destroy() { this.tooltip.remove(); } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ec766e069..e872ea48b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -16,6 +16,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"); @@ -64,12 +66,6 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { } } - undo = <S extends Schema = any>(state: EditorState<S>, dispatch?: (tr: Transaction<S>) => void): boolean => { - console.log(state); - console.log(dispatch); - return true; - } - get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.Document; } get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; } @@ -79,7 +75,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { inpRules, //these currently don't do anything, but could eventually be helpful plugins: [ history(), - keymap({ "Mod-z": this.undo, "Mod-y": redo }), + keymap(buildKeymap(schema)), keymap(baseKeymap), this.tooltipMenuPlugin() ] @@ -213,7 +209,8 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { }); } onKeyPress(e: React.KeyboardEvent) { - e.stopPropagation(); + console.log(e.keyCode) + // e.stopPropagation(); // stop propagation doesn't seem to stop propagation of native keyboard events. // so we set a flag on the native event that marks that the event's been handled. // (e.nativeEvent as any).DASHFormattedTextBoxHandled = true; |