From a72fcdd0ebc06a3c851007c6ed89ab13a9a0d835 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 8 Apr 2019 18:07:13 -0400 Subject: fixed up a bunch of small things + started on text wackiness. --- src/client/util/DocumentManager.ts | 2 +- src/client/util/DragManager.ts | 3 +- src/client/util/SelectionManager.ts | 2 + src/client/views/InkingCanvas.scss | 3 + src/client/views/Main.scss | 23 +- src/client/views/Main.tsx | 66 ++-- .../CollectionFreeFormLinksView.tsx | 91 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 + src/client/views/nodes/FormattedTextBox.tsx | 338 ++++++++++++--------- 9 files changed, 313 insertions(+), 217 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index bf59fbb43..341959936 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -29,7 +29,7 @@ export class DocumentManager { public getAllDocumentViews(collection: Document) { return this.DocumentViews.filter(dv => - dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document == collection); + dv.props.ContainingCollectionView && dv.props.ContainingCollectionView.props.Document.Id === collection.Id); } public getDocumentView(toFind: Document): DocumentView | null { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c1919093e..0ee7ed2b3 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,7 +1,5 @@ import { action } from "mobx"; import { Document } from "../../fields/Document"; -import { ImageField } from "../../fields/ImageField"; -import { KeyStore } from "../../fields/KeyStore"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionView } from "../views/collections/CollectionView"; import { DocumentDecorations } from "../views/DocumentDecorations"; @@ -18,6 +16,7 @@ export function setupDrag( e.stopPropagation(); e.preventDefault(); + // TODO: bcz -- this needs to have a drag threshold so that it doesn't trigger when just selecting. document.removeEventListener("pointermove", onRowMove); document.removeEventListener("pointerup", onRowUp); var dragData = new DragManager.DocumentDragData([docFunc()]); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 958c14491..79d4ceb25 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,7 @@ import { observable, action } from "mobx"; import { DocumentView } from "../views/nodes/DocumentView"; import { Document } from "../../fields/Document"; +import { Main } from "../views/Main"; export namespace SelectionManager { class Manager { @@ -40,6 +41,7 @@ export namespace SelectionManager { } manager.SelectedDocuments.length = 0; if (found) manager.SelectedDocuments.push(found); + Main.Instance.SetTextDoc(undefined, undefined); } export function SelectedDocuments(): Array { diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index c5a2a50cb..42ae38c73 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -1,5 +1,8 @@ @import "global_variables"; +.inkingCanvas { + opacity:0.99; +} .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect { position: absolute; top: 0; diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 594803aab..fe7f007b0 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -4,7 +4,7 @@ html, body { width: 100%; height: 100%; - overflow: hidden; + overflow: auto; font-family: $sans-serif; margin: 0; position:absolute; @@ -157,12 +157,33 @@ button:hover { cursor: pointer; } } +#root { + overflow: visible; +} #main-div { width:100%; height:100%; position:absolute; top: 0; left:0; + overflow: scroll; +} +.mainDiv-textInput { + background:pink; + width: 200px; + height: 200px; + position:absolute; + overflow: visible; + top: 0; + left: 0; + .formattedTextBox-cont { + background:pink; + width: 100%; + height: 100%; + position:absolute; + top: 0; + left: 0; + } } #mainContent-div { width:100%; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index b4ab9e21d..c9467e130 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,7 +1,7 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction } from 'mobx'; +import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -36,6 +36,7 @@ import { DocumentDecorations } from './DocumentDecorations'; import { InkingControl } from './InkingControl'; import "./Main.scss"; import { DocumentView } from './nodes/DocumentView'; +import { FormattedTextBox } from './nodes/FormattedTextBox'; @observer export class Main extends React.Component { @@ -203,6 +204,30 @@ export class Main extends React.Component { focusDocument = (doc: Document) => { } noScaling = () => 1; + @observable _textDoc?: Document = undefined; + _textRect: any; + @action + SetTextDoc(textDoc?: Document, div?: HTMLDivElement) { + this._textDoc = undefined; + this._textDoc = textDoc; + if (div) + this._textRect = div.getBoundingClientRect(); + } + + @computed + get activeTextBox() { + if (this._textDoc) { + let x: number = this._textRect.x; + let y: number = this._textRect.y; + let w: number = this._textRect.width; + let h: number = this._textRect.height; + return
+ true} select={() => { }} isTopMost={true} selectOnLoad={true} bindings={undefined} /> + + } + else return (null); + } + @computed get mainContent() { return !this.mainContainer ? (null) : @@ -296,24 +321,27 @@ export class Main extends React.Component { isShown={this.areWorkspacesShown} toggle={this.toggleWorkspaces} /> } return ( -
- - runInAction(() => { - this.pwidth = r.entry.width; - this.pheight = r.entry.height; - })}> - {({ measureRef }) => -
- {this.mainContent} -
- } -
- - {this.nodesMenu} - {this.miscButtons} - {workspaceMenu} - -
+ [ +
+ + runInAction(() => { + this.pwidth = r.entry.width; + this.pheight = r.entry.height; + })}> + {({ measureRef }) => +
+ {this.mainContent} +
+ } +
+ + {this.nodesMenu} + {this.miscButtons} + {workspaceMenu} + +
, + this.activeTextBox + ] ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index d4809ac1c..1189dd4e8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,4 +1,4 @@ -import { computed, reaction, runInAction, trace } from "mobx"; +import { computed, reaction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../../fields/Document"; import { FieldWaiting } from "../../../../fields/Field"; @@ -11,59 +11,60 @@ import { CollectionViewProps } from "../CollectionViewBase"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import v5 = require("uuid/v5"); -import { find } from "async"; @observer export class CollectionFreeFormLinksView extends React.Component { + HackToAvoidReactionFiringUnnecessarily?: Document = undefined; componentDidMount() { - reaction(() => { - return DocumentManager.Instance.getAllDocumentViews(this.props.Document).map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)) - }, () => { - let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document); - for (let i = 0; i < views.length; i++) { - for (let j = 0; j < views.length; j++) { - let srcDoc = views[j].props.Document; - let dstDoc = views[i].props.Document; - let x1 = srcDoc.GetNumber(KeyStore.X, 0); - let x1w = srcDoc.GetNumber(KeyStore.Width, -1); - let x2 = dstDoc.GetNumber(KeyStore.X, 0); - let x2w = dstDoc.GetNumber(KeyStore.Width, -1); - if (x1w < 0 || x2w < 0 || i == j) - continue; - let dstTarg = dstDoc; - let srcTarg = srcDoc; - let findBrush = (field: ListField) => field.Data.findIndex(brush => { - let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : []; - return (bdocs.length && ((bdocs[0] == dstTarg && bdocs[1] == srcTarg)) ? true : false) - }); - let brushAction = (field: ListField) => { - let found = findBrush(field); - if (found != -1) { - console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title); - field.Data.splice(found, 1); - } - }; - if (Math.abs(x1 + x1w - x2) < 20) { - let linkDoc: Document = new Document(); - linkDoc.SetText(KeyStore.Title, "Histogram Brush"); - linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title); - linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField); - - brushAction = brushAction = (field: ListField) => { - if (findBrush(field) == -1) { - console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title); - (findBrush(field) == -1) && field.Data.push(linkDoc); + this.HackToAvoidReactionFiringUnnecessarily = this.props.Document + reaction(() => + DocumentManager.Instance.getAllDocumentViews(this.HackToAvoidReactionFiringUnnecessarily!). + map(dv => dv.props.Document.GetNumber(KeyStore.X, 0)), + () => { + let views = DocumentManager.Instance.getAllDocumentViews(this.props.Document); + for (let i = 0; i < views.length; i++) { + for (let j = 0; j < views.length; j++) { + let srcDoc = views[j].props.Document; + let dstDoc = views[i].props.Document; + let x1 = srcDoc.GetNumber(KeyStore.X, 0); + let x1w = srcDoc.GetNumber(KeyStore.Width, -1); + let x2 = dstDoc.GetNumber(KeyStore.X, 0); + let x2w = dstDoc.GetNumber(KeyStore.Width, -1); + if (x1w < 0 || x2w < 0 || i == j) + continue; + let dstTarg = dstDoc; + let srcTarg = srcDoc; + let findBrush = (field: ListField) => field.Data.findIndex(brush => { + let bdocs = brush ? brush.GetList(KeyStore.BrushingDocs, [] as Document[]) : []; + return (bdocs.length && ((bdocs[0] == dstTarg && bdocs[1] == srcTarg)) ? true : false) + }); + let brushAction = (field: ListField) => { + let found = findBrush(field); + if (found != -1) { + console.log("REMOVE BRUSH " + srcTarg.Title + " " + dstTarg.Title); + field.Data.splice(found, 1); } }; - } - dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); - srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); + if (Math.abs(x1 + x1w - x2) < 20) { + let linkDoc: Document = new Document(); + linkDoc.SetText(KeyStore.Title, "Histogram Brush"); + linkDoc.SetText(KeyStore.LinkDescription, "Brush between " + srcTarg.Title + " and " + dstTarg.Title); + linkDoc.SetData(KeyStore.BrushingDocs, [dstTarg, srcTarg], ListField); + brushAction = brushAction = (field: ListField) => { + if (findBrush(field) == -1) { + console.log("ADD BRUSH " + srcTarg.Title + " " + dstTarg.Title); + (findBrush(field) == -1) && field.Data.push(linkDoc); + } + }; + } + dstTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); + srcTarg.GetOrCreateAsync(KeyStore.BrushingDocs, ListField, brushAction); + + } } - } - }) + }) } documentAnchors(view: DocumentView) { let equalViews = [view]; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1ddb84a99..2a86d0ee1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -21,6 +21,7 @@ import v5 = require("uuid/v5"); import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import { PreviewCursor } from "./PreviewCursor"; import { NumberField } from "../../../../fields/NumberField"; +import { Main } from "../../Main"; @observer export class CollectionFreeFormView extends CollectionViewBase { @@ -185,6 +186,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @action private SetPan(panX: number, panY: number) { + Main.Instance.SetTextDoc(undefined, undefined); var x1 = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY)); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0c0efc437..e59179874 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -2,19 +2,19 @@ import { action, IReactionDisposer, reaction } from "mobx"; import { baseKeymap } from "prosemirror-commands"; import { history, redo, undo } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; -import { schema } from "../../util/RichTextSchema"; -import { EditorState, Transaction } from "prosemirror-state"; +import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { Opt, FieldWaiting } from "../../../fields/Field"; -import "./FormattedTextBox.scss"; -import React = require("react"); +import { FieldWaiting, Opt } from "../../../fields/Field"; +import { KeyStore } from "../../../fields/KeyStore"; import { RichTextField } from "../../../fields/RichTextField"; -import { FieldViewProps, FieldView } from "./FieldView"; -import { Plugin } from "prosemirror-state"; -import { Decoration, DecorationSet } from "prosemirror-view"; +import { inpRules } from "../../util/RichTextRules"; +import { schema } from "../../util/RichTextSchema"; import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { ContextMenu } from "../../views/ContextMenu"; -import { inpRules } from "../../util/RichTextRules"; +import { Main } from "../Main"; +import { FieldView, FieldViewProps } from "./FieldView"; +import "./FormattedTextBox.scss"; +import React = require("react"); const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); @@ -35,156 +35,196 @@ const { menuBar } = require("prosemirror-menu"); // this will edit the document and assign the new value to that field. //] export class FormattedTextBox extends React.Component { - public static LayoutString(fieldStr: string = "DataKey") { - return FieldView.LayoutString(FormattedTextBox, fieldStr); - } - private _ref: React.RefObject; - private _editorView: Opt; - private _reactionDisposer: Opt; - - constructor(props: FieldViewProps) { - super(props); - - this._ref = React.createRef(); - this.onChange = this.onChange.bind(this); - } - - dispatchTransaction = (tx: Transaction) => { - if (this._editorView) { - const state = this._editorView.state.apply(tx); - this._editorView.updateState(state); - const { doc, fieldKey } = this.props; - doc.SetDataOnPrototype( - fieldKey, - JSON.stringify(state.toJSON()), - RichTextField - ); - // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); + public static LayoutString(fieldStr: string = "DataKey") { + return FieldView.LayoutString(FormattedTextBox, fieldStr); + } + private _ref: React.RefObject; + private _editorView: Opt; + private _reactionDisposer: Opt; + private _inputReactionDisposer: Opt; + + constructor(props: FieldViewProps) { + super(props); + + this._ref = React.createRef(); + this.onChange = this.onChange.bind(this); } - }; - - componentDidMount() { - let state: EditorState; - const config = { - schema, - inpRules, //these currently don't do anything, but could eventually be helpful - plugins: [ - history(), - keymap({ "Mod-z": undo, "Mod-y": redo }), - keymap(baseKeymap), - this.tooltipMenuPlugin() - ] + + dispatchTransaction = (tx: Transaction) => { + if (this._editorView) { + const state = this._editorView.state.apply(tx); + this._editorView.updateState(state); + this.FieldDoc.SetDataOnPrototype( + this.FieldKey, + JSON.stringify(state.toJSON()), + RichTextField + ); + // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField); + } }; - let field = this.props.doc.GetT(this.props.fieldKey, RichTextField); - if (field && field != FieldWaiting && field.Data) { - state = EditorState.fromJSON(config, JSON.parse(field.Data)); - } else { - state = EditorState.create(config); + get FieldDoc() { return this.props.fieldKey === KeyStore.Archives ? Main.Instance._textDoc! : this.props.doc; } + get FieldKey() { return this.props.fieldKey === KeyStore.Archives ? KeyStore.Data : this.props.fieldKey; } + + componentDidMount() { + const config = { + schema, + inpRules, //these currently don't do anything, but could eventually be helpful + plugins: [ + history(), + keymap({ "Mod-z": undo, "Mod-y": redo }), + keymap(baseKeymap), + this.tooltipMenuPlugin() + ] + }; + + if (this.props.fieldKey === KeyStore.Archives) { + this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id, + () => { + if (this._editorView) + this._editorView!.destroy(); + + this.setupEditor(config); + } + ) + } + + this._reactionDisposer = reaction( + () => { + const field = this.FieldDoc.GetT(this.FieldKey, RichTextField); + return field && field != FieldWaiting ? field.Data : undefined; + }, + field => { + if (field && this._editorView) { + this._editorView.updateState( + EditorState.fromJSON(config, JSON.parse(field)) + ); + } + } + ); + this.setupEditor(config); + } + + private setupEditor(config: any) { + + let state: EditorState; + let field = this.FieldDoc.GetT(this.FieldKey, RichTextField); + if (field && field != FieldWaiting && field.Data) { + state = EditorState.fromJSON(config, JSON.parse(field.Data)); + } else { + state = EditorState.create(config); + } + if (this._ref.current) { + this._editorView = new EditorView(this._ref.current, { + state, + dispatchTransaction: this.dispatchTransaction + }); + } + + if (this.props.selectOnLoad) { + this.props.select(); + this._editorView!.focus(); + } } - if (this._ref.current) { - this._editorView = new EditorView(this._ref.current, { - state, - dispatchTransaction: this.dispatchTransaction - }); + + componentWillUnmount() { + if (this._editorView) { + this._editorView.destroy(); + } + if (this._reactionDisposer) { + this._reactionDisposer(); + } + if (this._inputReactionDisposer) { + this._inputReactionDisposer(); + } + } + + shouldComponentUpdate() { + return false; + } + + @action + onChange(e: React.ChangeEvent) { + const { fieldKey, doc } = this.props; + doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value)); + // doc.SetData(fieldKey, e.target.value, RichTextField); } + onPointerDown = (e: React.PointerEvent): void => { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + e.stopPropagation(); + } + if (e.buttons === 1 && this.props.fieldKey !== KeyStore.Archives) + e.preventDefault(); + }; + onPointerUp = (e: React.PointerEvent): void => { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + e.stopPropagation(); + } + if (this.props.fieldKey !== KeyStore.Archives) { + e.preventDefault(); + Main.Instance.SetTextDoc(this.props.doc, this._ref.current!); + } + }; - this._reactionDisposer = reaction( - () => { - const field = this.props.doc.GetT(this.props.fieldKey, RichTextField); - return field && field != FieldWaiting ? field.Data : undefined; - }, - field => { - if (field && this._editorView) { - this._editorView.updateState( - EditorState.fromJSON(config, JSON.parse(field)) - ); + onFocused = (e: React.FocusEvent): void => { + if (this.props.fieldKey !== KeyStore.Archives) { + Main.Instance.SetTextDoc(this.props.doc, this._ref.current!); } - } - ); - if (this.props.selectOnLoad) { - this.props.select(); - this._editorView!.focus(); } - } - componentWillUnmount() { - if (this._editorView) { - this._editorView.destroy(); + //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE + textCapability = (e: React.MouseEvent): void => { }; + + specificContextMenu = (e: React.MouseEvent): void => { + ContextMenu.Instance.addItem({ + description: "Text Capability", + event: this.textCapability + }); + + // ContextMenu.Instance.addItem({ + // description: "Submenu", + // items: [ + // { + // description: "item 1", event: + // }, + // { + // description: "item 2", event: + // } + // ] + // }) + // e.stopPropagation() + }; + + onPointerWheel = (e: React.WheelEvent): void => { + e.stopPropagation(); + }; + + tooltipMenuPlugin() { + return new Plugin({ + view(_editorView) { + return new TooltipTextMenu(_editorView); + } + }); } - if (this._reactionDisposer) { - this._reactionDisposer(); + onKeyPress(e: React.KeyboardEvent) { + 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; } - } - - shouldComponentUpdate() { - return false; - } - - @action - onChange(e: React.ChangeEvent) { - const { fieldKey, doc } = this.props; - doc.SetOnPrototype(fieldKey, new RichTextField(e.target.value)); - // doc.SetData(fieldKey, e.target.value, RichTextField); - } - onPointerDown = (e: React.PointerEvent): void => { - if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { - e.stopPropagation(); + render() { + return ( +
+ ); } - }; - - //REPLACE THIS WITH CAPABILITIES SPECIFIC TO THIS TYPE OF NODE - textCapability = (e: React.MouseEvent): void => {}; - - specificContextMenu = (e: React.MouseEvent): void => { - ContextMenu.Instance.addItem({ - description: "Text Capability", - event: this.textCapability - }); - - // ContextMenu.Instance.addItem({ - // description: "Submenu", - // items: [ - // { - // description: "item 1", event: - // }, - // { - // description: "item 2", event: - // } - // ] - // }) - // e.stopPropagation() - }; - - onPointerWheel = (e: React.WheelEvent): void => { - e.stopPropagation(); - }; - - tooltipMenuPlugin() { - return new Plugin({ - view(_editorView) { - return new TooltipTextMenu(_editorView); - } - }); - } - onKeyPress(e: React.KeyboardEvent) { - 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; - } - render() { - return ( -
- ); - } } -- cgit v1.2.3-70-g09d2