diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/Main.tsx | 50 | ||||
-rw-r--r-- | src/client/views/PresentationView.scss | 68 | ||||
-rw-r--r-- | src/client/views/PresentationView.tsx | 186 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 8 | ||||
-rw-r--r-- | src/fields/KeyStore.ts | 2 |
5 files changed, 298 insertions, 16 deletions
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 0469211fa..d8e429ad1 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -12,7 +12,6 @@ import { Document } from '../../fields/Document'; import { Field, FieldWaiting, Opt, FIELD_WAITING } from '../../fields/Field'; import { KeyStore } from '../../fields/KeyStore'; import { ListField } from '../../fields/ListField'; -import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { MessageStore } from '../../server/Message'; import { RouteStore } from '../../server/RouteStore'; @@ -30,6 +29,8 @@ import { Server } from '../Server'; import { SetupDrag } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; +import { PresentationView } from './PresentationView'; +import { WorkspacesMenu } from '../../server/authentication/controllers/WorkspacesMenu'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; @@ -51,7 +52,10 @@ export class Main extends React.Component { return CurrentUserUtils.UserDocument.GetT(KeyStore.ActiveWorkspace, Document); } private set mainContainer(doc: Document | undefined | FIELD_WAITING) { - doc && CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc); + if (doc) { + doc.GetOrCreateAsync(KeyStore.PresentationView, Document, doc => { }); + CurrentUserUtils.UserDocument.Set(KeyStore.ActiveWorkspace, doc); + } } constructor(props: Readonly<{}>) { @@ -170,28 +174,44 @@ export class Main extends React.Component { } @computed + get presentationView() { + if (this.mainContainer) { + let presentation = this.mainContainer.GetT(KeyStore.PresentationView, Document); + return presentation ? <PresentationView Document={presentation} key="presentation" /> : (null); + } + return (null); + } + + @computed get mainContent() { let pwidthFunc = () => this.pwidth; let pheightFunc = () => this.pheight; let noScaling = () => 1; let mainCont = this.mainContainer; + let pcontent = this.presentationView; return <Measure onResize={action((r: any) => { this.pwidth = r.entry.width; this.pheight = r.entry.height; })}> {({ measureRef }) => <div ref={measureRef} id="mainContent-div"> {!mainCont ? (null) : - <DocumentView Document={mainCont} - addDocument={undefined} - removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={noScaling} - PanelWidth={pwidthFunc} - PanelHeight={pheightFunc} - isTopMost={true} - selectOnLoad={false} - focus={emptyDocFunction} - parentActive={returnTrue} - onActiveChanged={emptyFunction} - ContainingCollectionView={undefined} />} + <div style={{ width: "100%", height: "100%", position: "absolute", }}> + {pcontent} + <DocumentView + key="documentView" + Document={mainCont} + addDocument={undefined} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={noScaling} + PanelWidth={pwidthFunc} + PanelHeight={pheightFunc} + isTopMost={true} + selectOnLoad={false} + focus={emptyDocFunction} + parentActive={returnTrue} + onActiveChanged={emptyFunction} + ContainingCollectionView={undefined} /> + </div> + } </div> } </Measure>; diff --git a/src/client/views/PresentationView.scss b/src/client/views/PresentationView.scss new file mode 100644 index 000000000..7c5677f0d --- /dev/null +++ b/src/client/views/PresentationView.scss @@ -0,0 +1,68 @@ +.presentationView-cont { + position: absolute; + background: white; + z-index: 1; + box-shadow: #AAAAAA .2vw .2vw .4vw; + right: 0; + top:0; + bottom:0; +} + +.presentationView-item { + width: 220px; + height: 40px; + vertical-align: center; + padding-top: 15px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; +} + +.presentationView-item:hover { + transition: all .1s; + background: #AAAAAA +} + +.presentationView-heading { + margin-top: 0px; + height: 40px; + background: lightseagreen; + padding: 30px; +} +.presentationView-title { + padding-top: 3px; + padding-bottom: 3px; + font-size: 25px; + float:left; +} +.presentation-icon{ + float: right; + display: inline; + width: 10px; + margin-top: 7px; +} +.presentationView-header { + padding-top: 1px; + padding-bottom: 1px; + font-size: 15px; + float:left; + } + + .presentation-next{ + float: right; + } + .presentation-back{ + float: left; + } + .presentation-next:hover{ + transition: all .1s; + background: #AAAAAA +} +.presentation-back:hover{ + transition: all .1s; + background: #AAAAAA +}
\ No newline at end of file diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx new file mode 100644 index 000000000..9c5297e7c --- /dev/null +++ b/src/client/views/PresentationView.tsx @@ -0,0 +1,186 @@ +import { observer } from "mobx-react"; +import { Document } from "../../fields/Document"; +import { KeyStore } from "../../fields/KeyStore"; +import { ListField } from "../../fields/ListField"; +import React = require("react") +import { TextField } from "../../fields/TextField"; +import { observable, action } from "mobx"; +import { Field, FieldWaiting } from "../../fields/Field"; +import "./PresentationView.scss" +import { NumberField } from "../../fields/NumberField"; +import "./Main.tsx"; +import { DocumentManager } from "../util/DocumentManager"; +import { Utils } from "../../Utils"; + +export interface PresViewProps { + Document: Document; +} + + +@observer +/** + * Component that takes in a document prop and a boolean whether it's collapsed or not. + */ +class PresentationViewItem extends React.Component<PresViewProps> { + + //look at CollectionFreeformView.focusDocument(d) + @action + openDoc = (doc: Document) => { + let docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.focus(docView.props.Document); + } + } + + /** + * Removes a document from the presentation view + **/ + @action + public RemoveDoc(doc: Document) { + const value = this.props.Document.GetData(KeyStore.Data, ListField, new Array<Document>()) + let index = -1; + for (let i = 0; i < value.length; i++) { + if (value[i].Id === doc.Id) { + index = i; + break; + } + } + if (index !== -1) { + value.splice(index, 1) + } + } + + /** + * Renders a single child document. It will just append a list element. + * @param document The document to render. + */ + renderChild(document: Document) { + let title = document.Title; + + //to get currently selected presentation doc + let selected = this.props.Document.GetNumber(KeyStore.SelectedDoc, 0); + + // if the title hasn't loaded, immediately return the div + if (!title) { + return <div className="presentationView-item" key={document.Id}></div>; + } + // finally, if it's a normal document, then render it as such. + else { + const children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + if (children && children.Data[selected] === document) { + //this doc is selected + const styles = { + background: "gray" + } + return <li className="presentationView-item" style={styles} key={Utils.GenerateGuid()}> + <div className="presentationView-header" onClick={() => this.openDoc(document)}>{title}</div> + <div className="presentation-icon" onClick={() => this.RemoveDoc(document)}>X</div></li>; + } else { + return <li className="presentationView-item" key={Utils.GenerateGuid()} > + <div className="presentationView-header" onClick={() => this.openDoc(document)}>{title}</div> + <div className="presentation-icon" onClick={() => this.RemoveDoc(document)}>X</div></li>; + } + + } + } + + render() { + const children = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + + if (children) { + return (<div> + {children.Data.map(value => this.renderChild(value))} + </div>) + } else { + return <div></div>; + } + } +} + + +@observer +export class PresentationView extends React.Component<PresViewProps> { + public static Instance: PresentationView; + + //observable means render is re-called every time variable is changed + @observable + collapsed: boolean = false; + closePresentation = action(() => this.props.Document.SetNumber(KeyStore.Width, 0)); + next = () => { + const current = this.props.Document.GetNumber(KeyStore.SelectedDoc, 0); + const allDocs = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + if (allDocs && current < allDocs.Data.length + 1) { + //can move forwards + this.props.Document.SetNumber(KeyStore.SelectedDoc, current + 1); + const doc = allDocs.Data[current + 1]; + let docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.focus(docView.props.Document); + } + } + + }; + back = () => { + const current = this.props.Document.GetNumber(KeyStore.SelectedDoc, 0); + const allDocs = this.props.Document.GetT<ListField<Document>>(KeyStore.Data, ListField); + if (allDocs && current - 1 >= 0) { + //can move forwards + this.props.Document.SetNumber(KeyStore.SelectedDoc, current - 1); + const doc = allDocs.Data[current - 1]; + let docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.focus(docView.props.Document); + } + } + + }; + + private ref: React.RefObject<HTMLDivElement>; + + //initilize class variables + constructor(props: PresViewProps) { + super(props); + this.ref = React.createRef() + PresentationView.Instance = this; + } + + /** + * Adds a document to the presentation view + **/ + @action + public PinDoc(doc: Document) { + //add this new doc to props.Document + if (this.props.Document.Get(KeyStore.Data) instanceof Field) { + const value = this.props.Document.GetData(KeyStore.Data, ListField, new Array<Document>()) + value.push(doc); + } else { + this.props.Document.SetData(KeyStore.Data, [doc], ListField); + } + + this.props.Document.SetNumber(KeyStore.Width, 300); + } + + render() { + let titleStr = this.props.Document.Title; + let width = this.props.Document.GetNumber(KeyStore.Width, 0); + + //TODO: next and back should be icons + return ( + <div className="presentationView-cont" style={{ width: width, overflow: "hidden" }}> + <div className="presentationView-heading"> + <div className="presentationView-title">{titleStr}</div> + <div className='presentation-icon' onClick={this.closePresentation}>X</div></div> + <div> + <div className="presentation-back" onClick={this.back}>back</div> + <div className="presentation-next" onClick={this.next}>next</div> + + </div> + <ul> + <PresentationViewItem + Document={this.props.Document} + /> + </ul> + </div> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1cbb24223..9c991113c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -23,6 +23,7 @@ import { ContextMenu } from "../ContextMenu"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import React = require("react"); +import { PresentationView } from "../PresentationView"; export interface DocumentViewProps { @@ -292,7 +293,12 @@ export class DocumentView extends React.Component<DocumentViewProps> { ContextMenu.Instance.addItem({ description: "Copy URL", event: () => Utils.CopyText(ServerUtils.prepend("/doc/" + this.props.Document.Id)) }); ContextMenu.Instance.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document.Id) }); //ContextMenu.Instance.addItem({ description: "Docking", event: () => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Docking) }) + ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document) }); ContextMenu.Instance.addItem({ description: "Delete", event: this.deleteClicked }); + if (!this.topMost) { + // DocumentViews should stop propagation of this event + e.stopPropagation(); + } ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); SelectionManager.SelectDoc(this, e.ctrlKey); } @@ -330,4 +336,4 @@ export class DocumentView extends React.Component<DocumentViewProps> { </div> ); } -} +}
\ No newline at end of file diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 16a909eb8..1fb65965b 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -30,6 +30,7 @@ export namespace KeyStore { export const Caption = new Key("Caption"); export const ActiveWorkspace = new Key("ActiveWorkspace"); export const DocumentText = new Key("DocumentText"); + export const PresentationView = new Key("PresentationView"); export const BrushingDocs = new Key("BrushingDocs"); export const LinkedToDocs = new Key("LinkedToDocs"); export const LinkedFromDocs = new Key("LinkedFromDocs"); @@ -41,6 +42,7 @@ export namespace KeyStore { export const AnnotationOn = new Key("AnnotationOn"); export const NumPages = new Key("NumPages"); export const Ink = new Key("Ink"); + export const SelectedDoc = new Key("SelectedDoc"); export const Cursors = new Key("Cursors"); export const OptionalRightCollection = new Key("OptionalRightCollection"); export const Archives = new Key("Archives"); |