From e0b3c759880639bf56f9b8b39ea2e38c5cbad8a6 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 10 Apr 2019 14:28:25 -0400 Subject: fixed extra rendering for collections. --- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index beca6cdc6..bb1bc4fb0 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -161,13 +161,13 @@ export class FormattedTextBox extends React.Component { } if (this.props.fieldKey !== KeyStore.Archives) { e.preventDefault(); - Main.Instance.SetTextDoc(this.props.Document, this._ref.current!); + Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); } } onFocused = (e: React.FocusEvent): void => { if (this.props.fieldKey !== KeyStore.Archives) { - Main.Instance.SetTextDoc(this.props.Document, this._ref.current!); + Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); } } -- cgit v1.2.3-70-g09d2 From d4326a225f3e801c98e4acdc6558af1c3fe6ae07 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 10 Apr 2019 15:18:10 -0400 Subject: improved text editing. --- src/client/util/DragManager.ts | 2 ++ src/client/views/DocumentDecorations.tsx | 2 ++ src/client/views/Main.scss | 4 ++-- src/client/views/Main.tsx | 18 +++++++++++++++--- src/client/views/nodes/DocumentView.tsx | 2 -- src/client/views/nodes/FormattedTextBox.tsx | 10 +++++++--- 6 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f7395578f..b8a35a4d1 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -5,6 +5,7 @@ import { CollectionView } from "../views/collections/CollectionView"; import { DocumentDecorations } from "../views/DocumentDecorations"; import { DocumentView } from "../views/nodes/DocumentView"; import { returnFalse, emptyFunction } from "../../Utils"; +import { Main } from "../views/Main"; export function setupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { let onRowMove = action((e: PointerEvent): void => { @@ -147,6 +148,7 @@ export namespace DragManager { dragDiv.className = "dragManager-dragDiv"; DragManager.Root().appendChild(dragDiv); } + Main.Instance.SetTextDoc(undefined, undefined, undefined); let scaleXs: number[] = []; let scaleYs: number[] = []; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b7bf727f1..fa521b7e2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -16,6 +16,7 @@ import { LinkMenu } from "./nodes/LinkMenu"; import React = require("react"); import { FieldWaiting } from "../../fields/Field"; import { emptyFunction } from "../../Utils"; +import { Main } from "./Main"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -321,6 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } + Main.Instance.SetTextDoc(undefined, undefined, undefined); SelectionManager.SelectedDocuments().forEach(element => { const rect = element.screenRect(); if (rect.width !== 0) { diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 8ef6b83f1..c4da47e0e 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -169,7 +169,7 @@ button:hover { overflow: scroll; } .mainDiv-textInput { - background-color: rgba(248, 6, 6, 0.1); + background-color: rgba(248, 6, 6, 0.001); width: 200px; height: 200px; position:absolute; @@ -177,7 +177,7 @@ button:hover { top: 0; left: 0; .formattedTextBox-cont { - background-color: rgba(248, 6, 6, 0.1); + background-color: rgba(248, 6, 6, 0.001); width: 100%; height: 100%; position:absolute; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 2751b1899..e96e4cbf8 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -66,6 +66,7 @@ export class Main extends React.Component { constructor(props: Readonly<{}>) { super(props); + this._textProxyDiv = React.createRef(); Main.Instance = this; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); @@ -205,13 +206,25 @@ export class Main extends React.Component { @observable _textDoc?: Document = undefined; _textRect: any; _textXf: Transform = Transform.Identity(); + _textScroll: number = 0; + _textTargetDiv: HTMLDivElement | undefined; + _textProxyDiv: React.RefObject; @action SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) { this._textDoc = undefined; this._textDoc = textDoc; this._textXf = tx ? tx : Transform.Identity(); + this._textTargetDiv = div; if (div) { this._textRect = div.getBoundingClientRect(); + this._textScroll = div.scrollTop; + } + } + + @action + textScroll = (e: React.UIEvent) => { + if (this._textProxyDiv.current && this._textTargetDiv) { + this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop } } @@ -225,9 +238,8 @@ export class Main extends React.Component { let t = this._textXf.transformPoint(0, 0); let s = this._textXf.transformPoint(1, 0); s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); - return
-
- + return
+
{ }} />
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 42967eb22..6b585ec4b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -381,7 +381,6 @@ export class DocumentView extends React.Component { @computed get nativeHeight(): number { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } @computed get contents() { - trace(); return ( { if (!this.props.Document) { return null; } - trace(); var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index bb1bc4fb0..eb40a03a5 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -92,7 +92,7 @@ export class FormattedTextBox extends React.Component { this._reactionDisposer = reaction( () => { - const field = this.FieldDoc.GetT(this.FieldKey, RichTextField); + const field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined; return field && field !== FieldWaiting ? field.Data : undefined; }, field => { @@ -107,9 +107,8 @@ export class FormattedTextBox extends React.Component { } private setupEditor(config: any) { - let state: EditorState; - let field = this.FieldDoc.GetT(this.FieldKey, RichTextField); + let field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined; if (field && field !== FieldWaiting && field.Data) { state = EditorState.fromJSON(config, JSON.parse(field.Data)); } else { @@ -168,6 +167,10 @@ export class FormattedTextBox extends React.Component { onFocused = (e: React.FocusEvent): void => { if (this.props.fieldKey !== KeyStore.Archives) { Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); + } else { + if (this._ref.current) { + this._ref.current.scrollTop = Main.Instance._textScroll; + } } } @@ -219,6 +222,7 @@ export class FormattedTextBox extends React.Component { onKeyPress={this.onKeyPress} onPointerUp={this.onPointerUp} onPointerDown={this.onPointerDown} + onFocus={this.onFocused} onContextMenu={this.specificContextMenu} // tfs: do we need this event handler onWheel={this.onPointerWheel} -- cgit v1.2.3-70-g09d2 From 4e45ad641bd34c4703188e69a93a23243f3659a4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 10 Apr 2019 21:35:12 -0400 Subject: fixed text menu and text dragging. --- src/client/util/TooltipTextMenu.tsx | 14 +++++++++----- src/client/views/DocumentDecorations.scss | 3 +++ src/client/views/Main.scss | 1 + src/client/views/Main.tsx | 23 +++++++++++++++++++---- src/client/views/nodes/FormattedTextBox.tsx | 13 +++++++++---- 5 files changed, 41 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index bd5753093..7951e5686 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -12,19 +12,23 @@ const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); import { library } from '@fortawesome/fontawesome-svg-core'; import { wrapInList, bulletList } from 'prosemirror-schema-list'; import { faListUl } from '@fortawesome/free-solid-svg-icons'; +import { FieldViewProps } from "../views/nodes/FieldView"; +import { throwStatement } from "babel-types"; //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { private tooltip: HTMLElement; + private editorProps: FieldViewProps; - constructor(view: EditorView) { + constructor(view: EditorView, editorProps: FieldViewProps) { + this.editorProps = editorProps; this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; //add the div which is the tooltip - view.dom.parentNode!.appendChild(this.tooltip); + view.dom.parentNode!.parentNode!.appendChild(this.tooltip); //add additional icons library.add(faListUl); @@ -124,13 +128,13 @@ export class TooltipTextMenu { // Find a center-ish x position from the selection endpoints (when // 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; + this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; + let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; 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.bottom = (box.bottom - start.top) + "px"; + this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; } destroy() { this.tooltip.remove(); } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index c4e4aed8e..b2096bccb 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -1,5 +1,8 @@ @import "global_variables"; +.documentDecorations { + position: absolute; +} #documentDecorations-container { position: absolute; top: 0; diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index c4da47e0e..a68b90dd4 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -176,6 +176,7 @@ button:hover { overflow: visible; top: 0; left: 0; + z-index: 1000; .formattedTextBox-cont { background-color: rgba(248, 6, 6, 0.001); width: 100%; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index e96e4cbf8..b73b55fb1 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -28,7 +28,7 @@ import '../northstar/model/ModelExtensions'; import { HistogramOperation } from '../northstar/operations/HistogramOperation'; import '../northstar/utils/Extensions'; import { Server } from '../Server'; -import { setupDrag } from '../util/DragManager'; +import { setupDrag, DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; @@ -38,6 +38,7 @@ import { InkingControl } from './InkingControl'; import "./Main.scss"; import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/FormattedTextBox'; +import { REPLCommand } from 'repl'; @observer export class Main extends React.Component { @@ -228,6 +229,21 @@ export class Main extends React.Component { } } + textBoxDown = (e: React.PointerEvent) => { + let dragData = new DragManager.DocumentDragData([this._textDoc!]); + const [left, top] = this._textXf + .inverse() + .transformPoint(0, 0); + dragData.xOffset = e.clientX - left; + dragData.yOffset = e.clientY - top; + DragManager.StartDocumentDrag([this._textTargetDiv!], dragData, e.clientX, e.clientY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + @computed get activeTextBox() { if (this._textDoc) { @@ -239,9 +255,8 @@ export class Main extends React.Component { let s = this._textXf.transformPoint(1, 0); s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); return
-
- { }} /> - +
+ this._textXf} focus={(doc) => { }} />
; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index eb40a03a5..69086df42 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -70,12 +70,16 @@ export class FormattedTextBox extends React.Component { const config = { schema, inpRules, //these currently don't do anything, but could eventually be helpful - plugins: [ + plugins: this.props.fieldKey === KeyStore.Archives ? [ history(), keymap({ "Mod-z": undo, "Mod-y": redo }), keymap(baseKeymap), this.tooltipMenuPlugin() - ] + ] : [ + history(), + keymap({ "Mod-z": undo, "Mod-y": redo }), + keymap(baseKeymap), + ] }; if (this.props.fieldKey === KeyStore.Archives) { @@ -150,7 +154,7 @@ export class FormattedTextBox extends React.Component { // doc.SetData(fieldKey, e.target.value, RichTextField); } onPointerDown = (e: React.PointerEvent): void => { - if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + if (e.buttons === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } } @@ -202,9 +206,10 @@ export class FormattedTextBox extends React.Component { } tooltipMenuPlugin() { + let myprops = this.props; return new Plugin({ view(_editorView) { - return new TooltipTextMenu(_editorView); + return new TooltipTextMenu(_editorView, myprops); } }); } -- cgit v1.2.3-70-g09d2 From a006717f7a847c1a230a334fa645b7382aa067d2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 10 Apr 2019 23:44:10 -0400 Subject: switched text proxy to appear on selection, not pointerdown --- src/client/views/nodes/FormattedTextBox.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 69086df42..17125a298 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -42,6 +42,7 @@ export class FormattedTextBox extends React.Component { private _editorView: Opt; private _reactionDisposer: Opt; private _inputReactionDisposer: Opt; + private _proxyReactionDisposer: Opt; constructor(props: FieldViewProps) { super(props); @@ -92,6 +93,11 @@ export class FormattedTextBox extends React.Component { this.setupEditor(config); } ); + } else { + this._proxyReactionDisposer = reaction(() => this.props.isSelected(), + () => + this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()) + ); } this._reactionDisposer = reaction( @@ -141,6 +147,9 @@ export class FormattedTextBox extends React.Component { if (this._inputReactionDisposer) { this._inputReactionDisposer(); } + if (this._proxyReactionDisposer) { + this._proxyReactionDisposer(); + } } shouldComponentUpdate() { @@ -154,7 +163,7 @@ export class FormattedTextBox extends React.Component { // doc.SetData(fieldKey, e.target.value, RichTextField); } onPointerDown = (e: React.PointerEvent): void => { - if (e.buttons === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } } @@ -162,10 +171,6 @@ export class FormattedTextBox extends React.Component { if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } - if (this.props.fieldKey !== KeyStore.Archives) { - e.preventDefault(); - Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); - } } onFocused = (e: React.FocusEvent): void => { -- cgit v1.2.3-70-g09d2 From e5411598c4d2ecc7a0b4d8584b576ff5f45ad2a0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 11 Apr 2019 01:05:12 -0400 Subject: tried to fix some context menu and other conflicting interactions --- src/client/util/SelectionManager.ts | 2 +- src/client/views/Main.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 --- src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/FormattedTextBox.scss | 1 + src/client/views/nodes/FormattedTextBox.tsx | 12 ++++++++---- 6 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index c6b8c7b0d..2638e3b7d 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -25,6 +25,7 @@ export namespace SelectionManager { DeselectAll(): void { manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)); manager.SelectedDocuments = []; + Main.Instance.SetTextDoc(undefined, undefined, undefined); } } @@ -48,7 +49,6 @@ export namespace SelectionManager { manager.DeselectAll(); if (found) manager.SelectDoc(found, false); - Main.Instance.SetTextDoc(undefined, undefined, undefined); } export function SelectedDocuments(): Array { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 8153250c7..b9e209b3c 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -274,8 +274,8 @@ export class Main extends React.Component { let t = this._textXf.transformPoint(0, 0); let s = this._textXf.transformPoint(1, 0); s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); - return
-
+ return
+
this._textXf} focus={(doc) => { }} />
; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index db8c370df..4e1164ee5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -135,9 +135,6 @@ export class CollectionFreeFormView extends CollectionSubView { document.addEventListener("pointerup", this.onPointerUp); this._lastX = this.DownX = e.pageX; this._lastY = this.DownY = e.pageY; - if (this.props.isSelected()) { - e.stopPropagation(); - } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6b585ec4b..1465d589c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -105,7 +105,7 @@ export class DocumentView extends React.Component { } e.stopPropagation(); } else { - if (this.active && !e.isDefaultPrevented()) { + if (this.active) { e.stopPropagation(); document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); @@ -186,6 +186,7 @@ export class DocumentView extends React.Component { document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); if (!SelectionManager.IsSelected(this) && + e.button != 2 && Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4 ) { diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 32da2632e..d2ba52cf9 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -22,6 +22,7 @@ overflow-x: hidden; color: initial; height: 100%; + pointer-events: all; } .menuicon { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 17125a298..14b22e9f5 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -14,6 +14,7 @@ import { ContextMenu } from "../../views/ContextMenu"; import { Main } from "../Main"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; +import { emptyFunction } from '../../../Utils'; import React = require("react"); const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); @@ -95,8 +96,10 @@ export class FormattedTextBox extends React.Component { ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => - this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()) + () => { + if (this.props.isSelected()) + Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); + } ); } @@ -166,6 +169,8 @@ export class FormattedTextBox extends React.Component { if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } + if (e.button === 2) + e.preventDefault(); } onPointerUp = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { @@ -227,12 +232,11 @@ export class FormattedTextBox extends React.Component { render() { return (
Date: Thu, 11 Apr 2019 08:51:39 -0400 Subject: fixed zoom not always rendering. fixed documents (images, pdfs) not appearing. fixed lint warnings. --- src/client/views/Main.tsx | 7 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 +++++++----- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 9 +++------ 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index b9e209b3c..5fd778c7e 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -213,8 +213,9 @@ export class Main extends React.Component { _textProxyDiv: React.RefObject; @action SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) { - if (this._textTargetDiv) + if (this._textTargetDiv) { this._textTargetDiv.style.color = this._textColor; + } this._textDoc = undefined; this._textDoc = textDoc; @@ -231,12 +232,12 @@ export class Main extends React.Component { @action textScroll = (e: React.UIEvent) => { if (this._textProxyDiv.current && this._textTargetDiv) { - this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop + this._textTargetDiv.scrollTop = this._textScroll = this._textProxyDiv.current.children[0].scrollTop; } } textBoxDown = (e: React.PointerEvent) => { - if (e.button != 0 || e.metaKey || e.altKey) { + if (e.button !== 0 || e.metaKey || e.altKey) { document.addEventListener("pointermove", this.textBoxMove); document.addEventListener('pointerup', this.textBoxUp); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4e1164ee5..b40e36e77 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -304,10 +304,12 @@ export class CollectionFreeFormView extends CollectionSubView { childViews = () => this.views; render() { - let [dx, dy] = [this.centeringShiftX, this.centeringShiftY]; - + const [dx, dy] = [this.centeringShiftX, this.centeringShiftY]; const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0); const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0); + const zoom: number = this.zoomScaling; + const blay = this.backgroundView; + const olay = this.overlayView; return ( runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}> @@ -323,8 +325,8 @@ export class CollectionFreeFormView extends CollectionSubView {
- {this.backgroundView} + style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}> + {blay} {this.childViews} @@ -332,7 +334,7 @@ export class CollectionFreeFormView extends CollectionSubView {
- {this.overlayView} + {olay}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1465d589c..9c31a83c1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -186,7 +186,7 @@ export class DocumentView extends React.Component { document.removeEventListener("pointerup", this.onPointerUp); e.stopPropagation(); if (!SelectionManager.IsSelected(this) && - e.button != 2 && + e.button !== 2 && Math.abs(e.clientX - this._downX) < 4 && Math.abs(e.clientY - this._downY) < 4 ) { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 14b22e9f5..468cbcd1e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -96,11 +96,7 @@ export class FormattedTextBox extends React.Component { ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => { - if (this.props.isSelected()) - Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); - } - ); + () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform())); } this._reactionDisposer = reaction( @@ -169,8 +165,9 @@ export class FormattedTextBox extends React.Component { if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } - if (e.button === 2) + if (e.button === 2) { e.preventDefault(); + } } onPointerUp = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { -- cgit v1.2.3-70-g09d2 From 50be8cb7a93110821c972c679567ddb6aae8bc6f Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 11 Apr 2019 11:56:40 -0400 Subject: made css globals avabile from code. cleaned up formattedText overlay api.. --- src/client/util/DragManager.ts | 12 +- src/client/util/SelectionManager.ts | 2 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/Main.tsx | 7 +- src/client/views/_global_variables.scss | 2 +- src/client/views/_global_variables.scss.d.ts | 11 +- src/client/views/_global_variables.ts | 8 + .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 31 ++-- tsconfig.json | 42 ++--- webpack.config.js | 180 ++++++++++----------- 11 files changed, 154 insertions(+), 145 deletions(-) create mode 100644 src/client/views/_global_variables.ts (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e500b0274..d66c6e90f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,12 +1,12 @@ import { action } from "mobx"; import { Document } from "../../fields/Document"; +import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { CollectionView } from "../views/collections/CollectionView"; import { DocumentDecorations } from "../views/DocumentDecorations"; -import { DocumentView } from "../views/nodes/DocumentView"; -import { returnFalse, emptyFunction } from "../../Utils"; import { Main } from "../views/Main"; -import globalStyles from '../views/_global_variables.scss'; +import { DocumentView } from "../views/nodes/DocumentView"; +import globalStyles from "../views/_global_variables"; +// import globalStyleVariables from "../views/_global_variables.scss"; // bcz: why doesn't this work? export function setupDrag(_reference: React.RefObject, docFunc: () => Document, moveFunc?: DragManager.MoveFunction, copyOnDrop: boolean = false) { let onRowMove = action((e: PointerEvent): void => { @@ -149,7 +149,7 @@ export namespace DragManager { dragDiv.className = "dragManager-dragDiv"; DragManager.Root().appendChild(dragDiv); } - Main.Instance.SetTextDoc(undefined, undefined, undefined); + Main.Instance.SetTextDoc(); let scaleXs: number[] = []; let scaleYs: number[] = []; @@ -178,7 +178,7 @@ export namespace DragManager { dragElement.style.bottom = ""; dragElement.style.left = "0"; dragElement.style.transformOrigin = "0 0"; - dragElement.style.zIndex = "1000";// globalStyles.contextMenuZindex.toString(); + dragElement.style.zIndex = globalStyles.contextMenuZindex;// "1000"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; dragElement.style.width = `${rect.width / scaleX}px`; dragElement.style.height = `${rect.height / scaleY}px`; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 2638e3b7d..2fa45a086 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -25,7 +25,7 @@ export namespace SelectionManager { DeselectAll(): void { manager.SelectedDocuments.map(dv => dv.props.onActiveChanged(false)); manager.SelectedDocuments = []; - Main.Instance.SetTextDoc(undefined, undefined, undefined); + Main.Instance.SetTextDoc(); } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index fa521b7e2..29cca286d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -322,7 +322,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } - Main.Instance.SetTextDoc(undefined, undefined, undefined); + Main.Instance.SetTextDoc(); SelectionManager.SelectedDocuments().forEach(element => { const rect = element.screenRect(); if (rect.width !== 0) { diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 5fd778c7e..c4c4a6bf9 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -39,6 +39,7 @@ import "./Main.scss"; import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/FormattedTextBox'; import { REPLCommand } from 'repl'; +import { Key } from '../../fields/Key'; @observer export class Main extends React.Component { @@ -208,17 +209,19 @@ export class Main extends React.Component { _textRect: any; _textXf: Transform = Transform.Identity(); _textScroll: number = 0; + _textFieldKey: Key = KeyStore.Data; _textColor: string | null = null; _textTargetDiv: HTMLDivElement | undefined; _textProxyDiv: React.RefObject; @action - SetTextDoc(textDoc?: Document, div?: HTMLDivElement, tx?: Transform) { + SetTextDoc(textDoc?: Document, textFieldKey?: Key, div?: HTMLDivElement, tx?: Transform) { if (this._textTargetDiv) { this._textTargetDiv.style.color = this._textColor; } this._textDoc = undefined; this._textDoc = textDoc; + this._textFieldKey = textFieldKey!; this._textXf = tx ? tx : Transform.Identity(); this._textTargetDiv = div; if (div) { @@ -277,7 +280,7 @@ export class Main extends React.Component { s[0] = Math.sqrt((s[0] - t[0]) * (s[0] - t[0]) + (s[1] - t[1]) * (s[1] - t[1])); return
- this._textXf} focus={(doc) => { }} /> + this._textXf} focus={(doc) => { }} />
; } diff --git a/src/client/views/_global_variables.scss b/src/client/views/_global_variables.scss index 238351a77..cd6af2dac 100644 --- a/src/client/views/_global_variables.scss +++ b/src/client/views/_global_variables.scss @@ -24,5 +24,5 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? :export { - contextMenuZindex: $contextMenu-zindex + contextMenuZindex: $contextMenu-zindex; } \ No newline at end of file diff --git a/src/client/views/_global_variables.scss.d.ts b/src/client/views/_global_variables.scss.d.ts index 12008aeef..c902d473f 100644 --- a/src/client/views/_global_variables.scss.d.ts +++ b/src/client/views/_global_variables.scss.d.ts @@ -1,10 +1,7 @@ + export interface I_globalScss { - contextMenuZindex: number; // context menu shows up over everything - mainTextInputZindex: number; // then text input overlay so that it's context menu will appear over decorations, etc - docDecorationsZindex: number; // then doc decorations appear over everything else - remoteCursorsZindex: number; // ... not sure what level the remote cursors should go -- is this right? + contextMenuZindex: string; // context menu shows up over everything } +export const globalStyleVariables: I_globalScss; -export const globalStyles: I_globalScss; - -export default globalStyles; \ No newline at end of file +export default globalStyleVariables; \ No newline at end of file diff --git a/src/client/views/_global_variables.ts b/src/client/views/_global_variables.ts new file mode 100644 index 000000000..e70bfd56c --- /dev/null +++ b/src/client/views/_global_variables.ts @@ -0,0 +1,8 @@ +import * as globalStyleVariables from "../views/_global_variables.scss" + +export interface I_globalScss { + contextMenuZindex: string; // context menu shows up over everything +} +let globalStyles = globalStyleVariables as any as I_globalScss; + +export default globalStyles; \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b40e36e77..01ebbe0e1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -198,7 +198,7 @@ export class CollectionFreeFormView extends CollectionSubView { @action private SetPan(panX: number, panY: number) { - Main.Instance.SetTextDoc(undefined, undefined, undefined); + Main.Instance.SetTextDoc(); 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 468cbcd1e..8ea747b1c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -5,7 +5,6 @@ import { keymap } from "prosemirror-keymap"; import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { FieldWaiting, Opt } from "../../../fields/Field"; -import { KeyStore } from "../../../fields/KeyStore"; import { RichTextField } from "../../../fields/RichTextField"; import { inpRules } from "../../util/RichTextRules"; import { schema } from "../../util/RichTextSchema"; @@ -14,7 +13,6 @@ import { ContextMenu } from "../../views/ContextMenu"; import { Main } from "../Main"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; -import { emptyFunction } from '../../../Utils'; import React = require("react"); const { buildMenuItems } = require("prosemirror-example-setup"); const { menuBar } = require("prosemirror-menu"); @@ -35,7 +33,12 @@ const { menuBar } = require("prosemirror-menu"); // specified Key and assigns it to an HTML input node. When changes are made to this node, // this will edit the document and assign the new value to that field. //] -export class FormattedTextBox extends React.Component { + +export interface FormattedTextBoxOverlay { + isOverlay?: boolean; +} + +export class FormattedTextBox extends React.Component<(FieldViewProps & FormattedTextBoxOverlay)> { public static LayoutString(fieldStr: string = "DataKey") { return FieldView.LayoutString(FormattedTextBox, fieldStr); } @@ -56,8 +59,8 @@ export class FormattedTextBox extends React.Component { if (this._editorView) { const state = this._editorView.state.apply(tx); this._editorView.updateState(state); - this.FieldDoc.SetDataOnPrototype( - this.FieldKey, + this.props.Document.SetDataOnPrototype( + this.props.fieldKey, JSON.stringify(state.toJSON()), RichTextField ); @@ -65,14 +68,11 @@ export class FormattedTextBox extends React.Component { } } - 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; } - componentDidMount() { const config = { schema, inpRules, //these currently don't do anything, but could eventually be helpful - plugins: this.props.fieldKey === KeyStore.Archives ? [ + plugins: this.props.isOverlay ? [ history(), keymap({ "Mod-z": undo, "Mod-y": redo }), keymap(baseKeymap), @@ -84,7 +84,7 @@ export class FormattedTextBox extends React.Component { ] }; - if (this.props.fieldKey === KeyStore.Archives) { + if (this.props.isOverlay) { this._inputReactionDisposer = reaction(() => Main.Instance._textDoc && Main.Instance._textDoc.Id, () => { if (this._editorView) { @@ -96,12 +96,12 @@ export class FormattedTextBox extends React.Component { ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform())); + () => this.props.isSelected() && Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform())); } this._reactionDisposer = reaction( () => { - const field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined; + const field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined; return field && field !== FieldWaiting ? field.Data : undefined; }, field => { @@ -117,7 +117,7 @@ export class FormattedTextBox extends React.Component { private setupEditor(config: any) { let state: EditorState; - let field = this.FieldDoc ? this.FieldDoc.GetT(this.FieldKey, RichTextField) : undefined; + let field = this.props.Document ? this.props.Document.GetT(this.props.fieldKey, RichTextField) : undefined; if (field && field !== FieldWaiting && field.Data) { state = EditorState.fromJSON(config, JSON.parse(field.Data)); } else { @@ -176,8 +176,8 @@ export class FormattedTextBox extends React.Component { } onFocused = (e: React.FocusEvent): void => { - if (this.props.fieldKey !== KeyStore.Archives) { - Main.Instance.SetTextDoc(this.props.Document, this._ref.current!, this.props.ScreenToLocalTransform()); + if (!this.props.isOverlay) { + Main.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()); } else { if (this._ref.current) { this._ref.current.scrollTop = Main.Instance._textScroll; @@ -232,6 +232,7 @@ export class FormattedTextBox extends React.Component { className={`formattedTextBox-cont`} onKeyDown={this.onKeyPress} onKeyPress={this.onKeyPress} + onFocus={this.onFocused} onPointerUp={this.onPointerUp} onPointerDown={this.onPointerDown} onContextMenu={this.specificContextMenu} diff --git a/tsconfig.json b/tsconfig.json index 41db1d0a7..0d4d77002 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,23 @@ { - "compilerOptions": { - "target": "es5", - "removeComments": true, - "experimentalDecorators": true, - "strict": true, - "jsx": "react", - "sourceMap": true, - "outDir": "dist", - "lib": [ - "dom", - "es2015" - ], - }, - // "exclude": [ - // "node_modules", - // "static" - // ], - "typeRoots": [ - "./node_modules/@types", - "./src/typings" - ] + "compilerOptions": { + "target": "es5", + "removeComments": true, + "experimentalDecorators": true, + "strict": true, + "jsx": "react", + "sourceMap": true, + "outDir": "dist", + "lib": [ + "dom", + "es2015" + ], + }, + // "exclude": [ + // "node_modules", + // "static" + // ], + "typeRoots": [ + "./node_modules/@types", + "./src/typings" + ] } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 50079255f..574401807 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,95 +3,95 @@ var webpack = require('webpack'); const CopyWebpackPlugin = require("copy-webpack-plugin"); module.exports = { - mode: 'development', - entry: { - bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'], - viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'], - test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'], - inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'], - imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'], - }, - devtool: "source-map", - node: { - fs: 'empty', - module: 'empty', - dns: 'mock', - tls: 'mock', - net: 'mock' - }, - output: { - filename: "[name].js", - path: path.resolve(__dirname, "build"), - publicPath: "/" - }, - resolve: { - extensions: ['.js', '.ts', '.tsx'] - }, - module: { - rules: [ - { - test: [/\.tsx?$/, /\.ts?$/,], - enforce: 'pre', - use: [ - { - loader: "tslint-loader", - } - ] - }, { - test: [/\.tsx?$/, /\.ts?$/,], - loader: "awesome-typescript-loader", - include: path.join(__dirname, 'src') - }, - { - test: /\.scss|css$/, - use: [ - { - loader: "style-loader" - }, - { - loader: "css-loader" - }, - { - loader: "sass-loader" - } - ] - }, - { - test: /\.(jpg|png|pdf)$/, - use: [ - { - loader: 'file-loader' - } - ] - }, - { - test: /\.(png|jpg|gif)$/i, - use: [ - { - loader: 'url-loader', - options: { - limit: 8192 - } - } - ] - }] - }, - plugins: [ - new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]), - new webpack.optimize.OccurrenceOrderPlugin(), - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ], - devServer: { - compress: false, - host: "localhost", - contentBase: path.join(__dirname, 'deploy'), - port: 4321, - hot: true, - https: false, - overlay: { - warnings: true, - errors: true + mode: 'development', + entry: { + bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'], + viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'], + test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'], + inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'], + imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'], + }, + devtool: "source-map", + node: { + fs: 'empty', + module: 'empty', + dns: 'mock', + tls: 'mock', + net: 'mock' + }, + output: { + filename: "[name].js", + path: path.resolve(__dirname, "build"), + publicPath: "/" + }, + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + module: { + rules: [ + { + test: [/\.tsx?$/, /\.ts?$/,], + enforce: 'pre', + use: [ + { + loader: "tslint-loader", + } + ] + }, { + test: [/\.tsx?$/, /\.ts?$/,], + loader: "awesome-typescript-loader", + include: path.join(__dirname, 'src') + }, + { + test: /\.scss|css$/, + use: [ + { + loader: "style-loader" + }, + { + loader: "css-loader" + }, + { + loader: "sass-loader" + } + ] + }, + { + test: /\.(jpg|png|pdf)$/, + use: [ + { + loader: 'file-loader' + } + ] + }, + { + test: /\.(png|jpg|gif)$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: 8192 + } + } + ] + }] + }, + plugins: [ + new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]), + new webpack.optimize.OccurrenceOrderPlugin(), + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ], + devServer: { + compress: false, + host: "localhost", + contentBase: path.join(__dirname, 'deploy'), + port: 4321, + hot: true, + https: false, + overlay: { + warnings: true, + errors: true + } } - } }; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 15514b0f3d685764d1bd7ebeac9cdee1f778e184 Mon Sep 17 00:00:00 2001 From: laurawilsonri Date: Thu, 11 Apr 2019 12:06:46 -0400 Subject: font style and size dropdowns work --- package.json | 332 +++++++++--------- src/client/util/RichTextSchema.tsx | 507 ++++++++++++++++------------ src/client/util/TooltipTextMenu.scss | 238 ++++++++++++- src/client/util/TooltipTextMenu.tsx | 173 +++++++--- src/client/views/nodes/FormattedTextBox.tsx | 7 +- 5 files changed, 830 insertions(+), 427 deletions(-) (limited to 'src/client/views/nodes/FormattedTextBox.tsx') diff --git a/package.json b/package.json index 135bd4a91..11eb6f0f5 100644 --- a/package.json +++ b/package.json @@ -1,168 +1,168 @@ { - "name": "dash", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "start": "nodemon --watch src/server/**/*.ts --exec ts-node src/server/index.ts", - "build": "webpack --env production", - "test": "mocha -r ts-node/register test/**/*.ts", - "tsc": "tsc" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^5.2.6", - "@types/react-dom": "^16.8.2", - "@types/webpack-dev-middleware": "^2.0.2", - "@types/webpack-hot-middleware": "^2.16.4", - "awesome-typescript-loader": "^5.2.1", - "chai": "^4.2.0", - "copy-webpack-plugin": "^4.6.0", - "css-loader": "^2.1.1", - "file-loader": "^3.0.1", - "mocha": "^5.2.0", - "sass-loader": "^7.1.0", - "scss-loader": "0.0.1", - "style-loader": "^0.23.1", - "ts-node": "^7.0.1", - "typescript": "^3.4.1", - "webpack": "^4.29.6", - "webpack-cli": "^3.2.3", - "webpack-dev-middleware": "^3.6.1", - "webpack-dev-server": "^3.2.1", - "webpack-hot-middleware": "^2.24.3" - }, - "dependencies": { - "@fortawesome/fontawesome-free-solid": "^5.0.13", - "@fortawesome/fontawesome-svg-core": "^1.2.15", - "@fortawesome/free-solid-svg-icons": "^5.7.2", - "@fortawesome/react-fontawesome": "^0.1.4", - "@hig/flyout": "^1.0.3", - "@hig/theme-context": "^2.1.3", - "@hig/theme-data": "^2.3.3", - "@trendmicro/react-dropdown": "^1.3.0", - "@types/async": "^2.4.1", - "@types/bcrypt-nodejs": "0.0.30", - "@types/bluebird": "^3.5.25", - "@types/body-parser": "^1.17.0", - "@types/connect-flash": "0.0.34", - "@types/cookie-parser": "^1.4.1", - "@types/cookie-session": "^2.0.36", - "@types/d3-format": "^1.3.1", - "@types/express": "^4.16.1", - "@types/express-flash": "0.0.0", - "@types/express-session": "^1.15.12", - "@types/express-validator": "^3.0.0", - "@types/formidable": "^1.0.31", - "@types/jquery": "^3.3.29", - "@types/jsonwebtoken": "^8.3.2", - "@types/lodash": "^4.14.121", - "@types/mobile-detect": "^1.3.4", - "@types/mongodb": "^3.1.22", - "@types/mongoose": "^5.3.21", - "@types/node": "^10.12.30", - "@types/nodemailer": "^4.6.6", - "@types/passport": "^1.0.0", - "@types/passport-local": "^1.0.33", - "@types/prosemirror-commands": "^1.0.1", - "@types/prosemirror-history": "^1.0.1", - "@types/prosemirror-inputrules": "^1.0.2", - "@types/prosemirror-keymap": "^1.0.1", - "@types/prosemirror-menu": "^1.0.1", - "@types/prosemirror-model": "^1.7.0", - "@types/prosemirror-schema-basic": "^1.0.1", - "@types/prosemirror-schema-list": "^1.0.1", - "@types/prosemirror-state": "^1.2.3", - "@types/prosemirror-transform": "^1.1.0", - "@types/prosemirror-view": "^1.3.1", - "@types/pug": "^2.0.4", - "@types/react": "^16.8.7", - "@types/react-color": "^2.14.1", - "@types/react-measure": "^2.0.4", - "@types/react-table": "^6.7.22", - "@types/request": "^2.48.1", - "@types/request-promise": "^4.1.42", - "@types/socket.io": "^2.1.2", - "@types/socket.io-client": "^1.4.32", - "@types/typescript": "^2.0.0", - "@types/uuid": "^3.4.4", - "@types/webpack": "^4.4.25", - "async": "^2.6.2", - "babel-runtime": "^6.26.0", - "bcrypt-nodejs": "0.0.3", - "bluebird": "^3.5.3", - "body-parser": "^1.18.3", - "bootstrap": "^4.3.1", - "class-transformer": "^0.2.0", - "connect-flash": "^0.1.1", - "connect-mongo": "^2.0.3", - "cookie-parser": "^1.4.4", - "cookie-session": "^2.0.0-beta.3", - "crypto-browserify": "^3.11.0", - "d3-format": "^1.3.2", - "express": "^4.16.4", - "express-flash": "0.0.2", - "express-session": "^1.15.6", - "express-validator": "^5.3.1", - "expressjs": "^1.0.1", - "flexlayout-react": "^0.3.3", - "font-awesome": "^4.7.0", - "formidable": "^1.2.1", - "golden-layout": "^1.5.9", - "html-to-image": "^0.1.0", - "i": "^0.3.6", - "jsonwebtoken": "^8.5.0", - "jsx-to-string": "^1.4.0", - "lodash": "^4.17.11", - "mobile-detect": "^1.4.3", - "mobx": "^5.9.0", - "mobx-react": "^5.3.5", - "mobx-react-devtools": "^6.1.1", - "mongodb": "^3.1.13", - "mongoose": "^5.4.18", - "node-sass": "^4.11.0", - "nodemailer": "^5.1.1", - "nodemon": "^1.18.10", - "normalize.css": "^8.0.1", - "npm": "^6.9.0", - "passport": "^0.4.0", - "passport-local": "^1.0.0", - "prosemirror-commands": "^1.0.7", - "prosemirror-example-setup": "^1.0.1", - "prosemirror-history": "^1.0.4", - "prosemirror-keymap": "^1.0.1", - "prosemirror-model": "^1.7.0", - "prosemirror-schema-basic": "^1.0.0", - "prosemirror-schema-list": "^1.0.2", - "prosemirror-state": "^1.2.2", - "prosemirror-transform": "^1.1.3", - "prosemirror-view": "^1.8.3", - "pug": "^2.0.3", - "raw-loader": "^1.0.0", - "react": "^16.8.4", - "react-bootstrap": "^1.0.0-beta.5", - "react-bootstrap-dropdown-menu": "^1.1.15", - "react-color": "^2.17.0", - "react-dimensions": "^1.3.1", - "react-dom": "^16.8.4", - "react-golden-layout": "^1.0.6", - "react-image-lightbox": "^5.1.0", - "react-jsx-parser": "^1.15.0", - "react-measure": "^2.2.4", - "react-mosaic": "0.0.20", - "react-pdf": "^4.0.2", - "react-pdf-highlighter": "^2.1.2", - "react-pdf-js": "^4.0.2", - "react-simple-dropdown": "^3.2.3", - "react-split-pane": "^0.1.85", - "react-table": "^6.9.2", - "request": "^2.88.0", - "request-promise": "^4.2.4", - "socket.io": "^2.2.0", - "socket.io-client": "^2.2.0", - "typescript-collections": "^1.3.2", - "url-loader": "^1.1.2", - "uuid": "^3.3.2", - "xoauth2": "^1.2.0" - } + "name": "dash", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "nodemon --watch src/server/**/*.ts --exec ts-node src/server/index.ts", + "build": "webpack --env production", + "test": "mocha -r ts-node/register test/**/*.ts", + "tsc": "tsc" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.6", + "@types/react-dom": "^16.8.2", + "@types/webpack-dev-middleware": "^2.0.2", + "@types/webpack-hot-middleware": "^2.16.4", + "awesome-typescript-loader": "^5.2.1", + "chai": "^4.2.0", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^2.1.1", + "file-loader": "^3.0.1", + "mocha": "^5.2.0", + "sass-loader": "^7.1.0", + "scss-loader": "0.0.1", + "style-loader": "^0.23.1", + "ts-node": "^7.0.1", + "typescript": "^3.4.1", + "webpack": "^4.29.6", + "webpack-cli": "^3.2.3", + "webpack-dev-middleware": "^3.6.1", + "webpack-dev-server": "^3.2.1", + "webpack-hot-middleware": "^2.24.3" + }, + "dependencies": { + "@fortawesome/fontawesome-free-solid": "^5.0.13", + "@fortawesome/fontawesome-svg-core": "^1.2.15", + "@fortawesome/free-solid-svg-icons": "^5.7.2", + "@fortawesome/react-fontawesome": "^0.1.4", + "@hig/flyout": "^1.0.3", + "@hig/theme-context": "^2.1.3", + "@hig/theme-data": "^2.3.3", + "@trendmicro/react-dropdown": "^1.3.0", + "@types/async": "^2.4.1", + "@types/bcrypt-nodejs": "0.0.30", + "@types/bluebird": "^3.5.25", + "@types/body-parser": "^1.17.0", + "@types/connect-flash": "0.0.34", + "@types/cookie-parser": "^1.4.1", + "@types/cookie-session": "^2.0.36", + "@types/d3-format": "^1.3.1", + "@types/express": "^4.16.1", + "@types/express-flash": "0.0.0", + "@types/express-session": "^1.15.12", + "@types/express-validator": "^3.0.0", + "@types/formidable": "^1.0.31", + "@types/jquery": "^3.3.29", + "@types/jsonwebtoken": "^8.3.2", + "@types/lodash": "^4.14.121", + "@types/mobile-detect": "^1.3.4", + "@types/mongodb": "^3.1.22", + "@types/mongoose": "^5.3.21", + "@types/node": "^10.12.30", + "@types/nodemailer": "^4.6.6", + "@types/passport": "^1.0.0", + "@types/passport-local": "^1.0.33", + "@types/prosemirror-commands": "^1.0.1", + "@types/prosemirror-history": "^1.0.1", + "@types/prosemirror-inputrules": "^1.0.2", + "@types/prosemirror-keymap": "^1.0.1", + "@types/prosemirror-menu": "^1.0.1", + "@types/prosemirror-model": "^1.7.0", + "@types/prosemirror-schema-basic": "^1.0.1", + "@types/prosemirror-schema-list": "^1.0.1", + "@types/prosemirror-state": "^1.2.3", + "@types/prosemirror-transform": "^1.1.0", + "@types/prosemirror-view": "^1.3.1", + "@types/pug": "^2.0.4", + "@types/react": "^16.8.7", + "@types/react-color": "^2.14.1", + "@types/react-measure": "^2.0.4", + "@types/react-table": "^6.7.22", + "@types/request": "^2.48.1", + "@types/request-promise": "^4.1.42", + "@types/socket.io": "^2.1.2", + "@types/socket.io-client": "^1.4.32", + "@types/typescript": "^2.0.0", + "@types/uuid": "^3.4.4", + "@types/webpack": "^4.4.25", + "async": "^2.6.2", + "babel-runtime": "^6.26.0", + "bcrypt-nodejs": "0.0.3", + "bluebird": "^3.5.3", + "body-parser": "^1.18.3", + "bootstrap": "^4.3.1", + "class-transformer": "^0.2.0", + "connect-flash": "^0.1.1", + "connect-mongo": "^2.0.3", + "cookie-parser": "^1.4.4", + "cookie-session": "^2.0.0-beta.3", + "crypto-browserify": "^3.11.0", + "d3-format": "^1.3.2", + "express": "^4.16.4", + "express-flash": "0.0.2", + "express-session": "^1.15.6", + "express-validator": "^5.3.1", + "expressjs": "^1.0.1", + "flexlayout-react": "^0.3.3", + "font-awesome": "^4.7.0", + "formidable": "^1.2.1", + "golden-layout": "^1.5.9", + "html-to-image": "^0.1.0", + "i": "^0.3.6", + "jsonwebtoken": "^8.5.0", + "jsx-to-string": "^1.4.0", + "lodash": "^4.17.11", + "mobile-detect": "^1.4.3", + "mobx": "^5.9.0", + "mobx-react": "^5.3.5", + "mobx-react-devtools": "^6.1.1", + "mongodb": "^3.1.13", + "mongoose": "^5.4.18", + "node-sass": "^4.11.0", + "nodemailer": "^5.1.1", + "nodemon": "^1.18.10", + "normalize.css": "^8.0.1", + "npm": "^6.9.0", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "prosemirror-commands": "^1.0.7", + "prosemirror-example-setup": "^1.0.1", + "prosemirror-history": "^1.0.4", + "prosemirror-keymap": "^1.0.1", + "prosemirror-model": "^1.7.0", + "prosemirror-schema-basic": "^1.0.0", + "prosemirror-schema-list": "^1.0.2", + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.1.3", + "prosemirror-view": "^1.8.3", + "pug": "^2.0.3", + "raw-loader": "^1.0.0", + "react": "^16.8.4", + "react-bootstrap": "^1.0.0-beta.5", + "react-bootstrap-dropdown-menu": "^1.1.15", + "react-color": "^2.17.0", + "react-dimensions": "^1.3.1", + "react-dom": "^16.8.4", + "react-golden-layout": "^1.0.6", + "react-image-lightbox": "^5.1.0", + "react-jsx-parser": "^1.15.0", + "react-measure": "^2.2.4", + "react-mosaic": "0.0.20", + "react-pdf": "^4.0.2", + "react-pdf-highlighter": "^2.1.2", + "react-pdf-js": "^4.0.2", + "react-simple-dropdown": "^3.2.3", + "react-split-pane": "^0.1.85", + "react-table": "^6.9.2", + "request": "^2.88.0", + "request-promise": "^4.2.4", + "socket.io": "^2.2.0", + "socket.io-client": "^2.2.0", + "typescript-collections": "^1.3.2", + "url-loader": "^1.1.2", + "uuid": "^3.3.2", + "xoauth2": "^1.2.0" + } } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 489c22a57..765ac0ae3 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -7,134 +7,134 @@ 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] + 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 } = { - // :: NodeSpec The top level document node. - doc: { - content: "block+" - }, - - // :: NodeSpec A plain paragraph textblock. Represented in the DOM - // as a `

` element. - paragraph: { - content: "inline*", - group: "block", - parseDOM: [{ tag: "p" }], - toDOM() { return pDOM } - }, - - // :: NodeSpec A blockquote (`

`) wrapping one or more blocks. - blockquote: { - content: "block+", - group: "block", - defining: true, - parseDOM: [{ tag: "blockquote" }], - toDOM() { return blockquoteDOM } - }, - - // :: NodeSpec A horizontal rule (`
`). - horizontal_rule: { - group: "block", - parseDOM: [{ tag: "hr" }], - toDOM() { return hrDOM } - }, - - // :: NodeSpec A heading textblock, with a `level` attribute that - // should hold the number 1 to 6. Parsed and serialized as `

` to - // `

` elements. - heading: { - attrs: { level: { default: 1 } }, - content: "inline*", - group: "block", - defining: true, - parseDOM: [{ tag: "h1", attrs: { level: 1 } }, - { tag: "h2", attrs: { level: 2 } }, - { tag: "h3", attrs: { level: 3 } }, - { tag: "h4", attrs: { level: 4 } }, - { tag: "h5", attrs: { level: 5 } }, - { tag: "h6", attrs: { level: 6 } }], - toDOM(node: any) { return ["h" + node.attrs.level, 0] } - }, - - // :: NodeSpec A code listing. Disallows marks or non-text inline - // nodes by default. Represented as a `
` element with a
-  // `` element inside of it.
-  code_block: {
-    content: "text*",
-    marks: "",
-    group: "block",
-    code: true,
-    defining: true,
-    parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
-    toDOM() { return preDOM }
-  },
-
-  // :: NodeSpec The text node.
-  text: {
-    group: "inline"
-  },
-
-  // :: NodeSpec An inline image (``) node. Supports `src`,
-  // `alt`, and `href` attributes. The latter two default to the empty
-  // string.
-  image: {
-    inline: true,
-    attrs: {
-      src: {},
-      alt: { default: null },
-      title: { default: null }
-    },
-    group: "inline",
-    draggable: true,
-    parseDOM: [{
-      tag: "img[src]", getAttrs(dom: any) {
-        return {
-          src: dom.getAttribute("src"),
-          title: dom.getAttribute("title"),
-          alt: dom.getAttribute("alt")
-        }
-      }
-    }],
-    toDOM(node: any) { return ["img", node.attrs] }
-  },
-
-  // :: NodeSpec A hard line break, represented in the DOM as `
`. - hard_break: { - inline: true, - group: "inline", - selectable: false, - parseDOM: [{ tag: "br" }], - toDOM() { return brDOM } - }, - - ordered_list: { - ...orderedList, - 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 } - }, - //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*' - } + // :: NodeSpec The top level document node. + doc: { + content: "block+" + }, + + // :: NodeSpec A plain paragraph textblock. Represented in the DOM + // as a `

` element. + paragraph: { + content: "inline*", + group: "block", + parseDOM: [{ tag: "p" }], + toDOM() { return pDOM } + }, + + // :: NodeSpec A blockquote (`

`) wrapping one or more blocks. + blockquote: { + content: "block+", + group: "block", + defining: true, + parseDOM: [{ tag: "blockquote" }], + toDOM() { return blockquoteDOM } + }, + + // :: NodeSpec A horizontal rule (`
`). + horizontal_rule: { + group: "block", + parseDOM: [{ tag: "hr" }], + toDOM() { return hrDOM } + }, + + // :: NodeSpec A heading textblock, with a `level` attribute that + // should hold the number 1 to 6. Parsed and serialized as `

` to + // `

` elements. + heading: { + attrs: { level: { default: 1 } }, + content: "inline*", + group: "block", + defining: true, + parseDOM: [{ tag: "h1", attrs: { level: 1 } }, + { tag: "h2", attrs: { level: 2 } }, + { tag: "h3", attrs: { level: 3 } }, + { tag: "h4", attrs: { level: 4 } }, + { tag: "h5", attrs: { level: 5 } }, + { tag: "h6", attrs: { level: 6 } }], + toDOM(node: any) { return ["h" + node.attrs.level, 0] } + }, + + // :: NodeSpec A code listing. Disallows marks or non-text inline + // nodes by default. Represented as a `
` element with a
+    // `` element inside of it.
+    code_block: {
+        content: "text*",
+        marks: "",
+        group: "block",
+        code: true,
+        defining: true,
+        parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
+        toDOM() { return preDOM }
+    },
+
+    // :: NodeSpec The text node.
+    text: {
+        group: "inline"
+    },
+
+    // :: NodeSpec An inline image (``) node. Supports `src`,
+    // `alt`, and `href` attributes. The latter two default to the empty
+    // string.
+    image: {
+        inline: true,
+        attrs: {
+            src: {},
+            alt: { default: null },
+            title: { default: null }
+        },
+        group: "inline",
+        draggable: true,
+        parseDOM: [{
+            tag: "img[src]", getAttrs(dom: any) {
+                return {
+                    src: dom.getAttribute("src"),
+                    title: dom.getAttribute("title"),
+                    alt: dom.getAttribute("alt")
+                }
+            }
+        }],
+        toDOM(node: any) { return ["img", node.attrs] }
+    },
+
+    // :: NodeSpec A hard line break, represented in the DOM as `
`. + hard_break: { + inline: true, + group: "inline", + selectable: false, + parseDOM: [{ tag: "br" }], + toDOM() { return brDOM } + }, + + ordered_list: { + ...orderedList, + 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 } + }, + //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*' + } } const emDOM: DOMOutputSpecArray = ["em", 0]; @@ -144,92 +144,179 @@ const underlineDOM: DOMOutputSpecArray = ["underline", 0]; // :: Object [Specs](#model.MarkSpec) for the marks in the schema. export const marks: { [index: string]: MarkSpec } = { - // :: MarkSpec A link. Has `href` and `title` attributes. `title` - // defaults to the empty string. Rendered and parsed as an `` - // element. - link: { - attrs: { - href: {}, - title: { default: null } - }, - inclusive: false, - parseDOM: [{ - tag: "a[href]", getAttrs(dom: any) { - return { href: dom.getAttribute("href"), title: dom.getAttribute("title") } - } - }], - toDOM(node: any) { return ["a", node.attrs, 0] } - }, - - // :: MarkSpec An emphasis mark. Rendered as an `` element. - // Has parse rules that also match `` and `font-style: italic`. - em: { - parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }], - toDOM() { return emDOM } - }, - - // :: MarkSpec A strong mark. Rendered as ``, parse rules - // also match `` and `font-weight: bold`. - strong: { - parseDOM: [{ tag: "strong" }, - { tag: "b" }, - { style: "font-weight" }], - toDOM() { return strongDOM } - }, - - underline: { - parseDOM: [ - { tag: 'u' }, - { style: 'text-decoration=underline' } - ], - toDOM: () => ['span', { - style: 'text-decoration:underline' - }] - }, - - strikethrough: { - parseDOM: [ - { tag: 'strike' }, - { style: 'text-decoration=line-through' }, - { style: 'text-decoration-line=line-through' } - ], - toDOM: () => ['span', { - style: 'text-decoration-line:line-through' - }] - }, - - subscript: { - excludes: 'superscript', - parseDOM: [ - { tag: 'sub' }, - { style: 'vertical-align=sub' } - ], - toDOM: () => ['sub'] - }, - - superscript: { - excludes: 'subscript', - parseDOM: [ - { tag: 'sup' }, - { style: 'vertical-align=super' } - ], - toDOM: () => ['sup'] - }, - - - // :: MarkSpec Code font mark. Represented as a `` element. - code: { - parseDOM: [{ tag: "code" }], - toDOM() { return codeDOM } - }, - - - timesNewRoman: { - parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }], - toDOM: () => ['span', { - style: 'font-family: "Times New Roman", Times, serif;' - }] - }, + // :: MarkSpec A link. Has `href` and `title` attributes. `title` + // defaults to the empty string. Rendered and parsed as an `` + // element. + link: { + attrs: { + href: {}, + title: { default: null } + }, + inclusive: false, + parseDOM: [{ + tag: "a[href]", getAttrs(dom: any) { + return { href: dom.getAttribute("href"), title: dom.getAttribute("title") } + } + }], + toDOM(node: any) { return ["a", node.attrs, 0] } + }, + + // :: MarkSpec An emphasis mark. Rendered as an `` element. + // Has parse rules that also match `` and `font-style: italic`. + em: { + parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style=italic" }], + toDOM() { return emDOM } + }, + + // :: MarkSpec A strong mark. Rendered as ``, parse rules + // also match `` and `font-weight: bold`. + strong: { + parseDOM: [{ tag: "strong" }, + { tag: "b" }, + { style: "font-weight" }], + toDOM() { return strongDOM } + }, + + underline: { + parseDOM: [ + { tag: 'u' }, + { style: 'text-decoration=underline' } + ], + toDOM: () => ['span', { + style: 'text-decoration:underline' + }] + }, + + strikethrough: { + parseDOM: [ + { tag: 'strike' }, + { style: 'text-decoration=line-through' }, + { style: 'text-decoration-line=line-through' } + ], + toDOM: () => ['span', { + style: 'text-decoration-line:line-through' + }] + }, + + subscript: { + excludes: 'superscript', + parseDOM: [ + { tag: 'sub' }, + { style: 'vertical-align=sub' } + ], + toDOM: () => ['sub'] + }, + + superscript: { + excludes: 'subscript', + parseDOM: [ + { tag: 'sup' }, + { style: 'vertical-align=super' } + ], + toDOM: () => ['sup'] + }, + + + // :: MarkSpec Code font mark. Represented as a `` element. + code: { + parseDOM: [{ tag: "code" }], + toDOM() { return codeDOM } + }, + + + /* FONTS */ + timesNewRoman: { + parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }], + toDOM: () => ['span', { + style: 'font-family: "Times New Roman", Times, serif;' + }] + }, + + arial: { + parseDOM: [{ style: 'font-family: Arial, Helvetica, sans-serif;' }], + toDOM: () => ['span', { + style: 'font-family: Arial, Helvetica, sans-serif;' + }] + }, + + georgia: { + parseDOM: [{ style: 'font-family: Georgia, serif;' }], + toDOM: () => ['span', { + style: 'font-family: Georgia, serif;' + }] + }, + + comicSans: { + parseDOM: [{ style: 'font-family: "Comic Sans MS", cursive, sans-serif;' }], + toDOM: () => ['span', { + style: 'font-family: "Comic Sans MS", cursive, sans-serif;' + }] + }, + + tahoma: { + parseDOM: [{ style: 'font-family: Tahoma, Geneva, sans-serif;' }], + toDOM: () => ['span', { + style: 'font-family: Tahoma, Geneva, sans-serif;' + }] + }, + + impact: { + parseDOM: [{ style: 'font-family: Impact, Charcoal, sans-serif;' }], + toDOM: () => ['span', { + style: 'font-family: Impact, Charcoal, sans-serif;' + }] + }, + + /** FONT SIZES */ + + p10: { + parseDOM: [{ style: 'font-size: 10px;' }], + toDOM: () => ['span', { + style: 'font-size: 10px;' + }] + }, + + p12: { + parseDOM: [{ style: 'font-size: 12px;' }], + toDOM: () => ['span', { + style: 'font-size: 12px;' + }] + }, + + p16: { + parseDOM: [{ style: 'font-size: 16px;' }], + toDOM: () => ['span', { + style: 'font-size: 16px;' + }] + }, + + p24: { + parseDOM: [{ style: 'font-size: 24px;' }], + toDOM: () => ['span', { + style: 'font-size: 24px;' + }] + }, + + p32: { + parseDOM: [{ style: 'font-size: 32px;' }], + toDOM: () => ['span', { + style: 'font-size: 32px;' + }] + }, + + p48: { + parseDOM: [{ style: 'font-size: 48px;' }], + toDOM: () => ['span', { + style: 'font-size: 48px;' + }] + }, + + p72: { + parseDOM: [{ style: 'font-size: 72px;' }], + toDOM: () => ['span', { + style: 'font-size: 72px;' + }] + }, } // :: Schema diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index ea580d104..b23074239 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -1,5 +1,241 @@ @import "../views/global_variables"; +.ProseMirror-textblock-dropdown { + min-width: 3em; + } + + .ProseMirror-menu { + margin: 0 -4px; + line-height: 1; + } + + .ProseMirror-tooltip .ProseMirror-menu { + width: -webkit-fit-content; + width: fit-content; + white-space: pre; + } + + .ProseMirror-menuitem { + margin-right: 3px; + display: inline-block; + } + + .ProseMirror-menuseparator { + // border-right: 1px solid #ddd; + margin-right: 3px; + } + + .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu { + font-size: 90%; + white-space: nowrap; + } + + .ProseMirror-menu-dropdown { + vertical-align: 1px; + cursor: pointer; + position: relative; + padding-right: 15px; + } + + .ProseMirror-menu-dropdown-wrap { + padding: 1px 0 1px 4px; + display: inline-block; + position: relative; + } + + .ProseMirror-menu-dropdown:after { + content: ""; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid currentColor; + opacity: .6; + position: absolute; + right: 4px; + top: calc(50% - 2px); + } + + .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { + position: absolute; + background: $dark-color; + color:white; + border: 1px solid #aaa; + padding: 2px; + } + + .ProseMirror-menu-dropdown-menu { + z-index: 15; + min-width: 6em; + } + + .ProseMirror-menu-dropdown-item { + cursor: pointer; + padding: 2px 8px 2px 4px; + width: auto; + } + + .ProseMirror-menu-dropdown-item:hover { + background: #2e2b2b; + } + + .ProseMirror-menu-submenu-wrap { + position: relative; + margin-right: -4px; + } + + .ProseMirror-menu-submenu-label:after { + content: ""; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 4px solid currentColor; + opacity: .6; + position: absolute; + right: 4px; + top: calc(50% - 4px); + } + + .ProseMirror-menu-submenu { + display: none; + min-width: 4em; + left: 100%; + top: -3px; + } + + .ProseMirror-menu-active { + background: #eee; + border-radius: 4px; + } + + .ProseMirror-menu-active { + background: #eee; + border-radius: 4px; + } + + .ProseMirror-menu-disabled { + opacity: .3; + } + + .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu { + display: block; + } + + .ProseMirror-menubar { + border-top-left-radius: inherit; + border-top-right-radius: inherit; + position: relative; + min-height: 1em; + color: white; + padding: 1px 6px; + top: 0; left: 0; right: 0; + border-bottom: 1px solid silver; + background:$dark-color; + z-index: 10; + -moz-box-sizing: border-box; + box-sizing: border-box; + overflow: visible; + } + + .ProseMirror-icon { + display: inline-block; + line-height: .8; + vertical-align: -2px; /* Compensate for padding */ + padding: 2px 8px; + cursor: pointer; + } + + .ProseMirror-menu-disabled.ProseMirror-icon { + cursor: default; + } + + .ProseMirror-icon svg { + fill: currentColor; + height: 1em; + } + + .ProseMirror-icon span { + vertical-align: text-top; + } +.ProseMirror-example-setup-style hr { + padding: 2px 10px; + border: none; + margin: 1em 0; + } + + .ProseMirror-example-setup-style hr:after { + content: ""; + display: block; + height: 1px; + background-color: silver; + line-height: 2px; + } + + .ProseMirror ul, .ProseMirror ol { + padding-left: 30px; + } + + .ProseMirror blockquote { + padding-left: 1em; + border-left: 3px solid #eee; + margin-left: 0; margin-right: 0; + } + + .ProseMirror-example-setup-style img { + cursor: default; + } + + .ProseMirror-prompt { + background: white; + padding: 5px 10px 5px 15px; + border: 1px solid silver; + position: fixed; + border-radius: 3px; + z-index: 11; + box-shadow: -.5px 2px 5px rgba(0, 0, 0, .2); + } + + .ProseMirror-prompt h5 { + margin: 0; + font-weight: normal; + font-size: 100%; + color: #444; + } + + .ProseMirror-prompt input[type="text"], + .ProseMirror-prompt textarea { + background: #eee; + border: none; + outline: none; + } + + .ProseMirror-prompt input[type="text"] { + padding: 0 4px; + } + + .ProseMirror-prompt-close { + position: absolute; + left: 2px; top: 1px; + color: #666; + border: none; background: transparent; padding: 0; + } + + .ProseMirror-prompt-close:after { + content: "✕"; + font-size: 12px; + } + + .ProseMirror-invalid { + background: #ffc; + border: 1px solid #cc7; + border-radius: 4px; + padding: 5px 10px; + position: absolute; + min-width: 10em; + } + + .ProseMirror-prompt-buttons { + margin-top: 5px; + display: none; + } + .tooltipMenu { position: absolute; z-index: 20; @@ -39,7 +275,7 @@ display: inline-block; border-right: 1px solid rgba(0, 0, 0, 0.2); //color: rgb(19, 18, 18); - color: $light-color; + color: white; line-height: 1; padding: 0px 2px; margin: 1px; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 82e9d1bac..777abe030 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1,12 +1,12 @@ import { action, IReactionDisposer, reaction } from "mobx"; -import { Dropdown, DropdownSubmenu, MenuItem } from "prosemirror-menu"; +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 } from "prosemirror-state"; +import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; -import { Schema, NodeType } from "prosemirror-model"; +import { Schema, NodeType, MarkType } from "prosemirror-model"; import React = require("react"); import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); @@ -24,8 +24,12 @@ export class TooltipTextMenu { private tooltip: HTMLElement; private num_icons = 0; + private view: EditorView; + private fontStyles: MarkType[]; + private fontSizes: MarkType[]; constructor(view: EditorView) { + this.view = view; this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; @@ -34,7 +38,6 @@ export class TooltipTextMenu { //add additional icons library.add(faListUl); - //add the buttons to the tooltip let items = [ { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong") }, @@ -44,44 +47,142 @@ export class TooltipTextMenu { { 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: toggleMark(schema.marks.timesNewRoman), dom: this.icon("x", "TNR") }, { command: lift, dom: this.icon("<", "lift") }, ] //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 (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"; - } + //pointer down handler to activate button effects + dom.addEventListener("pointerdown", e => { + e.preventDefault(); + view.focus(); + command(view.state, view.dispatch, view); }) - }) + + }); + + //dropdowns + //list of font btns to add + this.fontStyles = [ + schema.marks.timesNewRoman, + schema.marks.arial, + schema.marks.georgia, + schema.marks.comicSans, + schema.marks.tahoma, + schema.marks.impact, + ] + this.fontSizes = [ + schema.marks.p10, + schema.marks.p12, + schema.marks.p16, + schema.marks.p24, + schema.marks.p32, + schema.marks.p48, + schema.marks.p72, + ] + this.addFontDropdowns(); this.update(view, undefined); } + //adds font size and font style dropdowns + addFontDropdowns() { + //filtering function - might be unecessary + let cut = (arr: MenuItem[]) => arr.filter(x => x); + let fontBtns = [ + this.dropdownBtn("Times New Roman", "font-family: Times New Roman, Times, serif; width: 120px;", schema.marks.timesNewRoman, this.view, this.changeToMarkInGroup, this.fontStyles), + this.dropdownBtn("Arial", "font-family: Arial, Helvetica, sans-serif; width: 120px;", schema.marks.arial, this.view, this.changeToMarkInGroup, this.fontStyles), + this.dropdownBtn("Georgia", "font-family: Georgia, serif; width: 120px; width: 120px;", schema.marks.georgia, this.view, this.changeToMarkInGroup, this.fontStyles), + this.dropdownBtn("ComicSans", "font-family: Comic Sans MS, cursive, sans-serif; width: 120px;", schema.marks.comicSans, this.view, this.changeToMarkInGroup, this.fontStyles), + this.dropdownBtn("Tahoma", "font-family: Tahoma, Geneva, sans-serif; width: 120px;", schema.marks.tahoma, this.view, this.changeToMarkInGroup, this.fontStyles), + this.dropdownBtn("Impact", "font-family: Impact, Charcoal, sans-serif; width: 120px;", schema.marks.impact, this.view, this.changeToMarkInGroup, this.fontStyles), + ] + + let fontSizeBtns = [ + this.dropdownBtn("10", "width: 50px;", schema.marks.p10, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("12", "width: 50px;", schema.marks.p12, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("16", "width: 50px;", schema.marks.p16, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("24", "width: 50px;", schema.marks.p24, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("32", "width: 50px;", schema.marks.p32, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("48", "width: 50px;", schema.marks.p48, this.view, this.changeToMarkInGroup, this.fontSizes), + this.dropdownBtn("72", "width: 50px;", schema.marks.p72, this.view, this.changeToMarkInGroup, this.fontSizes), + ] + + //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;" }) as MenuItem; + this.tooltip.appendChild(dd_fontStyle.render(this.view).dom); + this.tooltip.appendChild(dd_fontSize.render(this.view).dom); + } + + //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text + changeToMarkInGroup(markType: MarkType, view: EditorView, fontMarks: MarkType[]) { + let { empty, $cursor, ranges } = view.state.selection as TextSelection; + let state = view.state; + let dispatch = view.dispatch; + + //remove all other active font marks + fontMarks.forEach((type) => { + if (dispatch) { + if ($cursor) { + if (type.isInSet(state.storedMarks || $cursor.marks())) { + dispatch(state.tr.removeStoredMark(type)); + } + } else { + let has = false, tr = state.tr + for (let i = 0; !has && i < ranges.length; i++) { + let { $from, $to } = ranges[i] + has = state.doc.rangeHasMark($from.pos, $to.pos, type) + } + for (let i = 0; i < ranges.length; i++) { + let { $from, $to } = ranges[i] + if (has) { + toggleMark(type)(view.state, view.dispatch, view); + } + } + } + } + }); //actually apply font + return toggleMark(markType)(view.state, view.dispatch, view); + } + + //makes a button for the drop down + //css is the style you want applied to the button + dropdownBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) { + return new MenuItem({ + title: "", + label: label, + execEvent: "", + class: "menuicon", + css: css, + enable(state) { return true; }, + run() { + changeToMarkInGroup(markType, view, groupMarks); + } + }); + } // Helper function to create menu icons icon(text: string, name: string) { let span = document.createElement("span"); span.className = "menuicon " + name; span.title = name; span.textContent = text; + span.style.color = "white"; return span; } + //method for checking whether node can be inserted + canInsert(state: EditorState, nodeType: NodeType>) { + let $from = state.selection.$from + for (let d = $from.depth; d >= 0; d--) { + let index = $from.index(d) + if ($from.node(d).canReplaceWith(index, index, nodeType)) return true + } + return false + } + + //adapted this method - use it to check if block has a tag (ie bulleting) blockActive(type: NodeType>, state: EditorState) { let attrs = {}; @@ -100,30 +201,6 @@ 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"; - //icon.style.color = "white"; - //span.appendChild(); - 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; - } - // Create an icon for a heading at the given level heading(level: number) { return { @@ -156,10 +233,8 @@ export class TooltipTextMenu { 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 = 8 * 16 + 15; + let width = 220; let mid = Math.min(start.left, end.left) + width; - - //THIS WIDTH IS 15 * NUMBER OF ICONS + 15 this.tooltip.style.width = width + "px"; this.tooltip.style.bottom = (box.bottom - start.top) + "px"; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e59179874..fdb1b97be 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -74,7 +74,12 @@ export class FormattedTextBox extends React.Component { history(), keymap({ "Mod-z": undo, "Mod-y": redo }), keymap(baseKeymap), - this.tooltipMenuPlugin() + this.tooltipMenuPlugin(), + new Plugin({ + props: { + attributes: { class: "ProseMirror-example-setup-style" } + } + }) ] }; -- cgit v1.2.3-70-g09d2