diff options
| author | monikahedman <monika_hedman@brown.edu> | 2019-08-23 11:04:56 -0400 |
|---|---|---|
| committer | monikahedman <monika_hedman@brown.edu> | 2019-08-23 11:04:56 -0400 |
| commit | dd4227a125c0cd679f6437fab85b1cd772a34f78 (patch) | |
| tree | 20f8b37248c3bbcdf68c3e6207f312d54798621b /src/client/views/nodes | |
| parent | 1fb290bcc1c46214cfd553f31c1282d2694530ea (diff) | |
| parent | 20f7d2dca1c115c84f6ac89981ef1e3c7c9a2757 (diff) | |
pulled from master
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ButtonBox.tsx | 27 | ||||
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 64 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 126 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/PresBox.tsx | 282 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 6 |
7 files changed, 202 insertions, 307 deletions
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index 8b6f11aac..ca5f0acc2 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -15,7 +15,11 @@ import { Doc } from '../../../new_fields/Doc'; import './ButtonBox.scss'; import { observer } from 'mobx-react'; import { DocumentIconContainer } from './DocumentIcon'; -import { StrCast } from '../../../new_fields/Types'; +import { StrCast, BoolCast } from '../../../new_fields/Types'; +import { DragManager } from '../../util/DragManager'; +import { undoBatch } from '../../util/UndoManager'; +import { action, computed } from 'mobx'; +import { List } from '../../../new_fields/List'; library.add(faEdit as any); @@ -30,10 +34,29 @@ const ButtonDocument = makeInterface(ButtonSchema); @observer export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(ButtonDocument) { public static LayoutString() { return FieldView.LayoutString(ButtonBox); } + private dropDisposer?: DragManager.DragDropDisposer; + @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } + + + protected createDropTarget = (ele: HTMLDivElement) => { + if (this.dropDisposer) { + this.dropDisposer(); + } + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + } + } + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + if (de.data instanceof DragManager.DocumentDragData) { + Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments); + } + } render() { return ( - <div className="buttonBox-outerDiv" > + <div className="buttonBox-outerDiv" ref={this.createDropTarget} > <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >{this.Document.text || this.Document.title}</div> </div> ); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index ee596c841..7631ecc6c 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -83,7 +83,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF transformOrigin: "left top", position: "absolute", backgroundColor: "transparent", - boxShadow: this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : + boxShadow: this.props.Document.opacity === 0 ? undefined : this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : this.clusterColor ? ( this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : undefined, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9f1d98bb5..31c12c994 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,7 +40,7 @@ import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); -import { PresBox } from './PresBox'; +import { DocumentType } from '../../documents/DocumentTypes'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -295,8 +295,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onClick = async (e: React.MouseEvent) => { if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. - e.stopPropagation(); if (this.onClickHandler && this.onClickHandler.script) { + e.stopPropagation(); this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }); e.preventDefault(); return; @@ -304,6 +304,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu let altKey = e.altKey; let ctrlKey = e.ctrlKey; if (this._doubleTap && this.props.renderDepth) { + e.stopPropagation(); let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List<string>(); Doc.UseDetailLayout(fullScreenAlias); @@ -315,10 +316,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { + if (BoolCast(this.props.Document.ignoreClick)) { + return; + } + e.stopPropagation(); SelectionManager.SelectDoc(this, e.ctrlKey); let isExpander = (e.target as any).id === "isExpander"; - if (BoolCast(this.props.Document.isButton) || isExpander) { - SelectionManager.DeselectAll(); + if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) { let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); @@ -329,6 +333,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),]; if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents + SelectionManager.DeselectAll(); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); if (altKey || ctrlKey) { @@ -357,6 +362,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } else if (linkedDocs.length) { + SelectionManager.DeselectAll(); let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document)); let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]]; @@ -394,13 +400,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); - // } } onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && this.active) { + if (e.cancelBubble && this.active) { + document.removeEventListener("pointermove", this.onPointerMove); + } + else if (!e.cancelBubble && this.active) { if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) { if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) { document.removeEventListener("pointermove", this.onPointerMove); @@ -583,7 +591,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); let existingMake = ContextMenu.Instance.findByDescription("Make..."); let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : []; - makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); + makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" }); makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" }); makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") }); makes.push({ @@ -593,6 +601,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this.makeBtnClicked(); }, icon: "window-restore" }); + makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" }) !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); let existing = ContextMenu.Instance.findByDescription("Layout..."); let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; @@ -629,35 +638,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } }); - cm.addItem({ - description: "Import document", icon: "upload", event: () => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".zip"; - input.onchange = async _e => { - const files = input.files; - if (!files) return; - const file = files[0]; - let formData = new FormData(); - formData.append('file', file); - formData.append('remap', "true"); - const upload = Utils.prepend("/uploadDoc"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json === "error") { - return; - } - const doc = await DocServer.GetRefField(json); - if (!doc || !(doc instanceof Doc)) { - return; - } - const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY); - doc.x = x, doc.y = y; - this.props.addDocument && this.props.addDocument(doc, false); - }; - input.click(); - } - }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; @@ -745,7 +725,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu chromeHeight = () => { let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined; let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle); - return showTitle ? 25 : 0; + let templates = Cast(this.layoutDoc.templates, listSpec("string")); + if (!showOverlays && templates instanceof List) { + templates.map(str => { + if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title"; + }); + } + return (showTitle ? 25 : 0) + 1;// bcz: why 8?? } get layoutDoc() { @@ -801,7 +787,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu {!showTitle && !showCaption ? this.contents : <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}> - <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 33px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}> + <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}> {this.contents} </div> {!showTitle ? (null) : diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9652a3a78..467f10ab8 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -6,7 +6,7 @@ import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { Fragment, Node, Node as ProsNode, NodeType, Slice } from "prosemirror-model"; -import { EditorState, Plugin, Transaction } from "prosemirror-state"; +import { EditorState, Plugin, Transaction, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; @@ -35,6 +35,8 @@ import React = require("react"); import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; import { DocumentDecorations } from '../DocumentDecorations'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; +import { DictationManager } from '../../util/DictationManager'; +import { ReplaceStep } from 'prosemirror-transform'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -62,7 +64,7 @@ export const GoogleRef = "googleDocId"; type RichTextDocument = makeInterface<[typeof richTextSchema]>; const RichTextDocument = makeInterface(richTextSchema); -type PullHandler = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => void; +type PullHandler = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => void; @observer export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { @@ -85,7 +87,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private pushReactionDisposer: Opt<IReactionDisposer>; private dropDisposer?: DragManager.DragDropDisposer; public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } - private isGoogleDocsUpdate = false; @observable _entered = false; @observable public static InputBoxOverlay?: FormattedTextBox = undefined; @@ -183,6 +184,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const marks = tx.storedMarks; if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); } } + this._applyingChange = true; const fieldkey = "preview"; if (this.extensionDoc) this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"); @@ -268,6 +270,64 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + recordKeyHandler = (e: KeyboardEvent) => { + if (this.props.Document !== SelectionManager.SelectedDocuments()[0].props.Document) { + return; + } + if (e.key === "R" && e.altKey) { + e.stopPropagation(); + e.preventDefault(); + this.recordBullet(); + } + } + + recordBullet = async () => { + let completedCue = "end session"; + let results = await DictationManager.Controls.listen({ + interimHandler: this.setCurrentBulletContent, + continuous: { indefinite: false }, + terminators: [completedCue, "bullet", "next"] + }); + if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) { + DictationManager.Controls.stop(); + return; + } + this.nextBullet(this._editorView!.state.selection.to); + setTimeout(this.recordBullet, 2000); + } + + setCurrentBulletContent = (value: string) => { + if (this._editorView) { + let state = this._editorView.state; + let from = state.selection.from; + let to = state.selection.to; + this._editorView.dispatch(state.tr.insertText(value, from, to)); + state = this._editorView.state; + let updated = TextSelection.create(state.doc, from, from + value.length); + this._editorView.dispatch(state.tr.setSelection(updated)); + } + } + + nextBullet = (pos: number) => { + if (this._editorView) { + let frag = Fragment.fromArray(this.newListItems(2)); + let slice = new Slice(frag, 2, 2); + let state = this._editorView.state; + this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice))); + pos += 4; + state = this._editorView.state; + this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos))); + } + } + + private newListItems = (count: number) => { + let listItems: any[] = []; + for (let i = 0; i < count; i++) { + listItems.push(schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create())); + } + return listItems; + } + componentDidMount() { const config = { schema, @@ -301,7 +361,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } this.pullFromGoogleDoc(this.checkState); - runInAction(() => DocumentDecorations.Instance.isAnimatingFetch = true); + this.dataDoc[GoogleRef] && this.dataDoc.unchanged && runInAction(() => DocumentDecorations.Instance.isAnimatingFetch = true); this._reactionDisposer = reaction( () => { @@ -312,13 +372,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._editorView && !this._applyingChange) { let updatedState = JSON.parse(incomingValue); this._editorView.updateState(EditorState.fromJSON(config, updatedState)); - // manually sets cursor selection at the end of the text on focus - if (this.isGoogleDocsUpdate) { - this.isGoogleDocsUpdate = false; - let end = this._editorView.state.doc.content.size - 1; - updatedState.selection = { type: "text", anchor: end, head: end }; - this._editorView.updateState(EditorState.fromJSON(config, updatedState)); - } this.tryUpdateHeight(); } } @@ -377,25 +430,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.unhighlightSearchTerms(); } }, { fireImmediately: true }); + setTimeout(() => this.tryUpdateHeight(), 0); } pushToGoogleDoc = async () => { - this.pullFromGoogleDoc(async (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => { - let modes = GoogleApiClientUtils.Docs.WriteMode; + this.pullFromGoogleDoc(async (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => { + let modes = GoogleApiClientUtils.WriteMode; let mode = modes.Replace; - let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], "string"); + let reference: Opt<GoogleApiClientUtils.Reference> = Cast(this.dataDoc[GoogleRef], "string"); if (!reference) { mode = modes.Insert; - reference = { - title: StrCast(this.dataDoc.title), - handler: id => this.dataDoc[GoogleRef] = id - }; + reference = { service: GoogleApiClientUtils.Service.Documents, title: StrCast(this.dataDoc.title) }; } let redo = async () => { let data = Cast(this.dataDoc.data, RichTextField); if (this._editorView && reference && data) { let content = data[ToPlainText](); let response = await GoogleApiClientUtils.Docs.write({ reference, content, mode }); + response && (this.dataDoc[GoogleRef] = response.documentId); let pushSuccess = response !== undefined && !("errors" in response); dataDoc.unchanged = pushSuccess; DocumentDecorations.Instance.startPushOutcome(pushSuccess); @@ -415,32 +467,38 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe pullFromGoogleDoc = async (handler: PullHandler) => { let dataDoc = this.dataDoc; let documentId = StrCast(dataDoc[GoogleRef]); - let exportState: GoogleApiClientUtils.Docs.ReadResult = {}; + let exportState: GoogleApiClientUtils.ReadResult = {}; if (documentId) { - exportState = await GoogleApiClientUtils.Docs.read({ documentId }); + exportState = await GoogleApiClientUtils.Docs.read({ identifier: documentId }); } UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls); } - updateState = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => { + updateState = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => { let pullSuccess = false; if (exportState !== undefined && exportState.body !== undefined && exportState.title !== undefined) { - let data = Cast(dataDoc.data, RichTextField); - if (data) { + const data = Cast(dataDoc.data, RichTextField); + if (data instanceof RichTextField) { pullSuccess = true; - this.isGoogleDocsUpdate = true; dataDoc.data = new RichTextField(data[FromPlainText](exportState.body)); + setTimeout(() => { + if (this._editorView) { + let state = this._editorView.state; + let end = state.doc.content.size - 1; + this._editorView.dispatch(state.tr.setSelection(TextSelection.create(state.doc, end, end))); + } + }, 0); dataDoc.title = exportState.title; + this.Document.customTitle = true; dataDoc.unchanged = true; } } else { delete dataDoc[GoogleRef]; } DocumentDecorations.Instance.startPullOutcome(pullSuccess); - this.tryUpdateHeight(); } - checkState = (exportState: GoogleApiClientUtils.Docs.ReadResult, dataDoc: Doc) => { + checkState = (exportState: GoogleApiClientUtils.ReadResult, dataDoc: Doc) => { if (exportState !== undefined && exportState.body !== undefined && exportState.title !== undefined) { let data = Cast(dataDoc.data, RichTextField); if (data) { @@ -575,7 +633,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (!this.props.isOverlay) this.props.select(false); else this._editorView!.focus(); } - this.tryUpdateHeight(); } componentWillUnmount() { @@ -661,6 +718,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @action onFocused = (e: React.FocusEvent): void => { + document.removeEventListener("keypress", this.recordKeyHandler); + document.addEventListener("keypress", this.recordKeyHandler); + this.tryUpdateHeight(); if (!this.props.isOverlay) { FormattedTextBox.InputBoxOverlay = this; } else { @@ -710,6 +770,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }); } onBlur = (e: any) => { + document.removeEventListener("keypress", this.recordKeyHandler); if (this._undoTyping) { this._undoTyping.end(); this._undoTyping = undefined; @@ -738,14 +799,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @action tryUpdateHeight() { - if (this.props.Document.autoHeight && this._ref.current!.scrollHeight !== 0) { - // console.log("DT = " + this.props.Document.title + " " + this._ref.current!.clientHeight + " " + this._ref.current!.scrollHeight + " " + this._ref.current!.textContent); - let xf = this._ref.current!.getBoundingClientRect(); - let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, this._ref.current!.textContent === "" ? 35 : this._ref.current!.scrollHeight); + const ChromeHeight = this.props.ChromeHeight; + let sh = this._ref.current ? this._ref.current.scrollHeight : 0; + if (this.props.Document.autoHeight && sh !== 0) { let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.props.Document.height, 0); - let sh = scrBounds.height; - const ChromeHeight = MainOverlayTextBox.Instance.ChromeHeight; this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0)); this.dataDoc.nativeHeight = nh ? sh : undefined; } @@ -781,7 +839,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe <div className={`formattedTextBox-cont-${style}`} ref={this._ref} style={{ overflowY: this.props.Document.autoHeight ? "hidden" : "auto", - height: this.props.height ? this.props.height : undefined, + height: this.props.Document.autoHeight ? "max-content" : this.props.height ? this.props.height : undefined, background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined, opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || Doc.IsBrushed(this.props.Document) ? 1 : 0.1) : 1, color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit", diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 0d4b377dd..653c5c27f 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -128,7 +128,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { let rows: JSX.Element[] = []; let i = 0; const self = this; - for (let key of Object.keys(ids).sort()) { + for (let key of Object.keys(ids).slice().sort()) { rows.push(<KeyValuePair doc={realDoc} ref={(function () { let oldEl: KeyValuePair | undefined; return (el: KeyValuePair) => { diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 112d39c32..e376fbddb 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -12,7 +12,7 @@ import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_field import { Utils } from "../../../Utils"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import PresentationElement, { buttonIndex } from "../presentationview/PresentationElement"; +import PresentationElement from "../presentationview/PresentationElement"; import PresentationViewList from "../presentationview/PresentationList"; import "../presentationview/PresentationView.scss"; import { FieldView, FieldViewProps } from './FieldView'; @@ -45,17 +45,12 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? //Keeping track of the doc for the current presentation -- bcz: keeping a list of current presentations shouldn't be needed. Let users create them, store them, as they see fit. @computed get curPresentation() { return this.props.Document; } - //Mapping from presentation ids to a list of doc that represent a group - @observable groupMappings: Map<String, Doc[]> = new Map(); //mapping from docs to their rendered component @observable presElementsMappings: Map<Doc, PresentationElement> = new Map(); //variable that holds all the docs in the presentation @observable childrenDocs: Doc[] = []; //variable to hold if presentation is started @observable presStatus: boolean = false; - //back-up so that presentation stays the way it's when refreshed - @observable presGroupBackUp: Doc = new Doc(); - @observable presButtonBackUp: Doc = new Doc(); //Mapping of guids to presentations. @observable presentationsMapping: Map<String, Doc> = new Map(); //Mapping of presentations to guid, so that select option values can be given. @@ -102,87 +97,11 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? * otherwise initializes. */ setPresentationBackUps = async () => { - //getting both backUp documents - - let castedGroupBackUp = Cast(this.curPresentation.presGroupBackUp, Doc); - let castedButtonBackUp = Cast(this.curPresentation.presButtonBackUp, Doc); - //if instantiated before - if (castedGroupBackUp instanceof Promise) { - castedGroupBackUp.then(doc => { - let toAssign = doc ? doc : new Doc(); - this.curPresentation.presGroupBackUp = toAssign; - runInAction(() => this.presGroupBackUp = toAssign); - if (doc) { - if (toAssign[Id] === doc[Id]) { - this.retrieveGroupMappings(); - } - } - }); - - //if never instantiated a store doc yet - } else if (castedGroupBackUp instanceof Doc) { - let castedDoc: Doc = await castedGroupBackUp; - runInAction(() => this.presGroupBackUp = castedDoc); - this.retrieveGroupMappings(); - } else { - runInAction(() => { - let toAssign = new Doc(); - this.presGroupBackUp = toAssign; - this.curPresentation.presGroupBackUp = toAssign; - - }); - - } - //if instantiated before - if (castedButtonBackUp instanceof Promise) { - castedButtonBackUp.then(doc => { - let toAssign = doc ? doc : new Doc(); - this.curPresentation.presButtonBackUp = toAssign; - runInAction(() => this.presButtonBackUp = toAssign); - }); - - //if never instantiated a store doc yet - } else if (castedButtonBackUp instanceof Doc) { - let castedDoc: Doc = await castedButtonBackUp; - runInAction(() => this.presButtonBackUp = castedDoc); - - } else { - runInAction(() => { - let toAssign = new Doc(); - this.presButtonBackUp = toAssign; - this.curPresentation.presButtonBackUp = toAssign; - }); - - } - - //storing the presentation status,ie. whether it was stopped or playing let presStatusBackUp = BoolCast(this.curPresentation.presStatus); runInAction(() => this.presStatus = presStatusBackUp); } - /** - * This is the function that is called to retrieve the groups that have been stored and - * push them to the groupMappings. - */ - retrieveGroupMappings = async () => { - let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); - if (castedGroupDocs !== undefined) { - castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { - let castedGrouping = await DocListCastAsync(groupDoc.grouping); - let castedKey = StrCast(groupDoc.presentIdStore, null); - if (castedGrouping) { - castedGrouping.forEach((doc: Doc) => { - doc.presentId = castedKey; - }); - } - if (castedGrouping !== undefined && castedKey !== undefined) { - this.groupMappings.set(castedKey, castedGrouping); - } - }); - } - } - //observable means render is re-called every time variable is changed @observable collapsed: boolean = false; @@ -193,17 +112,13 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? if (docAtCurrentNext === undefined) { return; } - //asking for it's presentation id - let curNextPresId = StrCast(docAtCurrentNext.presentId); let nextSelected = current + 1; - //if curDoc is in a group, selection slides until last one, if not it's next one - if (this.groupMappings.has(curNextPresId)) { - let currentsArray = this.groupMappings.get(StrCast(docAtCurrentNext.presentId))!; - nextSelected = current + currentsArray.length - currentsArray.indexOf(docAtCurrentNext); - - //end of grup so go beyond - if (nextSelected === current) nextSelected = current + 1; + let presDocs = DocListCast(this.curPresentation.data); + for (; nextSelected < presDocs.length - 1; nextSelected++) { + if (!this.presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + break; + } } this.gotoDocument(nextSelected, current); @@ -219,31 +134,31 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? //asking for its presentation id. let curPresId = StrCast(docAtCurrent.presentId); - let prevSelected = current - 1; + let prevSelected = current; let zoomOut: boolean = false; //checking if this presentation id is mapped to a group, if so chosing the first element in group - if (this.groupMappings.has(curPresId)) { - let currentsArray = this.groupMappings.get(StrCast(docAtCurrent.presentId))!; - prevSelected = current - currentsArray.length + (currentsArray.length - currentsArray.indexOf(docAtCurrent)) - 1; - //end of grup so go beyond - if (prevSelected === current) prevSelected = current - 1; - - //checking if any of the group members had used zooming in - currentsArray.forEach((doc: Doc) => { - //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); - if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) { - zoomOut = true; - return; - } - }); - + let presDocs = DocListCast(this.curPresentation.data); + let currentsArray: Doc[] = []; + for (; prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { + currentsArray.push(presDocs[prevSelected]); } + prevSelected = Math.max(0, prevSelected - 1); + + //checking if any of the group members had used zooming in + currentsArray.forEach((doc: Doc) => { + //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); + if (this.presElementsMappings.get(doc)!.props.document.showButton) { + zoomOut = true; + return; + } + }); + // if a group set that flag to zero or a single element //If so making sure to zoom out, which goes back to state before zooming action if (current > 0) { - if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) { + if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.showButton) { let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null); let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]); if (prevScale !== undefined) { @@ -264,19 +179,18 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? */ showAfterPresented = (index: number) => { this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { - let selectedButtons: boolean[] = presElem.selected; //the order of cases is aligned based on priority - if (selectedButtons[buttonIndex.HideTillPressed]) { + if (presElem.props.document.hideTillShownButton) { if (this.childrenDocs.indexOf(key) <= index) { key.opacity = 1; } } - if (selectedButtons[buttonIndex.HideAfter]) { + if (presElem.props.document.hideAfterButton) { if (this.childrenDocs.indexOf(key) < index) { key.opacity = 0; } } - if (selectedButtons[buttonIndex.FadeAfter]) { + if (presElem.props.document.fadeButton) { if (this.childrenDocs.indexOf(key) < index) { key.opacity = 0.5; } @@ -291,21 +205,19 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? */ hideIfNotPresented = (index: number) => { this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { - let selectedButtons: boolean[] = presElem.selected; - //the order of cases is aligned based on priority - if (selectedButtons[buttonIndex.HideAfter]) { + if (presElem.props.document.hideAfterButton) { if (this.childrenDocs.indexOf(key) >= index) { key.opacity = 1; } } - if (selectedButtons[buttonIndex.FadeAfter]) { + if (presElem.props.document.fadeButton) { if (this.childrenDocs.indexOf(key) >= index) { key.opacity = 1; } } - if (selectedButtons[buttonIndex.HideTillPressed]) { + if (presElem.props.document.hideTillShownButton) { if (this.childrenDocs.indexOf(key) > index) { key.opacity = 0; } @@ -320,34 +232,36 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? */ navigateToElement = async (curDoc: Doc, fromDoc: number) => { let docToJump: Doc = curDoc; - let curDocPresId = StrCast(curDoc.presentId, null); let willZoom: boolean = false; - //checking if in group - if (curDocPresId !== undefined) { - if (this.groupMappings.has(curDocPresId)) { - let currentDocGroup = this.groupMappings.get(curDocPresId)!; - currentDocGroup.forEach((doc: Doc, index: number) => { - let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected; - if (selectedButtons[buttonIndex.Navigate]) { - docToJump = doc; - willZoom = false; - } - if (selectedButtons[buttonIndex.Show]) { - docToJump = doc; - willZoom = true; - } - }); - } + let presDocs = DocListCast(this.curPresentation.data); + let nextSelected = presDocs.indexOf(curDoc); + let currentDocGroups: Doc[] = []; + for (; nextSelected < presDocs.length - 1; nextSelected++) { + if (!this.presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + break; + } + currentDocGroups.push(presDocs[nextSelected]); } + + currentDocGroups.forEach((doc: Doc, index: number) => { + if (this.presElementsMappings.get(doc)!.navButton) { + docToJump = doc; + willZoom = false; + } + if (this.presElementsMappings.get(doc)!.showButton) { + docToJump = doc; + willZoom = true; + } + }); + //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - let curDocButtons = this.presElementsMappings.get(curDoc)!.selected; - if (curDocButtons[buttonIndex.Navigate]) { + if (this.presElementsMappings.get(curDoc)!.navButton) { DocumentManager.Instance.jumpToDocument(curDoc, false); - } else if (curDocButtons[buttonIndex.Show]) { + } else if (this.presElementsMappings.get(curDoc)!.showButton) { let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); //awaiting jump so that new scale can be found, since jumping is async await DocumentManager.Instance.jumpToDocument(curDoc, true); @@ -406,69 +320,6 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? //removing the Presentation Element stored for it this.presElementsMappings.delete(removedDoc); - let removedDocPresentId = StrCast(removedDoc.presentId); - - //Removing it from local mapping of the groups - if (this.groupMappings.has(removedDocPresentId)) { - let removedDocsGroup = this.groupMappings.get(removedDocPresentId); - if (removedDocsGroup) { - removedDocsGroup.splice(removedDocsGroup.indexOf(removedDoc), 1); - if (removedDocsGroup.length === 0) { - this.groupMappings.delete(removedDocPresentId); - } - } - } - - //removing it from the backUp of selected Buttons - // let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc)); - // if (castedList) { - // castedList.forEach(async (doc, indexOfDoc) => { - // let curDoc = await doc; - // let curDocId = StrCast(curDoc.docId); - // if (curDocId === removedDoc[Id]) { - // if (castedList) { - // castedList.splice(indexOfDoc, 1); - // return; - // } - // } - // }); - - // } - //removing it from the backUp of selected Buttons - - let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc)); - if (castedList) { - for (let doc of castedList) { - let curDoc = await doc; - let curDocId = StrCast(curDoc.docId); - if (curDocId === removedDoc[Id]) { - castedList.splice(castedList.indexOf(curDoc), 1); - break; - - } - } - } - - //removing it from the backup of groups - let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); - if (castedGroupDocs) { - castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { - let castedKey = StrCast(groupDoc.presentIdStore, null); - if (castedKey === removedDocPresentId) { - let castedGrouping = await DocListCastAsync(groupDoc.grouping); - if (castedGrouping) { - castedGrouping.splice(castedGrouping.indexOf(removedDoc), 1); - if (castedGrouping.length === 0) { - castedGroupDocs!.splice(castedGroupDocs!.indexOf(groupDoc), 1); - } - } - } - - }); - - } - - } } @@ -489,6 +340,7 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? //it'll also execute the necessary actions if presentation is playing. @action public gotoDocument = async (index: number, fromDoc: number) => { + Doc.UnBrushAllDocs(); const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); if (!list) { return; @@ -509,26 +361,7 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? this.hideIfNotPresented(index); this.showAfterPresented(index); } - } - - //Function that is called to resetGroupIds, so that documents get new groupIds at - //first load, when presentation is changed. - resetGroupIds = async () => { - let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); - if (castedGroupDocs !== undefined) { - castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { - let castedGrouping = await DocListCastAsync(groupDoc.grouping); - if (castedGrouping) { - castedGrouping.forEach((doc: Doc) => { - doc.presentId = Utils.GenerateGuid(); - }); - } - }); - } - runInAction(() => this.groupMappings = new Map()); - } - //Function that sets the store of the children docs. @action setChildrenDocs = (docList: Doc[]) => { @@ -580,21 +413,19 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? //The function that starts the presentation, also checking if actions should be applied //directly at start. startPresentation = (startIndex: number) => { - let selectedButtons: boolean[]; this.presElementsMappings.forEach((component: PresentationElement, doc: Doc) => { - selectedButtons = component.selected; - if (selectedButtons[buttonIndex.HideTillPressed]) { + if (component.props.document.hideTillShownButton) { if (this.childrenDocs.indexOf(doc) > startIndex) { doc.opacity = 0; } } - if (selectedButtons[buttonIndex.HideAfter]) { + if (component.props.document.hideAfterButton) { if (this.childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0; } } - if (selectedButtons[buttonIndex.FadeAfter]) { + if (component.props.document.fadeButton) { if (this.childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0.5; } @@ -684,12 +515,9 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps? mainDocument={this.curPresentation} deleteDocument={this.RemoveDoc} gotoDocument={this.gotoDocument} - groupMappings={this.groupMappings} PresElementsMappings={this.presElementsMappings} setChildrenDocs={this.setChildrenDocs} presStatus={this.presStatus} - presButtonBackUp={this.presButtonBackUp} - presGroupBackUp={this.presGroupBackUp} removeDocByRef={this.removeDocByRef} clearElemMap={() => this.presElementsMappings.clear()} /> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 704030d85..3f4ee8960 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -34,7 +34,7 @@ library.add(faVideo); export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) { private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; - private _youtubePlayer: any = undefined; + private _youtubePlayer: YT.Player | undefined = undefined; private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; @@ -78,7 +78,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD @action public Pause = (update: boolean = true) => { this.Playing = false; update && this.player && this.player.pause(); - update && this._youtubePlayer && this._youtubePlayer.pauseVideo(); + update && this._youtubePlayer && this._youtubePlayer.pauseVideo && this._youtubePlayer.pauseVideo(); this._youtubePlayer && this._playTimer && clearInterval(this._playTimer); this._playTimer = undefined; this.updateTimecode(); @@ -244,7 +244,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD let onYoutubePlayerStateChange = (event: any) => runInAction(() => { if (started && event.data === YT.PlayerState.PLAYING) { started = false; - this._youtubePlayer.unMute(); + this._youtubePlayer && this._youtubePlayer.unMute(); this.Pause(); return; } |
