diff options
Diffstat (limited to 'src/client/util')
-rw-r--r-- | src/client/util/DocumentManager.ts | 50 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 113 | ||||
-rw-r--r-- | src/client/util/Scripting.ts | 34 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 15 | ||||
-rw-r--r-- | src/client/util/SerializationHelper.ts | 25 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.scss | 4 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 148 | ||||
-rw-r--r-- | src/client/util/UndoManager.ts | 7 |
8 files changed, 242 insertions, 154 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 3b5a5b470..69964e2c9 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,9 +1,8 @@ import { computed, observable } from 'mobx'; -import { Document } from "../../fields/Document"; -import { FieldWaiting } from '../../fields/Field'; -import { KeyStore } from '../../fields/KeyStore'; -import { ListField } from '../../fields/ListField'; import { DocumentView } from '../views/nodes/DocumentView'; +import { Doc } from '../../new_fields/Doc'; +import { FieldValue, Cast } from '../../new_fields/Types'; +import { listSpec } from '../../new_fields/Schema'; export class DocumentManager { @@ -25,28 +24,29 @@ export class DocumentManager { // this.DocumentViews = new Array<DocumentView>(); } - public getDocumentView(toFind: Document): DocumentView | null { + public getDocumentView(toFind: Doc): DocumentView | null { - let toReturn: DocumentView | null; - toReturn = null; + let toReturn: DocumentView | null = null; //gets document view that is in a freeform canvas collection DocumentManager.Instance.DocumentViews.map(view => { - let doc = view.props.Document; - - if (doc === toFind) { + if (view.props.Document === toFind) { toReturn = view; return; } - let docSrc = doc.GetT(KeyStore.Prototype, Document); - if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) { - toReturn = view; - } }); + if (!toReturn) { + DocumentManager.Instance.DocumentViews.map(view => { + let doc = view.props.Document.proto; + if (doc && Object.is(doc, toFind)) { + toReturn = view; + } + }); + } return toReturn; } - public getDocumentViews(toFind: Document): DocumentView[] { + public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; @@ -58,8 +58,8 @@ export class DocumentManager { if (doc === toFind) { toReturn.push(view); } else { - let docSrc = doc.GetT(KeyStore.Prototype, Document); - if (docSrc && docSrc !== FieldWaiting && Object.is(docSrc, toFind)) { + let docSrc = FieldValue(doc.proto); + if (docSrc && Object.is(docSrc, toFind)) { toReturn.push(view); } } @@ -71,20 +71,20 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { return DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => { - let linksList = dv.props.Document.GetT(KeyStore.LinkedToDocs, ListField); - if (linksList && linksList !== FieldWaiting && linksList.Data.length) { - pairs.push(...linksList.Data.reduce((pairs, link) => { - if (link instanceof Document) { - let linkToDoc = link.GetT(KeyStore.LinkedToDocs, Document); - if (linkToDoc && linkToDoc !== FieldWaiting) { + let linksList = Cast(dv.props.Document.linkedToDocs, listSpec(Doc)); + if (linksList && linksList.length) { + pairs.push(...linksList.reduce((pairs, link) => { + if (link) { + let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + if (linkToDoc) { DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => pairs.push({ a: dv, b: docView1, l: link })); } } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Document }[])); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Document }[]); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } }
\ No newline at end of file diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4bd654e15..a3dbe6e43 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,14 +1,14 @@ import { action } from "mobx"; -import { Document } from "../../fields/Document"; -import { FieldWaiting } from "../../fields/Field"; -import { KeyStore } from "../../fields/KeyStore"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { DocumentDecorations } from "../views/DocumentDecorations"; import * as globalCssVariables from "../views/globalCssVariables.scss"; import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; +import { Doc } from "../../new_fields/Doc"; +import { Cast } from "../../new_fields/Types"; +import { listSpec } from "../../new_fields/Schema"; -export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { +export type dropActionType = "alias" | "copy" | undefined; +export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -16,7 +16,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); - dragData.copyOnDrop = copyOnDrop; + dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); }); @@ -40,19 +40,19 @@ export function SetupDrag(_reference: React.RefObject<HTMLDivElement>, docFunc: return onItemDown; } -export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Document) { - let srcTarg = sourceDoc.GetT(KeyStore.Prototype, Document); - let draggedDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedToDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedToDocs, Document)) as Document) : []; - let draggedFromDocs = (srcTarg && srcTarg !== FieldWaiting) ? - srcTarg.GetList(KeyStore.LinkedFromDocs, [] as Document[]).map(linkDoc => - (linkDoc.GetT(KeyStore.LinkedFromDocs, Document)) as Document) : []; +export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { + let srcTarg = sourceDoc.proto; + let draggedDocs = srcTarg ? + Cast(srcTarg.linkedToDocs, listSpec(Doc), []).map(linkDoc => + Cast(linkDoc.linkedTo, Doc) as Doc) : []; + let draggedFromDocs = srcTarg ? + Cast(srcTarg.linkedFromDocs, listSpec(Doc), []).map(linkDoc => + Cast(linkDoc.linkedFrom, Doc) as Doc) : []; draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { - let moddrag = [] as Document[]; + let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { - let doc = await draggedDoc.GetTAsync(KeyStore.AnnotationOn, Document); + let doc = await Cast(draggedDoc.annotationOn, Doc); if (doc) moddrag.push(doc); } let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); @@ -134,35 +134,42 @@ export namespace DragManager { }; } - export type MoveFunction = (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; + export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; export class DocumentDragData { - constructor(dragDoc: Document[]) { + constructor(dragDoc: Doc[]) { this.draggedDocuments = dragDoc; this.droppedDocuments = dragDoc; this.xOffset = 0; this.yOffset = 0; } - draggedDocuments: Document[]; - droppedDocuments: Document[]; + draggedDocuments: Doc[]; + droppedDocuments: Doc[]; xOffset: number; yOffset: number; - aliasOnDrop?: boolean; - copyOnDrop?: boolean; + dropAction: dropActionType; + userDropAction: dropActionType; moveDocument?: MoveFunction; [id: string]: any; } export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options, - (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.aliasOnDrop ? dragData.draggedDocuments.map(d => d.CreateAlias()) : dragData.copyOnDrop ? dragData.draggedDocuments.map(d => d.Copy(true) as Document) : dragData.draggedDocuments)); + (dropData: { [id: string]: any }) => + (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ? + dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : + dragData.userDropAction == "copy" || (!dragData.userDropAction && dragData.dropAction == "copy") ? + dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : + dragData.draggedDocuments)); } export class LinkDragData { - constructor(linkSourceDoc: Document) { + constructor(linkSourceDoc: Doc, blacklist: Doc[] = []) { this.linkSourceDocument = linkSourceDoc; + this.blacklist = blacklist; } - droppedDocuments: Document[] = []; - linkSourceDocument: Document; + droppedDocuments: Doc[] = []; + linkSourceDocument: Doc; + blacklist: Doc[]; [id: string]: any; } @@ -170,10 +177,13 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } + export let AbortDrag: () => void = emptyFunction; + function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { if (!dragDiv) { dragDiv = document.createElement("div"); dragDiv.className = "dragManager-dragDiv"; + dragDiv.style.pointerEvents = "none"; DragManager.Root().appendChild(dragDiv); } MainOverlayTextBox.Instance.SetTextDoc(); @@ -183,7 +193,7 @@ export namespace DragManager { let xs: number[] = []; let ys: number[] = []; - const docs: Document[] = + const docs: Doc[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, @@ -247,10 +257,10 @@ export namespace DragManager { e.stopPropagation(); e.preventDefault(); if (dragData instanceof DocumentDragData) { - dragData.aliasOnDrop = e.ctrlKey || e.altKey; + dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } if (e.shiftKey) { - abortDrag(); + AbortDrag(); CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, @@ -269,31 +279,47 @@ export namespace DragManager { ); }; - const abortDrag = () => { + let hideDragElements = () => { + dragElements.map(dragElement => dragElement.parentNode == dragDiv && dragDiv.removeChild(dragElement)); + eles.map(ele => (ele.hidden = false)); + }; + let endDrag = () => { document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); - dragElements.map(dragElement => dragDiv.removeChild(dragElement)); - eles.map(ele => (ele.hidden = false)); + if (options) { + options.handlers.dragComplete({}); + } + } + + AbortDrag = () => { + hideDragElements(); + endDrag(); }; const upHandler = (e: PointerEvent) => { - abortDrag(); - FinishDrag(eles, e, dragData, options, finishDrag); + hideDragElements(); + dispatchDrag(eles, e, dragData, options, finishDrag); + endDrag(); }; document.addEventListener("pointermove", moveHandler, true); document.addEventListener("pointerup", upHandler); } - function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) { + function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) { let removed = dragEles.map(dragEle => { - let parent = dragEle.parentElement; - if (parent) parent.removeChild(dragEle); - return [dragEle, parent]; + // let parent = dragEle.parentElement; + // if (parent) parent.removeChild(dragEle); + let ret = [dragEle, dragEle.style.width, dragEle.style.height]; + dragEle.style.width = "0"; + dragEle.style.height = "0"; + return ret; }); const target = document.elementFromPoint(e.x, e.y); removed.map(r => { - let dragEle = r[0]; - let parent = r[1]; - if (parent && dragEle) parent.appendChild(dragEle); + let dragEle = r[0] as HTMLElement; + dragEle.style.width = r[1] as string; + dragEle.style.height = r[2] as string; + // let parent = r[1]; + // if (parent && dragEle) parent.appendChild(dragEle); }); if (target) { if (finishDrag) finishDrag(dragData); @@ -308,11 +334,6 @@ export namespace DragManager { } }) ); - - if (options) { - options.handlers.dragComplete({}); - } } - DocumentDecorations.Instance.Hidden = false; } } diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c67cc067a..e45f61c11 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,13 +1,5 @@ // import * as ts from "typescript" let ts = (window as any).ts; -import { Opt, Field } from "../../fields/Field"; -import { Document } from "../../fields/Document"; -import { NumberField } from "../../fields/NumberField"; -import { ImageField } from "../../fields/ImageField"; -import { TextField } from "../../fields/TextField"; -import { RichTextField } from "../../fields/RichTextField"; -import { KeyStore } from "../../fields/KeyStore"; -import { ListField } from "../../fields/ListField"; // // @ts-ignore // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // // @ts-ignore @@ -15,8 +7,11 @@ import { ListField } from "../../fields/ListField"; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Documents } from "../documents/Documents"; -import { Key } from "../../fields/Key"; +import { Docs } from "../documents/Documents"; +import { Doc, Field } from '../../new_fields/Doc'; +import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; +import { List } from '../../new_fields/List'; +import { RichTextField } from '../../new_fields/RichTextField'; export interface ScriptSucccess { success: true; @@ -50,9 +45,9 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an return { compiled: false, errors: diagnostics }; } - let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField, Key]; - let paramNames = ["KeyStore", "Documents", ...fieldTypes.map(fn => fn.name)]; - let params: any[] = [KeyStore, Documents, ...fieldTypes]; + let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField]; + let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; + let params: any[] = [Docs, ...fieldTypes]; let compiledFunction = new Function(...paramNames, `return ${script}`); let { capturedVariables = {} } = options; let run = (args: { [name: string]: any } = {}): ScriptResult => { @@ -171,17 +166,4 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics); return Run(outputText, paramNames, diagnostics, script, options); -} - -export function OrLiteralType(returnType: string): string { - return `${returnType} | string | number`; -} - -export function ToField(data: any): Opt<Field> { - if (typeof data === "string") { - return new TextField(data); - } else if (typeof data === "number") { - return new NumberField(data); - } - return undefined; }
\ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 320553952..fe5acf4b4 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,8 +1,7 @@ import { observable, action } from "mobx"; -import { DocumentView } from "../views/nodes/DocumentView"; -import { Document } from "../../fields/Document"; -import { Main } from "../views/Main"; +import { Doc } from "../../new_fields/Doc"; import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; +import { DocumentView } from "../views/nodes/DocumentView"; export namespace SelectionManager { class Manager { @@ -18,13 +17,13 @@ export namespace SelectionManager { if (manager.SelectedDocuments.indexOf(doc) === -1) { manager.SelectedDocuments.push(doc); - doc.props.onActiveChanged(true); + doc.props.whenActiveChanged(true); } } @action DeselectAll(): void { - manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)); + manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments = []; MainOverlayTextBox.Instance.SetTextDoc(); } @@ -36,7 +35,7 @@ export namespace SelectionManager { } @action ReselectAll2(sdocs: DocumentView[]) { - sdocs.map(s => SelectionManager.SelectDoc(s, false)); + sdocs.map(s => SelectionManager.SelectDoc(s, true)); } } @@ -50,7 +49,7 @@ export namespace SelectionManager { return manager.SelectedDocuments.indexOf(doc) !== -1; } - export function DeselectAll(except?: Document): void { + export function DeselectAll(except?: Doc): void { let found: DocumentView | undefined = undefined; if (except) { for (const view of manager.SelectedDocuments) { @@ -64,7 +63,7 @@ export namespace SelectionManager { export function ReselectAll() { let sdocs = manager.ReselectAll(); - manager.ReselectAll2(sdocs); + setTimeout(() => manager.ReselectAll2(sdocs), 0); } export function SelectedDocuments(): Array<DocumentView> { return manager.SelectedDocuments; diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 7273c3fe4..7ded85e43 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -1,5 +1,5 @@ import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema, primitive, SKIP } from "serializr"; -import { Field } from "../../fields/NewDoc"; +import { Field } from "../../new_fields/Doc"; export namespace SerializationHelper { let serializing: number = 0; @@ -8,8 +8,8 @@ export namespace SerializationHelper { } export function Serialize(obj: Field): any { - if (!obj) { - return null; + if (obj === undefined || obj === null) { + return undefined; } if (typeof obj !== 'object') { @@ -28,8 +28,8 @@ export namespace SerializationHelper { } export function Deserialize(obj: any): any { - if (!obj) { - return null; + if (obj === undefined || obj === null) { + return undefined; } if (typeof obj !== 'object') { @@ -55,14 +55,19 @@ let serializationTypes: { [name: string]: any } = {}; let reverseMap: { [ctor: string]: string } = {}; export interface DeserializableOpts { - (constructor: Function): void; + (constructor: { new(...args: any[]): any }): void; withFields(fields: string[]): Function; } export function Deserializable(name: string): DeserializableOpts; -export function Deserializable(constructor: Function): void; -export function Deserializable(constructor: Function | string): DeserializableOpts | void { - function addToMap(name: string, ctor: Function) { +export function Deserializable(constructor: { new(...args: any[]): any }): void; +export function Deserializable(constructor: { new(...args: any[]): any } | string): DeserializableOpts | void { + function addToMap(name: string, ctor: { new(...args: any[]): any }) { + const schema = getDefaultModelSchema(ctor) as any; + if (schema.targetClass !== ctor) { + const newSchema = { ...schema, factory: () => new ctor() }; + setDefaultModelSchema(ctor, newSchema); + } if (!(name in serializationTypes)) { serializationTypes[name] = ctor; reverseMap[ctor.name] = name; @@ -71,7 +76,7 @@ export function Deserializable(constructor: Function | string): DeserializableOp } } if (typeof constructor === "string") { - return Object.assign((ctor: Function) => { + return Object.assign((ctor: { new(...args: any[]): any }) => { addToMap(constructor, ctor); }, { withFields: Deserializable.withFields }); } diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 5c2d66480..70d9ad772 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -35,6 +35,10 @@ cursor: pointer; position: relative; padding-right: 15px; + margin: 3px; + background: #333333; + border-radius: 3px; + text-align: center; } .ProseMirror-menu-dropdown-wrap { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index a92cbd263..68a73375e 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -11,7 +11,8 @@ import React = require("react"); import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); import { library } from '@fortawesome/fontawesome-svg-core'; -import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list'; +import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list'; +import { liftTarget } from 'prosemirror-transform'; import { faListUl, } from '@fortawesome/free-solid-svg-icons'; @@ -24,16 +25,22 @@ const SVG = "http://www.w3.org/2000/svg"; //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { - private tooltip: HTMLElement; + public tooltip: HTMLElement; private num_icons = 0; private view: EditorView; private fontStyles: MarkType[]; private fontSizes: MarkType[]; + private listTypes: NodeType[]; private editorProps: FieldViewProps; private state: EditorState; private fontSizeToNum: Map<MarkType, number>; private fontStylesToName: Map<MarkType, string>; + private listTypeToIcon: Map<NodeType, string>; private fontSizeIndicator: HTMLSpanElement = document.createElement("span"); + //dropdown doms + private fontSizeDom?: Node; + private fontStyleDom?: Node; + private listTypeBtnDom?: Node; constructor(view: EditorView, editorProps: FieldViewProps) { this.view = view; @@ -55,8 +62,9 @@ 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") }, - { command: lift, dom: this.icon("<", "lift") }, + // { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, + // { command: wrapInList(schema.nodes.ordered_list), dom: this.icon("1)", "bullets") }, + // { command: lift, dom: this.icon("<", "lift") }, ]; //add menu items items.forEach(({ dom, command }) => { @@ -76,7 +84,7 @@ export class TooltipTextMenu { this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman"); this.fontStylesToName.set(schema.marks.arial, "Arial"); this.fontStylesToName.set(schema.marks.georgia, "Georgia"); - this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans"); + this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans MS"); this.fontStylesToName.set(schema.marks.tahoma, "Tahoma"); this.fontStylesToName.set(schema.marks.impact, "Impact"); this.fontStylesToName.set(schema.marks.crimson, "Crimson Text"); @@ -93,38 +101,75 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p72, 72); this.fontSizes = Array.from(this.fontSizeToNum.keys()); - this.addFontDropdowns(); + //list types + this.listTypeToIcon = new Map(); + this.listTypeToIcon.set(schema.nodes.bullet_list, ":"); + this.listTypeToIcon.set(schema.nodes.ordered_list, "1)"); + this.listTypes = Array.from(this.listTypeToIcon.keys()); this.update(view, undefined); } - //adds font size and font style dropdowns - addFontDropdowns() { + //label of dropdown will change to given label + updateFontSizeDropdown(label: string) { //filtering function - might be unecessary let cut = (arr: MenuItem[]) => arr.filter(x => x); + + //font SIZES + let fontSizeBtns: MenuItem[] = []; + this.fontSizeToNum.forEach((number, mark) => { + fontSizeBtns.push(this.dropdownMarkBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes)); + }); + + if (this.fontSizeDom) { this.tooltip.removeChild(this.fontSizeDom); } + this.fontSizeDom = (new Dropdown(cut(fontSizeBtns), { + label: label, + css: "color:white; min-width: 60px; padding-left: 5px; margin-right: 0;" + }) as MenuItem).render(this.view).dom; + this.tooltip.appendChild(this.fontSizeDom); + } + + //label of dropdown will change to given label + updateFontStyleDropdown(label: string) { + //filtering function - might be unecessary + let cut = (arr: MenuItem[]) => arr.filter(x => x); + //font STYLES let fontBtns: MenuItem[] = []; this.fontStylesToName.forEach((name, mark) => { - fontBtns.push(this.dropdownBtn(name, "font-family: " + name + ", sans-serif; width: 120px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles)); + fontBtns.push(this.dropdownMarkBtn(name, "font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles)); }); - //font size indicator - this.fontSizeIndicator = this.icon("12", "font-size-indicator"); + if (this.fontStyleDom) { this.tooltip.removeChild(this.fontStyleDom); } + this.fontStyleDom = (new Dropdown(cut(fontBtns), { + label: label, + css: "color:white; width: 125px; margin-left: -3px; padding-left: 2px;" + }) as MenuItem).render(this.view).dom; - //font SIZES - let fontSizeBtns: MenuItem[] = []; - this.fontSizeToNum.forEach((number, mark) => { - fontSizeBtns.push(this.dropdownBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes)); + this.tooltip.appendChild(this.fontStyleDom); + } + + //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown + updateListItemDropdown(label: string, listTypeBtn: Node) { + //remove old btn + if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); } + + //Make a dropdown of all list types + let toAdd: MenuItem[] = []; + this.listTypeToIcon.forEach((icon, type) => { + toAdd.push(this.dropdownNodeBtn(icon, "width: 40px;", type, this.view, this.listTypes, this.changeToNodeType)); }); + //option to remove the list formatting + toAdd.push(this.dropdownNodeBtn("X", "width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType)); + + listTypeBtn = (new Dropdown(toAdd, { + label: label, + css: "color:white; width: 40px;" + }) as MenuItem).render(this.view).dom; - //dropdown to hold font btns - let dd_fontStyle = new Dropdown(cut(fontBtns), { label: "Font Style", css: "color:white;" }) as MenuItem; - let dd_fontSize = new Dropdown(cut(fontSizeBtns), { label: "Font Size", css: "color:white; margin-left: -6px;" }) as MenuItem; - this.tooltip.appendChild(dd_fontStyle.render(this.view).dom); - this.tooltip.appendChild(this.fontSizeIndicator); - this.tooltip.appendChild(dd_fontSize.render(this.view).dom); - dd_fontStyle.render(this.view).dom.nodeValue = "TEST"; - console.log(dd_fontStyle.render(this.view).dom.nodeValue); + //add this new button and return it + this.tooltip.appendChild(listTypeBtn); + return listTypeBtn; } //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text @@ -158,9 +203,18 @@ export class TooltipTextMenu { return toggleMark(markType)(view.state, view.dispatch, view); } - //makes a button for the drop down + //remove all node typeand apply the passed-in one to the selected text + changeToNodeType(nodeType: NodeType | undefined, view: EditorView, allNodes: NodeType[]) { + //remove old + liftListItem(schema.nodes.list_item)(view.state, view.dispatch); + if (nodeType) { //add new + wrapInList(nodeType)(view.state, view.dispatch); + } + } + + //makes a button for the drop down FOR MARKS //css is the style you want applied to the button - dropdownBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) { + dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) { return new MenuItem({ title: "", label: label, @@ -173,6 +227,23 @@ export class TooltipTextMenu { } }); } + + //makes a button for the drop down FOR NODE TYPES + //css is the style you want applied to the button + dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) { + return new MenuItem({ + title: "", + label: label, + execEvent: "", + class: "menuicon", + css: css, + enable(state) { return true; }, + run() { + changeToNodeInGroup(nodeType, view, groupNodes); + } + }); + } + // Helper function to create menu icons icon(text: string, name: string) { let span = document.createElement("span"); @@ -246,34 +317,39 @@ export class TooltipTextMenu { let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; let mid = Math.min(start.left, end.left) + width; - this.tooltip.style.width = 220 + "px"; + this.tooltip.style.width = 225 + "px"; this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; + //UPDATE LIST ITEM DROPDOWN + this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); + + //UPDATE FONT STYLE DROPDOWN let activeStyles = this.activeMarksOnSelection(this.fontStyles); if (activeStyles.length === 1) { // if we want to update something somewhere with active font name let fontName = this.fontStylesToName.get(activeStyles[0]); + if (fontName) { this.updateFontStyleDropdown(fontName); } } else if (activeStyles.length === 0) { //crimson on default + this.updateFontStyleDropdown("Crimson Text"); + } else { + this.updateFontStyleDropdown("Various"); } - //update font size indicator + //UPDATE FONT SIZE DROPDOWN let activeSizes = this.activeMarksOnSelection(this.fontSizes); if (activeSizes.length === 1) { //if there's only one active font size let size = this.fontSizeToNum.get(activeSizes[0]); - if (size) { - this.fontSizeIndicator.innerHTML = String(size); - } - //should be 14 on default + if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } } else if (activeSizes.length === 0) { - this.fontSizeIndicator.innerHTML = "14"; - //multiple font sizes selected - } else { - this.fontSizeIndicator.innerHTML = ""; + //should be 14 on default + this.updateFontSizeDropdown("14 pt"); + } else { //multiple font sizes selected + this.updateFontSizeDropdown("Various"); } } - //finds all active marks on selection + //finds all active marks on selection in given group activeMarksOnSelection(markGroup: MarkType[]) { //current selection let { empty, $cursor, ranges } = this.view.state.selection as TextSelection; diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 27aed4bac..0b5280c4a 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -1,4 +1,4 @@ -import { observable, action } from "mobx"; +import { observable, action, runInAction } from "mobx"; import 'source-map-support/register'; import { Without } from "../../Utils"; import { string } from "prop-types"; @@ -140,10 +140,11 @@ export namespace UndoManager { } }); - export function RunInBatch(fn: () => void, batchName: string) { + //TODO Make this return the return value + export function RunInBatch<T>(fn: () => T, batchName: string) { let batch = StartBatch(batchName); try { - fn(); + return runInAction(fn); } finally { batch.end(); } |