diff options
author | bob <bcz@cs.brown.edu> | 2019-08-30 13:04:40 -0400 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2019-08-30 13:04:40 -0400 |
commit | 5f39a01998e5ee60eb6b796a5cd6b11fc680ceee (patch) | |
tree | 4b9a0e5e75e8b1ee2dbc9157eb6b2ecd381d4398 | |
parent | 195497328a6113c40993f3a3d525cc6596955d7c (diff) |
cleanup of outline mode text
-rw-r--r-- | src/client/util/ProsemirrorExampleTransfer.ts | 96 | ||||
-rw-r--r-- | src/client/util/RichTextSchema.tsx | 6 | ||||
-rw-r--r-- | src/client/util/TooltipLinkingMenu.tsx | 20 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 4 | ||||
-rw-r--r-- | src/client/util/prosemirrorPatches.js | 62 |
5 files changed, 117 insertions, 71 deletions
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 3979d8a49..4af2cf58f 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -2,7 +2,8 @@ import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, setB import { redo, undo } from "prosemirror-history"; import { undoInputRule } from "prosemirror-inputrules"; import { Schema } from "prosemirror-model"; -import { liftListItem, splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; +import { liftListItem, } from "./prosemirrorPatches.js"; +import { splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { TooltipTextMenu } from "./TooltipTextMenu"; @@ -79,75 +80,73 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - let nodeTypeMark = (depth: number) => depth === 2 ? "indent2" : depth === 4 ? "indent3" : depth === 6 ? "indent4" : "indent1"; + let updateBullets = (tx2: Transaction, refStart: number, delta: number) => { + for (let i = refStart; i > 0; i--) { + let testPos = tx2.doc.nodeAt(i); + if (testPos && testPos.type === schema.nodes.list_item) { + let start = i; + let preve = i > 0 && tx2.doc.nodeAt(start - 1); + if (preve && preve.type === schema.nodes.ordered_list) { + start = start - 1; + } + let rangeStart = tx2.doc.nodeAt(start); + if (rangeStart && rangeStart.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(start, rangeStart.type, { ...rangeStart.attrs, bulletStyle: rangeStart.attrs.bulletStyle + delta }, rangeStart.marks); + } + rangeStart && rangeStart.descendants((node: any, offset: any, index: any) => { + if (node.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(start + offset + 1, node.type, { ...node.attrs, bulletStyle: node.attrs.bulletStyle + delta }, node.marks); + } + }); + break; + } + } + } - let bulletFunc = (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { + bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { var ref = state.selection; var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - let depth = range && range.depth ? range.depth : 0; if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - const resolvedPos = tx2.doc.resolve(range!.start); - - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); - break; - } - } + updateBullets(tx2, range!.start, 1); marks && tx2.ensureMarks([...marks]); marks && tx2.setStoredMarks([...marks]); dispatch(tx2); - })) { + })) { // couldn't sink into an existing list, so wrap in a new one let sxf = state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)); let newstate = state.applyTransaction(sxf); if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { - const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); + for (let i = range!.start; i >= 0; i--) { + let rangeStart = tx2.doc.nodeAt(i); + if (rangeStart && rangeStart.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(i, rangeStart.type, { ...rangeStart.attrs, bulletStyle: 1 }, rangeStart.marks); break; } } // when promoting to a list, assume list will format things so don't copy the stored marks. - // marks && tx2.ensureMarks([...marks]); - // marks && tx2.setStoredMarks([...marks]); - + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); dispatch(tx2); })) { - console.log("bullet fail"); + console.log("bullet promote fail"); } } - }; - - bind("Tab", bulletFunc); + }); bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { - var ref = state.selection; - var range = ref.$from.blockRange(ref.$to); + var range = state.selection.$from.blockRange(state.selection.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - let depth = range && range.depth > 3 ? range.depth - 4 : 0; - liftListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - try { - const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); - break; - } - } - marks && tx2.ensureMarks([...marks]); - marks && tx2.setStoredMarks([...marks]); - dispatch(tx2); - } catch (e) { - dispatch(tx2); - } - }); + let tr = state.tr; + updateBullets(tr, range!.start, -1); + + if (!liftListItem(schema.nodes.list_item)(tr, (tx2: Transaction) => { + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + dispatch(tx2); + })) { + console.log("bullet demote fail"); + } }); bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => { @@ -160,8 +159,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: if (!splitBlockKeepMarks(state, (tx3: Transaction) => { marks && tx3.ensureMarks(marks); marks && tx3.setStoredMarks(marks); - if (!liftListItem(schema.nodes.list_item)(state, dispatch as ((tx: Transaction<Schema<any, any>>) => void)) - ) { + if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) { dispatch(tx3); } })) { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a642ee46c..0994f5f6b 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -176,13 +176,13 @@ export const nodes: { [index: string]: NodeSpec } = { content: 'list_item+', group: 'block', attrs: { - bulletStyle: { default: "" }, + bulletStyle: { default: 0 }, mapStyle: { default: "decimal" } }, toDOM(node: Node<any>) { const bs = node.attrs.bulletStyle; - const decMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "decimal2" : bs === "indent3" ? "decimal3" : bs === "indent4" ? "decimal4" : ""; - const multiMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "upper-alpha" : bs === "indent3" ? "lower-roman" : bs === "indent4" ? "lower-alpha" : ""; + const decMap = bs === 1 ? "decimal" : bs === 2 ? "decimal2" : bs === 3 ? "decimal3" : bs === 4 ? "decimal4" : ""; + const multiMap = bs === 1 ? "decimal" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : ""; let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap; for (let i = 0; i < node.childCount; i++) node.child(i).attrs.className = map; return ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0]; diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx index 55e0eb909..e6d6c471f 100644 --- a/src/client/util/TooltipLinkingMenu.tsx +++ b/src/client/util/TooltipLinkingMenu.tsx @@ -1,23 +1,9 @@ -import { action, IReactionDisposer, reaction } from "mobx"; -import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css -import { baseKeymap, lift } from "prosemirror-commands"; -import { history, redo, undo } from "prosemirror-history"; -import { keymap } from "prosemirror-keymap"; -import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { schema } from "./RichTextSchema"; -import { Schema, NodeType, MarkType } from "prosemirror-model"; -import React = require("react"); +import { FieldViewProps } from "../views/nodes/FieldView"; import "./TooltipTextMenu.scss"; +import React = require("react"); const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); -import { library } from '@fortawesome/fontawesome-svg-core'; -import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list'; -import { - faListUl, -} from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FieldViewProps } from "../views/nodes/FieldView"; -import { throwStatement } from "babel-types"; const SVG = "http://www.w3.org/2000/svg"; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index e979e6cde..3b4b7f05a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -3,7 +3,7 @@ import { faListUl } from '@fortawesome/free-solid-svg-icons'; import { action, observable } from "mobx"; import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model"; -import { liftListItem, wrapInList } from 'prosemirror-schema-list'; +import { wrapInList } from 'prosemirror-schema-list'; import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Field, Opt } from "../../new_fields/Doc"; @@ -525,7 +525,7 @@ export class TooltipTextMenu { let path = resolvedPos.path; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = "indent1"; + path[i].attrs.bulletStyle = 1; path[i].attrs.mapStyle = (nodeType as any).attrs.mapStyle; break; } diff --git a/src/client/util/prosemirrorPatches.js b/src/client/util/prosemirrorPatches.js new file mode 100644 index 000000000..c273c2323 --- /dev/null +++ b/src/client/util/prosemirrorPatches.js @@ -0,0 +1,62 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var prosemirrorTransform = require('prosemirror-transform'); +var prosemirrorModel = require('prosemirror-model'); + +exports.liftListItem = liftListItem; +// :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool +// Create a command to lift the list item around the selection up into +// a wrapping list. +function liftListItem(itemType) { + return function (tx, dispatch) { + var ref = tx.selection; + var $from = ref.$from; + var $to = ref.$to; + var range = $from.blockRange($to, function (node) { return node.childCount && node.firstChild.type == itemType; }); + if (!range) { return false } + if (!dispatch) { return true } + if ($from.node(range.depth - 1).type == itemType) // Inside a parent list + { return liftToOuterList(tx, dispatch, itemType, range) } + else // Outer list node + { return liftOutOfList(tx, dispatch, range) } + } +} + +function liftToOuterList(tr, dispatch, itemType, range) { + var end = range.end, endOfList = range.$to.end(range.depth); + if (end < endOfList) { + // There are siblings after the lifted items, which must become + // children of the last item + tr.step(new prosemirrorTransform.ReplaceAroundStep(end - 1, endOfList, end, endOfList, + new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true)); + range = new prosemirrorModel.NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth); + } + dispatch(tr.lift(range, prosemirrorTransform.liftTarget(range)).scrollIntoView()); + return true +} + +function liftOutOfList(tr, dispatch, range) { + var list = range.parent; + // Merge the list items into a single big item + for (var pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) { + pos -= list.child(i).nodeSize; + tr.delete(pos - 1, pos + 1); + } + var $start = tr.doc.resolve(range.start), item = $start.nodeAfter; + var atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount; + var parent = $start.node(-1), indexBefore = $start.index(-1); + if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, + item.content.append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list)))) { return false } + var start = $start.pos, end = start + item.nodeSize; + // Strip off the surrounding list. At the sides where we're not at + // the end of the list, the existing list is closed. At sides where + // this is the end, it is overwritten to its end. + tr.step(new prosemirrorTransform.ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, + new prosemirrorModel.Slice((atStart ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))) + .append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))), + atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1)); + dispatch(tr.scrollIntoView()); + return true +}
\ No newline at end of file |