diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 7 | ||||
| -rw-r--r-- | src/client/views/InkingControl.scss | 10 | ||||
| -rw-r--r-- | src/client/views/InkingControl.tsx | 16 | ||||
| -rw-r--r-- | src/client/views/Main.scss | 10 | ||||
| -rw-r--r-- | src/client/views/MainOverlayTextBox.tsx | 37 | ||||
| -rw-r--r-- | src/client/views/MainView.tsx | 42 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss | 1 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 19 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 18 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 93 |
11 files changed, 116 insertions, 139 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 389c1897d..ceca940b6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -478,11 +478,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - let proto = Doc.GetProto(doc); + let proto = Doc.GetProto(element.props.Document); let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect, false) && nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { - proto.nativeWidth = doc.width; - proto.nativeHeight = doc.height; + proto.nativeWidth = nwidth = doc.width || 0; + proto.nativeHeight = nheight = doc.height || 0; proto.ignoreAspect = true; } if (nwidth > 0 && nheight > 0) { @@ -505,6 +505,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { doc.width = zoomBasis * actualdW; doc.height = zoomBasis * actualdH; + proto.autoHeight = undefined; } } }); diff --git a/src/client/views/InkingControl.scss b/src/client/views/InkingControl.scss index ba4ec41af..465e14d07 100644 --- a/src/client/views/InkingControl.scss +++ b/src/client/views/InkingControl.scss @@ -1,8 +1,6 @@ @import "globalCssVariables"; .inking-control { - position: absolute; - left: 70px; - bottom: 70px; + bottom: 20px; margin: 0; padding: 0; display: flex; @@ -63,10 +61,9 @@ margin-top: 4px; } .ink-panel { - margin: 6px 12px 6px 0; - height: 30px; + height: 24px; vertical-align: middle; - line-height: 36px; + line-height: 28px; padding: 0 10px; color: $intermediate-color; &:first { @@ -114,7 +111,6 @@ border-radius: 11px; width: 22px; height: 22px; - margin-top: 6px; cursor: pointer; text-align: center; // span { // color: $light-color; diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index d1a6eb7fd..e7f0968af 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -57,16 +57,10 @@ export class InkingControl extends React.Component { return this._selectedWidth; } - selected = (tool: InkTool) => { - if (this._selectedTool === tool) { - return { color: "#61aaa3" }; - } - return {}; - } - @action toggleDisplay = () => { this._open = !this._open; + this.switchTool(this._open ? InkTool.Pen : InkTool.None); } @@ -78,14 +72,6 @@ export class InkingControl extends React.Component { render() { return ( <ul className="inking-control" style={this._open ? { display: "flex" } : { display: "none" }}> - <li className="ink-tools ink-panel"> - <div className="ink-tool-buttons"> - <button onClick={() => this.switchTool(InkTool.Pen)} style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" title="Pen" /></button> - <button onClick={() => this.switchTool(InkTool.Highlighter)} style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" title="Highlighter" /></button> - <button onClick={() => this.switchTool(InkTool.Eraser)} style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" title="Eraser" /></button> - <button onClick={() => this.switchTool(InkTool.None)} style={this.selected(InkTool.None)}><FontAwesomeIcon icon="ban" size="lg" title="Pointer" /></button> - </div> - </li> <li className="ink-color ink-panel"> <label>COLOR: </label> <div className="ink-color-display" style={{ backgroundColor: this._selectedColor }} diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 57a53c999..c3acd9790 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -133,7 +133,7 @@ button:hover { bottom: 22px; left: 24px; - label { + > label { background: $dark-color; color: $light-color; display: inline-block; @@ -155,15 +155,15 @@ button:hover { transform: scale(1.15); } - input { + > input { display: none; } - input:not(:checked)~#add-options-content { + > input:not(:checked)~#add-options-content { display: none; } - input:checked~label { + > input:checked~label { transform: rotate(45deg); transition: transform 0.5s; cursor: pointer; @@ -207,7 +207,7 @@ ul#add-options-list { list-style: none; padding: 5 0 0 0; - li { + > li { display: inline-block; padding: 0; } diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index a0359419b..23e90ece5 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -1,14 +1,15 @@ import { action, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; +import "normalize.css"; import * as React from 'react'; +import { Doc } from '../../new_fields/Doc'; +import { BoolCast } from '../../new_fields/Types'; import { emptyFunction, returnTrue, returnZero, Utils } from '../../Utils'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; -import "normalize.css"; +import { CollectionDockingView } from './collections/CollectionDockingView'; import "./MainOverlayTextBox.scss"; import { FormattedTextBox } from './nodes/FormattedTextBox'; -import { CollectionDockingView } from './collections/CollectionDockingView'; -import { Doc } from '../../new_fields/Doc'; interface MainOverlayTextBoxProps { } @@ -22,7 +23,9 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> private _textHideOnLeave?: boolean; private _textTargetDiv: HTMLDivElement | undefined; private _textProxyDiv: React.RefObject<HTMLDivElement>; - public TextDoc?: Doc; + private _textBottom: boolean | undefined; + private _textAutoHeight: boolean | undefined; + @observable public TextDoc?: Doc; constructor(props: MainOverlayTextBoxProps) { super(props); @@ -34,7 +37,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> this.TextDoc = box.props.Document; let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); }; - this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf) + this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content") } else { this.TextDoc = undefined; @@ -44,15 +47,18 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> } @action - private setTextDoc(textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) { + private setTextDoc(textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform, autoHeight?: boolean) { if (this._textTargetDiv) { this._textTargetDiv.style.color = this._textColor; } + this._textAutoHeight = autoHeight; this.TextFieldKey = textFieldKey!; - this._textXf = tx ? tx : () => Transform.Identity(); + let txf = tx ? tx : () => Transform.Identity(); + this._textXf = txf; this._textTargetDiv = div; this._textHideOnLeave = FormattedTextBox.InputBoxOverlay && FormattedTextBox.InputBoxOverlay.props.hideOnLeave; if (div) { + this._textBottom = div.parentElement && div.parentElement.style.bottom ? true : false; this._textColor = (getComputedStyle(div) as any).color; div.style.color = "transparent"; } @@ -99,16 +105,21 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> } } render() { + this.TextDoc; if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) { let textRect = this._textTargetDiv.getBoundingClientRect(); let s = this._textXf().Scale; - let auto = FormattedTextBox.InputBoxOverlay.props.height; - return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${textRect.top}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} > + let location = this._textBottom ? textRect.bottom : textRect.top; + let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; + return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${location}px) scale(${1 / s},${1 / s})`, width: "auto", height: "0px" }} > <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} - style={{ width: `${textRect.width * s}px`, height: auto ? "auto" : `${textRect.height * s}px` }}> - <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} isSelected={returnTrue} select={emptyFunction} isTopMost={true} - selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} - ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} /> + style={{ width: `${textRect.width * s}px`, height: "0px" }}> + <div style={{ height: hgt, width: "100%", position: "absolute", bottom: this._textBottom ? "0px" : undefined }}> + <FormattedTextBox color={`${this._textColor}`} fieldKey={this.TextFieldKey} hideOnLeave={this._textHideOnLeave} isOverlay={true} Document={FormattedTextBox.InputBoxOverlay.props.Document} + isSelected={returnTrue} select={emptyFunction} isTopMost={true} selectOnLoad={true} + ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} + ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} /> + </div> </div> </ div>; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 42d5929bf..8a6790677 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -32,6 +32,8 @@ import { listSpec } from '../../new_fields/Schema'; import { Id } from '../../new_fields/FieldSymbols'; import { HistoryUtil } from '../util/History'; import { CollectionBaseView } from './collections/CollectionBaseView'; +import { InkTool } from '../../new_fields/InkField'; +import { InkingCanvas } from './InkingCanvas'; @observer @@ -217,37 +219,27 @@ export class MainView extends React.Component { </Measure>; } + selected = (tool: InkTool) => { + if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" }; + if (InkingControl.Instance.selectedTool === tool) { + return { color: "#61aaa3", fontSize: "50%" }; + } + return { fontSize: "50%" }; + } + /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */ nodesMenu() { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; - let pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; - let weburl = "https://cs.brown.edu/courses/cs166/"; - let audiourl = "http://techslides.com/demos/samples/sample.mp3"; - let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); - let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); - //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); - // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); - let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); - let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [ - [React.createRef<HTMLDivElement>(), "font", "Add Textbox", addTextNode], [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode], - [React.createRef<HTMLDivElement>(), "file-pdf", "Add PDF", addPDFNode], - [React.createRef<HTMLDivElement>(), "film", "Add Video", addVideoNode], - [React.createRef<HTMLDivElement>(), "music", "Add Audio", addAudioNode], - [React.createRef<HTMLDivElement>(), "globe-asia", "Add Web Clipping", addWebNode], [React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode], [React.createRef<HTMLDivElement>(), "tree", "Add Tree", addTreeNode], - [React.createRef<HTMLDivElement>(), "table", "Add Schema", addSchemaNode], ]; return < div id="add-nodes-menu" > @@ -256,12 +248,20 @@ export class MainView extends React.Component { <div id="add-options-content"> <ul id="add-options-list"> + <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> + <li key="undo"><button className="add-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li> + <li key="redo"><button className="add-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li> {btns.map(btn => <li key={btn[1]} ><div ref={btn[0]}> <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}> <FontAwesomeIcon icon={btn[1]} size="sm" /> </button> </div></li>)} + <li key="ink" style={{ paddingRight: "6px" }}><button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button></li> + <li key="pen"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" title="Pen" /></button></li> + <li key="marker"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" title="Pen" /></button></li> + <li key="eraser"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" title="Pen" /></button></li> + <li key="inkControls"><InkingControl /></li> </ul> </div> </div >; @@ -276,7 +276,6 @@ export class MainView extends React.Component { let logoutRef = React.createRef<HTMLDivElement>(); return [ - <button className="clear-db-button" key="clear-db" onClick={e => e.shiftKey && e.altKey && DocServer.DeleteDatabase()}>Clear Database</button>, <div id="toolbar" key="toolbar"> <div ref={notifsRef}> <button className="toolbar-button round-button" title="Notifs" @@ -287,10 +286,6 @@ export class MainView extends React.Component { {length} </div> </div> - <button className="toolbar-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button> - <button className="toolbar-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button> - <button className="toolbar-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button> - <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /></button> </div >, this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <SearchBox /> </div> : null, <div className="main-buttonDiv" key="logout" style={{ bottom: '0px', right: '1px', position: 'absolute' }} ref={logoutRef}> @@ -314,7 +309,6 @@ export class MainView extends React.Component { <ContextMenu /> {this.nodesMenu()} {this.miscButtons} - <InkingControl /> <MainOverlayTextBox /> </div> ); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 762955a08..93a1a8cda 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -170,7 +170,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) { this.props.addDocument && this.props.addDocument(Docs.WebDocument(href, options)); } } else if (text) { - this.props.addDocument && this.props.addDocument(Docs.TextDocument({ ...options, documentText: "@@@" + text }), false); + this.props.addDocument && this.props.addDocument(Docs.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text }), false); } return; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index e10ba9d7e..5ac2e1f9c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -57,6 +57,7 @@ } >.jsx-parser { + position:absolute; z-index:0; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index c699b3437..5f2a732b9 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -45,12 +45,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps> _commandExecuted = false; @action - cleanupInteractions = (all: boolean = false) => { + cleanupInteractions = (all: boolean = false, rem_keydown: boolean = true) => { if (all) { document.removeEventListener("pointerup", this.onPointerUp, true); document.removeEventListener("pointermove", this.onPointerMove, true); } - document.removeEventListener("keydown", this.marqueeCommand, true); + if (rem_keydown) { + document.removeEventListener("keydown", this.marqueeCommand, true); + } this._visible = false; } @@ -93,7 +95,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps> } }); } else if (!e.ctrlKey) { - let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" }); + let newBox = Docs.TextDocument({ width: 200, height: 30, x: x, y: y, title: "-typed text-" }); + newBox.proto!.autoHeight = true; this.props.addLiveTextDocument(newBox); } e.stopPropagation(); @@ -145,6 +148,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> this._downY = this._lastY = e.pageY; this._commandExecuted = false; PreviewCursor.Visible = false; + this.cleanupInteractions(true); if (e.button === 2 || (e.button === 0 && e.altKey)) { if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]); document.addEventListener("pointermove", this.onPointerMove, true); @@ -180,14 +184,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps> @action onPointerUp = (e: PointerEvent): void => { + console.log("pointer up!"); if (this._visible) { + console.log("visible"); let mselect = this.marqueeSelect(); if (!e.shiftKey) { SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); } this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); + mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true); } - this.cleanupInteractions(true); + else { + console.log("invisible"); + this.cleanupInteractions(true); + } + if (e.altKey) { e.preventDefault(); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 19f5c7d36..6fe01963a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -125,10 +125,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu constructor(props: DocumentViewProps) { super(props); - this.selectOnLoad = props.selectOnLoad; } - _reactionDisposer?: IReactionDisposer; @action componentDidMount() { @@ -271,7 +269,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (!linkedFwdDocs.some(l => l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab"); - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], linkedFwdContextDocs[altKey ? 1 : 0]); + let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; + DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); } } } @@ -317,13 +316,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); - if (StrCast(doc.layout).indexOf("Formatted") !== -1) { // only need to freeze the dimensions of text boxes since they don't have a native width and height naturally - if (doc.isButton && !doc.nativeWidth) { + if (doc.isButton) { + if (!doc.nativeWidth) { doc.nativeWidth = this.props.Document[WidthSym](); doc.nativeHeight = this.props.Document[HeightSym](); - } else { - doc.nativeWidth = doc.nativeHeight = undefined; } + } else { + doc.nativeWidth = doc.nativeHeight = undefined; } } fullScreenClicked = (): void => { @@ -442,14 +441,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); - @action select = (ctrlPressed: boolean) => { this.selectOnLoad = false; SelectionManager.SelectDoc(this, ctrlPressed); } + @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); } - @observable selectOnLoad: boolean = false; @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } @computed get contents() { return ( - <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.selectOnLoad} layoutKey={"layout"} />); + <DocumentContentsView {...this.props} isSelected={this.isSelected} select={this.select} selectOnLoad={this.props.selectOnLoad} layoutKey={"layout"} />); } render() { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 7dcacd19b..0a4a67211 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -5,51 +5,37 @@ import { observer } from "mobx-react"; import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; +import { NodeType } from 'prosemirror-model'; import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Opt } from "../../../new_fields/Doc"; +import { Id } from '../../../new_fields/FieldSymbols'; import { RichTextField } from "../../../new_fields/RichTextField"; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { DocServer } from "../../DocServer"; -import { DocUtils, Docs } from '../../documents/Documents'; -import { DocumentManager } from "../../util/DocumentManager"; +import { Docs } from '../../documents/Documents'; import { DragManager } from "../../util/DragManager"; import buildKeymap from "../../util/ProsemirrorExampleTransfer"; import { inpRules } from "../../util/RichTextRules"; -import { ImageResizeView, schema } from "../../util/RichTextSchema"; +import { ImageResizeView, schema, SummarizedView } from "../../util/RichTextSchema"; import { SelectionManager } from "../../util/SelectionManager"; import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu"; import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { ContextMenu } from "../../views/ContextMenu"; +import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); -import { Id } from '../../../new_fields/FieldSymbols'; -import { MainOverlayTextBox } from '../MainOverlayTextBox'; -import { NodeType } from 'prosemirror-model'; library.add(faEdit); library.add(faSmile); // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document // -// HTML Markup: <FormattedTextBox Doc={Document's ID} FieldKey={Key's name} -// -// In Code, the node's HTML is specified in the document's parameterized structure as: -// document.SetField(KeyStore.Layout, "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />"); -// and the node's binding to the specified document KEYNAME as: -// document.SetField(KeyStore.LayoutKeys, new ListField([KeyStore.<KEYNAME>])); -// The Jsx parser at run time will bind: -// 'fieldKey' property to the Key stored in LayoutKeys -// and 'doc' property to the document that is being rendered -// -// When rendered() by React, this extracts the TextController from the Document stored at the -// 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 interface FormattedTextBoxProps { isOverlay?: boolean; @@ -74,11 +60,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _proseRef?: HTMLDivElement; private _editorView: Opt<EditorView>; private _toolTipTextMenu: TooltipTextMenu | undefined = undefined; - private _lastState: any = undefined; private _applyingChange: boolean = false; private _linkClicked = ""; private _reactionDisposer: Opt<IReactionDisposer>; - private _inputReactionDisposer: Opt<IReactionDisposer>; private _proxyReactionDisposer: Opt<IReactionDisposer>; private dropDisposer?: DragManager.DragDropDisposer; public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } @@ -117,10 +101,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } - dispatchTransaction = (tx: Transaction) => { if (this._editorView) { - const state = this._lastState = this._editorView.state.apply(tx); + const state = this._editorView.state.apply(tx); this._editorView.updateState(state); this._applyingChange = true; Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new RichTextField(JSON.stringify(state.toJSON()))); @@ -181,18 +164,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe ] }; - if (this.props.isOverlay) { - this._inputReactionDisposer = reaction(() => FormattedTextBox.InputBoxOverlay, - () => { - if (this._editorView) { - this._editorView.destroy(); - } - this.setupEditor(config, // bcz: not sure why, but the order of events is such that this.props.Document hasn't updated yet, so without forcing the editor to the MainOverlayTextBox, it will display the previously focused textbox - MainOverlayTextBox.Instance.TextDoc ? MainOverlayTextBox.Instance.TextDoc : this.props.Document, - MainOverlayTextBox.Instance.TextFieldKey ? MainOverlayTextBox.Instance.TextFieldKey : this.props.fieldKey); - } - ); - } else { + if (!this.props.isOverlay) { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), () => { if (this.props.isSelected()) { @@ -202,13 +174,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }); } - this._reactionDisposer = reaction( () => { const field = this.props.Document ? Cast(this.props.Document[this.props.fieldKey], RichTextField) : undefined; - return field ? field.Data : undefined; + return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; }, - field => field && this._editorView && !this._applyingChange && + field => this._editorView && !this._applyingChange && this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field))) ); this.setupEditor(config, this.props.Document, this.props.fieldKey); @@ -226,7 +197,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config), dispatchTransaction: this.dispatchTransaction, nodeViews: { - image(node, view, getPos) { return new ImageResizeView(node, view, getPos); } + image(node, view, getPos) { return new ImageResizeView(node, view, getPos); }, + star(node, view, getPos) { return new SummarizedView(node, view, getPos); } } }); if (startup) { @@ -236,9 +208,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } if (this.props.selectOnLoad) { - console.log("Sel on load " + this.props.Document.title + " " + doc!.title); - this.props.select(false); - this._editorView!.focus(); + if (!this.props.isOverlay) this.props.select(false); + else this._editorView!.focus(); } } @@ -249,9 +220,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._reactionDisposer) { this._reactionDisposer(); } - if (this._inputReactionDisposer) { - this._inputReactionDisposer(); - } if (this._proxyReactionDisposer) { this._proxyReactionDisposer(); } @@ -264,7 +232,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._toolTipTextMenu.tooltip.style.opacity = "0"; } } - this._linkClicked = ""; + let ctrlKey = e.ctrlKey; if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { let href = (e.target as any).href; for (let parent = (e.target as any).parentNode; !href && parent; parent = parent.parentNode) { @@ -281,6 +249,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe e.stopPropagation(); e.preventDefault(); } + } if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { e.preventDefault(); @@ -290,14 +259,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) { this._toolTipTextMenu.tooltip.style.opacity = "1"; } - let ctrlKey = e.ctrlKey; - if (this._linkClicked) { - DocServer.GetRefField(this._linkClicked).then(f => { - (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")); - }); - e.stopPropagation(); - e.preventDefault(); - } if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } @@ -375,6 +336,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (!this._undoTyping) { this._undoTyping = UndoManager.StartBatch("undoTyping"); } + if (this.props.isOverlay && this.props.Document.autoHeight) { + let xf = this._ref.current!.getBoundingClientRect(); + let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height); + let nh = NumCast(this.props.Document.nativeHeight, 0); + let dh = NumCast(this.props.Document.height, 0); + let sh = scrBounds.height; + this.props.Document.height = nh ? dh / nh * sh : sh; + this.props.Document.proto!.nativeHeight = nh ? sh : undefined; + } } @action @@ -385,6 +355,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onPointerLeave = (e: React.PointerEvent) => { this._entered = false; } + + specificContextMenu = (e: React.MouseEvent): void => { + let subitems: ContextMenuProps[] = []; + subitems.push({ + description: BoolCast(this.props.Document.autoHeight, false) ? "Manual Height" : "Auto Height", + event: action(() => this.props.Document.autoHeight = !BoolCast(this.props.Document.autoHeight, false)), icon: "expand-arrows-alt" + }); + ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems }); + } render() { let style = this.props.isOverlay ? "scroll" : "hidden"; let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : ""; @@ -398,10 +377,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "initial", pointerEvents: interactive ? "all" : "none", }} - // onKeyDown={this.onKeyPress} onKeyPress={this.onKeyPress} onFocus={this.onFocused} onClick={this.onClick} + onContextMenu={this.specificContextMenu} onBlur={this.onBlur} onPointerUp={this.onPointerUp} onPointerDown={this.onPointerDown} |
