diff options
| author | laurawilsonri <laura_wilson@brown.edu> | 2019-03-18 18:14:27 -0400 |
|---|---|---|
| committer | laurawilsonri <laura_wilson@brown.edu> | 2019-03-18 18:14:27 -0400 |
| commit | 9d939c1190ef86e456cf26e9f5cb84743279f7a6 (patch) | |
| tree | 24d72d918f2becf6762ea30b92da7339957eedbd /src/client/util | |
| parent | 67170e521366f8178645cc85aaf377e53b1a6f21 (diff) | |
| parent | f70ad315167b714f11f7d68f35a46abe9e525a4d (diff) | |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into editableSchema
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/DragManager.ts | 54 | ||||
| -rw-r--r-- | src/client/util/RichTextRules.ts | 43 | ||||
| -rw-r--r-- | src/client/util/RichTextSchema.tsx | 23 | ||||
| -rw-r--r-- | src/client/util/TooltipTextMenu.scss | 7 | ||||
| -rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 38 | ||||
| -rw-r--r-- | src/client/util/jsx-decl.d.ts | 1 |
6 files changed, 135 insertions, 31 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4a61220a5..753115f76 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -2,18 +2,21 @@ import { DocumentDecorations } from "../views/DocumentDecorations"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { Document } from "../../fields/Document" import { action } from "mobx"; -import { DocumentView } from "../views/nodes/DocumentView"; import { ImageField } from "../../fields/ImageField"; import { KeyStore } from "../../fields/KeyStore"; +import { CollectionView } from "../views/collections/CollectionView"; +import { DocumentView } from "../views/nodes/DocumentView"; -export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document) { +export function setupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); - DragManager.StartDrag(_reference.current!, { document: docFunc() }); + var dragData = new DragManager.DocumentDragData(docFunc()); + dragData.removeDocument = removeFunc; + DragManager.StartDocumentDrag(_reference.current!, dragData); }); let onRowUp = action((e: PointerEvent): void => { document.removeEventListener("pointermove", onRowMove); @@ -70,11 +73,12 @@ export namespace DragManager { export interface DropOptions { handlers: DropHandlers; } - export class DropEvent { constructor(readonly x: number, readonly y: number, readonly data: { [id: string]: any }) { } } + + export interface DropHandlers { drop: (e: Event, de: DropEvent) => void; } @@ -96,7 +100,34 @@ export namespace DragManager { }; } - export function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options?: DragOptions) { + export class DocumentDragData { + constructor(dragDoc: Document) { + this.draggedDocument = dragDoc; + this.droppedDocument = dragDoc; + } + draggedDocument: Document; + droppedDocument: Document; + xOffset?: number; + yOffset?: number; + aliasOnDrop?: boolean; + removeDocument?: (collectionDrop: CollectionView) => void; + [id: string]: any; + } + export function StartDocumentDrag(ele: HTMLElement, dragData: DocumentDragData, options?: DragOptions) { + StartDrag(ele, dragData, options, (dropData: { [id: string]: any }) => dropData.droppedDocument = dragData.aliasOnDrop ? dragData.draggedDocument.CreateAlias() : dragData.draggedDocument); + } + + export class LinkDragData { + constructor(linkSourceDoc: DocumentView) { + this.linkSourceDocumentView = linkSourceDoc; + } + linkSourceDocumentView: DocumentView; + [id: string]: any; + } + export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, options?: DragOptions) { + StartDrag(ele, dragData, options); + } + function StartDrag(ele: HTMLElement, dragData: { [id: string]: any }, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { if (!dragDiv) { dragDiv = document.createElement("div"); dragDiv.className = "dragManager-dragDiv" @@ -122,9 +153,7 @@ export namespace DragManager { // bcz: PDFs don't show up if you clone them because they contain a canvas. // however, PDF's have a thumbnail field that contains an image of their canvas. // So we replace the pdf's canvas with the image thumbnail - const docView: DocumentView = dragData["documentView"]; - const doc: Document = docView ? docView.props.Document : dragData["document"]; - + const doc: Document = dragData["draggedDocument"]; if (doc) { var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement; let thumbnail = doc.GetT(KeyStore.Thumbnail, ImageField); @@ -138,7 +167,6 @@ export namespace DragManager { } } - dragDiv.appendChild(dragElement); let hideSource = false; @@ -175,13 +203,13 @@ export namespace DragManager { } const upHandler = (e: PointerEvent) => { abortDrag(); - FinishDrag(ele, e, dragData, options); + FinishDrag(ele, e, dragData, options, finishDrag); }; document.addEventListener("pointermove", moveHandler, true); document.addEventListener("pointerup", upHandler); } - function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions) { + function FinishDrag(dragEle: HTMLElement, e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) { let parent = dragEle.parentElement; if (parent) parent.removeChild(dragEle); @@ -191,6 +219,9 @@ export namespace DragManager { if (!target) { return; } + if (finishDrag) + finishDrag(dragData); + target.dispatchEvent(new CustomEvent<DropEvent>("dashOnDrop", { bubbles: true, detail: { @@ -199,6 +230,7 @@ export namespace DragManager { data: dragData } })); + if (options) { options.handlers.dragComplete({}); } diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts new file mode 100644 index 000000000..3b8396510 --- /dev/null +++ b/src/client/util/RichTextRules.ts @@ -0,0 +1,43 @@ +import { + inputRules, + wrappingInputRule, + textblockTypeInputRule, + smartQuotes, + emDash, + ellipsis +} from "prosemirror-inputrules"; +import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model"; + +import { schema } from "./RichTextSchema"; + +export const inpRules = { + rules: [ + ...smartQuotes, + ellipsis, + emDash, + + // > blockquote + wrappingInputRule(/^\s*>\s$/, schema.nodes.blockquote), + + // 1. ordered list + wrappingInputRule( + /^(\d+)\.\s$/, + schema.nodes.ordered_list, + match => ({ order: +match[1] }), + (match, node) => node.childCount + node.attrs.order === +match[1] + ), + + // * bullet list + wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list), + + // ``` code block + textblockTypeInputRule(/^```$/, schema.nodes.code_block), + + // # heading + textblockTypeInputRule( + new RegExp("^(#{1,6})\\s$"), + schema.nodes.heading, + match => ({ level: match[1].length }) + ) + ] +}; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index abf448c9f..2a3c1da6e 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -1,12 +1,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray } from "prosemirror-model" +import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType } from "prosemirror-model" import { joinUp, lift, setBlockType, toggleMark, wrapIn } from 'prosemirror-commands' import { redo, undo } from 'prosemirror-history' -import { orderedList, bulletList, listItem } from 'prosemirror-schema-list' +import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list' +import { EditorState, Transaction, NodeSelection, } from "prosemirror-state"; +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 } = { @@ -113,12 +116,22 @@ export const nodes: { [index: string]: NodeSpec } = { content: 'list_item+', group: 'block' }, + //this doesn't currently work for some reason bullet_list: { + ...bulletList, content: 'list_item+', group: 'block', - parseDOM: [{ tag: "ul" }, { style: "list-style-type=disc;" }], - toDOM() { return ulDOM } - }, + // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }], + // toDOM() { return ulDOM } + }, + //bullet_list: { + // content: 'list_item+', + // group: 'block', + //active: blockActive(schema.nodes.bullet_list), + //enable: wrapInList(schema.nodes.bullet_list), + //run: wrapInList(schema.nodes.bullet_list), + //select: state => true, + // }, list_item: { ...listItem, content: 'paragraph block*' diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index fa43f5326..ea580d104 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -1,8 +1,9 @@ +@import "../views/global_variables"; .tooltipMenu { position: absolute; z-index: 20; - background: rgb(19, 18, 18); + background: $dark-color; border: 1px solid silver; border-radius: 4px; padding: 2px 10px; @@ -31,14 +32,14 @@ bottom: -4.5px; border: 5px solid transparent; border-bottom-width: 0; - border-top-color: black; + border-top-color: $dark-color; } .menuicon { display: inline-block; border-right: 1px solid rgba(0, 0, 0, 0.2); //color: rgb(19, 18, 18); - color: white; + color: $light-color; line-height: 1; padding: 0px 2px; margin: 1px; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 3b87fe9de..2a613ba8b 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -2,10 +2,10 @@ import { action, IReactionDisposer, reaction } from "mobx"; import { baseKeymap } from "prosemirror-commands"; import { history, redo, undo } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; -const { exampleSetup } = require("prosemirror-example-setup") -import { EditorState, Transaction, } from "prosemirror-state"; +import { EditorState, Transaction, NodeSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; +import { Schema, NodeType } from "prosemirror-model" import React = require("react") import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); @@ -16,7 +16,7 @@ import { } from '@fortawesome/free-solid-svg-icons'; - +//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { private tooltip: HTMLElement; @@ -39,7 +39,8 @@ 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") }, - { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") } + //this doesn't work currently - look into notion of active block + { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, ] items.forEach(({ dom }) => this.tooltip.appendChild(dom)); @@ -49,7 +50,9 @@ export class TooltipTextMenu { view.focus(); items.forEach(({ command, dom }) => { if (dom.contains(e.srcElement)) { - 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" } }) }) @@ -66,13 +69,25 @@ export class TooltipTextMenu { return span; } - blockActive(view: EditorView) { - const { $from, to } = view.state.selection + //adapted this method - use it to check if block has a tag (ie bulleting) + blockActive(type: NodeType<Schema<string, string>>, state: EditorState) { + let attrs = {}; + + if (state.selection instanceof NodeSelection) { + const sel: NodeSelection = state.selection; + let $from = sel.$from; + let to = sel.to; + let node = sel.node; + + if (node) { + return node.hasMarkup(type, attrs); + } - return to <= $from.end() && $from.parent.hasMarkup(schema.nodes.bulletList); + return to <= $from.end() && $from.parent.hasMarkup(type, attrs); + } } - //this doesn't currently work but hopefully will soon + //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"); @@ -105,8 +120,6 @@ export class TooltipTextMenu { // Otherwise, reposition it and update its content this.tooltip.style.display = "" let { from, to } = state.selection - // These are in screen coordinates - //check this - tranform let start = view.coordsAtPos(from), end = view.coordsAtPos(to) // The box in which the tooltip is positioned, to use as base let box = this.tooltip.offsetParent!.getBoundingClientRect() @@ -116,8 +129,9 @@ export class TooltipTextMenu { this.tooltip.style.left = (left - box.left) + "px" let width = Math.abs(start.left - end.left) / 2; let mid = Math.min(start.left, end.left) + width; + //THIS WIDTH IS 15 * NUMBER OF ICONS + 15 - this.tooltip.style.width = 120 + "px"; + this.tooltip.style.width = 122 + "px"; this.tooltip.style.bottom = (box.bottom - start.top) + "px"; } diff --git a/src/client/util/jsx-decl.d.ts b/src/client/util/jsx-decl.d.ts new file mode 100644 index 000000000..532f06178 --- /dev/null +++ b/src/client/util/jsx-decl.d.ts @@ -0,0 +1 @@ +declare module 'react-jsx-parser'; |
