diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 122 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkBox.scss | 39 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkBox.tsx | 89 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkEditor.scss | 29 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkEditor.tsx | 58 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkMenu.scss | 20 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkMenu.tsx | 54 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.scss | 1 |
9 files changed, 395 insertions, 19 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9a0c1e6a4..1db16aca6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,4 +1,4 @@ -import { action, computed } from "mobx"; +import { action, computed, IReactionDisposer, runInAction, reaction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { Field, FieldWaiting, Opt } from "../../../fields/Field"; @@ -24,6 +24,8 @@ import { WebBox } from "../nodes/WebBox"; import { PDFBox } from "../nodes/PDFBox"; import "./DocumentView.scss"; import React = require("react"); +import { TextField } from "../../../fields/TextField"; +import { DocumentManager } from "../../util/DocumentManager"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -88,6 +90,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { private _documentBindings: any = null; private _downX: number = 0; private _downY: number = 0; + private _reactionDisposer: Opt<IReactionDisposer>; @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); } @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; } @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); } @@ -97,16 +100,21 @@ export class DocumentView extends React.Component<DocumentViewProps> { onPointerDown = (e: React.PointerEvent): void => { this._downX = e.clientX; this._downY = e.clientY; +<<<<<<< HEAD if (e.shiftKey && e.buttons === 1) { CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e); +======= + if (e.shiftKey && e.buttons === 2) { + if (this.props.isTopMost) { + this.startDragging(e.pageX, e.pageY); + } + else CollectionDockingView.Instance.StartOtherDrag(this.props.Document, e); +>>>>>>> 618e66a5a070f1aac9224bd3f44b76a5ac314bfa e.stopPropagation(); } else { if (this.active && !e.isDefaultPrevented()) { e.stopPropagation(); - if (e.buttons === 2) { - e.preventDefault(); - } document.removeEventListener("pointermove", this.onPointerMove) document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp) @@ -114,25 +122,74 @@ export class DocumentView extends React.Component<DocumentViewProps> { } } } + + private dropDisposer?: DragManager.DragDropDisposer; + protected createDropTarget = (ele: HTMLDivElement) => { + + } + + componentDidMount() { + if (this._mainCont.current) { + this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); + } + runInAction(() => { + DocumentManager.Instance.DocumentViews.push(this); + }) + this._reactionDisposer = reaction( + () => this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.slice(), + () => { + if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.SelectedDocs.indexOf(this.props.Document.Id) != -1) + SelectionManager.SelectDoc(this, true); + }); + } + + componentDidUpdate() { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (this._mainCont.current) { + this.dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); + } + } + + componentWillUnmount() { + if (this.dropDisposer) { + this.dropDisposer(); + } + runInAction(() => { + DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); + + }) + if (this._reactionDisposer) { + this._reactionDisposer(); + } + } + + startDragging(x: number, y: number) { + if (this._mainCont.current) { + const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + let dragData: { [id: string]: any } = {}; + dragData["documentView"] = this; + dragData["xOffset"] = x - left; + dragData["yOffset"] = y - top; + DragManager.StartDrag(this._mainCont.current, dragData, { + handlers: { + dragComplete: action(() => { }), + }, + hideSource: true + }) + } + } + onPointerMove = (e: PointerEvent): void => { if (e.cancelBubble) { return; } if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { document.removeEventListener("pointermove", this.onPointerMove) - document.removeEventListener("pointerup", this.onPointerUp) - if (this._mainCont.current != null && !this.topMost) { - const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - let dragData: { [id: string]: any } = {}; - dragData["documentView"] = this; - dragData["xOffset"] = e.x - left; - dragData["yOffset"] = e.y - top; - DragManager.StartDrag(this._mainCont.current, dragData, { - handlers: { - dragComplete: action(() => { }), - }, - hideSource: true - }) + document.removeEventListener("pointerup", this.onPointerUp); + if (!this.topMost || e.buttons == 2) { + this.startDragging(e.x, e.y); } } e.stopPropagation(); @@ -173,6 +230,34 @@ export class DocumentView extends React.Component<DocumentViewProps> { } @action + drop = (e: Event, de: DragManager.DropEvent) => { + console.log("drop"); + const sourceDocView: DocumentView = de.data["linkSourceDoc"]; + if (!sourceDocView) { + return; + } + let sourceDoc: Document = sourceDocView.props.Document; + let destDoc: Document = this.props.Document; + if (this.props.isTopMost) { + return; + } + let linkDoc: Document = new Document(); + + linkDoc.Set(KeyStore.Title, new TextField("New Link")); + linkDoc.Set(KeyStore.LinkDescription, new TextField("")); + linkDoc.Set(KeyStore.LinkTags, new TextField("Default")); + + sourceDoc.GetOrCreateAsync(KeyStore.LinkedToDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) }); + linkDoc.Set(KeyStore.LinkedToDocs, destDoc); + destDoc.GetOrCreateAsync(KeyStore.LinkedFromDocs, ListField, field => { (field as ListField<Document>).Data.push(linkDoc) }); + linkDoc.Set(KeyStore.LinkedFromDocs, sourceDoc); + + + + e.stopPropagation(); + } + + @action onContextMenu = (e: React.MouseEvent): void => { e.stopPropagation(); let moved = Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3; @@ -197,7 +282,8 @@ export class DocumentView extends React.Component<DocumentViewProps> { ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15) SelectionManager.SelectDoc(this, e.ctrlKey); } - @computed get mainContent() { + + get mainContent() { return <JsxParser components={{ FormattedTextBox, ImageBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, WebBox, KeyValueBox, VideoBox, AudioBox, PDFBox }} bindings={this._documentBindings} diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f5d5597a7..521b0324e 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -69,7 +69,7 @@ export class FormattedTextBox extends React.Component<FieldViewProps> { }; let field = this.props.doc.GetT(this.props.fieldKey, RichTextField); - if (field && field != FieldWaiting) { + if (field && field != FieldWaiting && field.Data) { state = EditorState.fromJSON(config, JSON.parse(field.Data)); } else { state = EditorState.create(config); diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss new file mode 100644 index 000000000..00e5ebb3d --- /dev/null +++ b/src/client/views/nodes/LinkBox.scss @@ -0,0 +1,39 @@ +.link-container { + width: 100%; + height: 30px; + display: flex; + flex-direction: row; + border-top: 0.5px solid #bababa; +} + +.info-container { + width: 60%; + padding-top: 5px; + padding-left: 5px; + display: flex; + flex-direction: column +} + +.link-name { + font-size: 11px; +} + +.doc-name { + font-size: 8px; +} + +.button-container { + width: 40%; + display: flex; + flex-direction: row; +} + +.button { + height: 15px; + width: 15px; + margin: 8px 5px; + border-radius: 50%; + opacity: 0.6; + pointer-events: auto; + background-color: #2B6091; +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx new file mode 100644 index 000000000..69df676ff --- /dev/null +++ b/src/client/views/nodes/LinkBox.tsx @@ -0,0 +1,89 @@ +import { observable, computed, action } from "mobx"; +import React = require("react"); +import { SelectionManager } from "../../util/SelectionManager"; +import { observer } from "mobx-react"; +import './LinkBox.scss' +import { KeyStore } from '../../../fields/KeyStore' +import { props } from "bluebird"; +import { DocumentView } from "./DocumentView"; +import { Document } from "../../../fields/Document"; +import { ListField } from "../../../fields/ListField"; +import { DocumentManager } from "../../util/DocumentManager"; +import { LinkEditor } from "./LinkEditor"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; + +interface Props { + linkDoc: Document; + linkName: String; + pairedDoc: Document; + type: String; + showEditor: () => void +} + +@observer +export class LinkBox extends React.Component<Props> { + + onViewButtonPressed = (e: React.PointerEvent): void => { + console.log("view down"); + e.stopPropagation(); + let docView = DocumentManager.Instance.getDocumentView(this.props.pairedDoc); + if (docView) { + docView.props.focus(this.props.pairedDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc) + } + } + + onEditButtonPressed = (e: React.PointerEvent): void => { + console.log("edit down"); + e.stopPropagation(); + + this.props.showEditor(); + } + + onDeleteButtonPressed = (e: React.PointerEvent): void => { + console.log("delete down"); + e.stopPropagation(); + this.props.linkDoc.GetTAsync(KeyStore.LinkedFromDocs, Document, field => { + if (field) { + field.GetTAsync<ListField<Document>>(KeyStore.LinkedToDocs, ListField, field => { + if (field) { + field.Data.splice(field.Data.indexOf(this.props.linkDoc)); + } + }) + } + }); + this.props.linkDoc.GetTAsync(KeyStore.LinkedToDocs, Document, field => { + if (field) { + field.GetTAsync<ListField<Document>>(KeyStore.LinkedFromDocs, ListField, field => { + if (field) { + field.Data.splice(field.Data.indexOf(this.props.linkDoc)); + } + }) + } + }); + } + + render() { + + return ( + //<LinkEditor linkBox={this} linkDoc={this.props.linkDoc} /> + <div className="link-container"> + <div className="info-container" onPointerDown={this.onViewButtonPressed}> + <div className="link-name"> + <p>{this.props.linkName}</p> + </div> + <div className="doc-name"> + <p>{this.props.type}{this.props.pairedDoc.Title}</p> + </div> + </div> + + <div className="button-container"> + <div className="button" onPointerDown={this.onViewButtonPressed}></div> + <div className="button" onPointerDown={this.onEditButtonPressed}></div> + <div className="button" onPointerDown={this.onDeleteButtonPressed}></div> + </div> + </div> + ) + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss new file mode 100644 index 000000000..cb191dc8c --- /dev/null +++ b/src/client/views/nodes/LinkEditor.scss @@ -0,0 +1,29 @@ +.edit-container { + width: 100%; + height: auto; + display: flex; + flex-direction: column; +} + +.name-input { + margin-bottom: 10px; + padding: 5px; + font-size: 12px; +} + +.description-input { + font-size: 12px; + padding: 5px; + margin-bottom: 10px; +} + +.save-button { + width: 50px; + height: 20px; + background-color: #2B6091; + margin: 0 auto; + color: white; + text-align: center; + line-height: 20px; + font-size: 12px; +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx new file mode 100644 index 000000000..3f7b4bf2d --- /dev/null +++ b/src/client/views/nodes/LinkEditor.tsx @@ -0,0 +1,58 @@ +import { observable, computed, action } from "mobx"; +import React = require("react"); +import { SelectionManager } from "../../util/SelectionManager"; +import { observer } from "mobx-react"; +import './LinkEditor.scss' +import { KeyStore } from '../../../fields/KeyStore' +import { props } from "bluebird"; +import { DocumentView } from "./DocumentView"; +import { Document } from "../../../fields/Document"; +import { TextField } from "../../../fields/TextField"; +import { link } from "fs"; + +interface Props { + linkDoc: Document; + showLinks: () => void; +} + +@observer +export class LinkEditor extends React.Component<Props> { + + @observable private _nameInput: string = this.props.linkDoc.GetText(KeyStore.Title, ""); + @observable private _descriptionInput: string = this.props.linkDoc.GetText(KeyStore.LinkDescription, ""); + + + onSaveButtonPressed = (e: React.PointerEvent): void => { + console.log("view down"); + e.stopPropagation(); + + this.props.linkDoc.SetData(KeyStore.Title, this._nameInput, TextField); + this.props.linkDoc.SetData(KeyStore.LinkDescription, this._descriptionInput, TextField); + + this.props.showLinks(); + } + + + + render() { + + return ( + <div className="edit-container"> + <input onChange={this.onNameChanged} className="name-input" type="text" value={this._nameInput} placeholder="Name . . ."></input> + <textarea onChange={this.onDescriptionChanged} className="description-input" value={this._descriptionInput} placeholder="Description . . ."></textarea> + <div className="save-button" onPointerDown={this.onSaveButtonPressed}>SAVE</div> + </div> + + ) + } + + @action + onNameChanged = (e: React.ChangeEvent<HTMLInputElement>) => { + this._nameInput = e.target.value; + } + + @action + onDescriptionChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + this._descriptionInput = e.target.value; + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss new file mode 100644 index 000000000..a120ab2a7 --- /dev/null +++ b/src/client/views/nodes/LinkMenu.scss @@ -0,0 +1,20 @@ +#linkMenu-container { + width: 100%; + height: auto; + display: flex; + flex-direction: column; +} + +#linkMenu-searchBar { + width: 100%; + padding: 5px; + margin-bottom: 10px; + font-size: 12px; +} + +#linkMenu-list { + margin-top: 5px; + width: 100%; + height: 100px; + overflow-y: scroll; +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx new file mode 100644 index 000000000..5c6b06d00 --- /dev/null +++ b/src/client/views/nodes/LinkMenu.tsx @@ -0,0 +1,54 @@ +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { Document } from "../../../fields/Document"; +import { FieldWaiting } from "../../../fields/Field"; +import { Key } from "../../../fields/Key"; +import { KeyStore } from '../../../fields/KeyStore'; +import { ListField } from "../../../fields/ListField"; +import { DocumentView } from "./DocumentView"; +import { LinkBox } from "./LinkBox"; +import { LinkEditor } from "./LinkEditor"; +import './LinkMenu.scss'; +import React = require("react"); + +interface Props { + docView: DocumentView; + changeFlyout: () => void +} + +@observer +export class LinkMenu extends React.Component<Props> { + + @observable private _editingLink?: Document; + + renderLinkItems(links: Document[], key: Key, type: string) { + return links.map(link => { + let doc = link.GetT(key, Document); + if (doc && doc != FieldWaiting) { + return <LinkBox key={doc.Id} linkDoc={link} linkName={link.Title} pairedDoc={doc} showEditor={action(() => this._editingLink = link)} type={type} /> + } + }) + } + + render() { + //get list of links from document + let linkFrom: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedFromDocs, ListField, []); + let linkTo: Document[] = this.props.docView.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []); + if (this._editingLink === undefined) { + return ( + <div id="linkMenu-container"> + <input id="linkMenu-searchBar" type="text" placeholder="Search..."></input> + <div id="linkMenu-list"> + {this.renderLinkItems(linkTo, KeyStore.LinkedToDocs, "Source: ")} + {this.renderLinkItems(linkFrom, KeyStore.LinkedFromDocs, "Destination: ")} + </div> + </div> + ) + } else { + return ( + <LinkEditor linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor> + ) + } + + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index e72b3c4da..a535b2638 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -4,6 +4,7 @@ position: absolute; width: 100%; height: 100%; + overflow: scroll; } .webBox-button { |
