From 3dab85351e1fe862a54294cec70e7139abea0d7d Mon Sep 17 00:00:00 2001 From: Lionel Han <47760119+IGoByJoe@users.noreply.github.com> Date: Mon, 15 Jun 2020 00:10:16 -0700 Subject: added ? to a lot of things --- .../nodes/formattedText/FormattedTextBox.scss | 283 ++++++++++++++++++++- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 2 files changed, 284 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 49114d967..e608f9951 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -37,6 +37,7 @@ position: absolute; } } + .formattedTextBox-outer { position: relative; overflow: auto; @@ -273,4 +274,284 @@ footnote::after { .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; } .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; } .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; } -} \ No newline at end of file +} + +@media only screen and (max-width: 1000px) { + @import "../../globalCssVariables"; + + .ProseMirror { + width: 100%; + height: 100%; + min-height: 100%; + } + + .ProseMirror:focus { + outline: none !important; + } + + .formattedTextBox-cont { + touch-action: none; + cursor: text; + background: inherit; + padding: 0; + border-width: 0px; + border-radius: inherit; + border-color: $intermediate-color; + box-sizing: border-box; + background-color: inherit; + border-style: solid; + overflow-y: auto; + overflow-x: hidden; + color: initial; + max-height: 100%; + display: flex; + flex-direction: row; + transition: opacity 1s; + + .formattedTextBox-dictation { + height: 12px; + width: 10px; + top: 0px; + left: 0px; + position: absolute; + } + } + + .formattedTextBox-outer { + position: relative; + overflow: auto; + display: inline-block; + width: 100%; + height: 100%; + } + + .formattedTextBox-sidebar-handle { + position: absolute; + top: calc(50% - 17.5px); + width: 10px; + height: 35px; + background: lightgray; + border-radius: 20px; + cursor:grabbing; + } + + .formattedTextBox-cont>.formattedTextBox-sidebar-handle { + right: 0; + left: unset; + } + + .formattedTextBox-sidebar, + .formattedTextBox-sidebar-inking { + border-left: dashed 1px black; + height: 100%; + display: inline-block; + position: absolute; + right: 0; + + .collectionfreeformview-container { + position: relative; + } + + >.formattedTextBox-sidebar-handle { + right: unset; + left: -5; + } + } + + .formattedTextBox-sidebar-inking { + pointer-events: all; + } + + .formattedTextBox-inner-rounded { + height: 70%; + width: 85%; + position: absolute; + overflow: auto; + top: 15%; + left: 10%; + } + + .formattedTextBox-inner-rounded, + .formattedTextBox-inner { + height: 100%; + white-space: pre-wrap; + hr { + display: block; + unicode-bidi: isolate; + margin-block-start: 0.5em; + margin-block-end: 0.5em; + margin-inline-start: auto; + margin-inline-end: auto; + overflow: hidden; + border-style: inset; + border-width: 1px; + } + } + + // .menuicon { + // display: inline-block; + // border-right: 1px solid rgba(0, 0, 0, 0.2); + // color: #888; + // line-height: 1; + // padding: 0 7px; + // margin: 1px; + // cursor: pointer; + // text-align: center; + // min-width: 1.4em; + // } + + .strong, + .heading { + font-weight: bold; + } + + .em { + font-style: italic; + } + + .userMarkOpen { + background: rgba(255, 255, 0, 0.267); + display: inline; + } + + .userMark { + background: rgba(255, 255, 0, 0.267); + font-size: 2px; + display: inline-grid; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 10px; + min-height: 10px; + text-align: center; + align-content: center; + } + + footnote { + display: inline-block; + position: relative; + cursor: pointer; + + div { + padding: 0 !important; + } + } + + footnote::after { + content: counter(prosemirror-footnote); + vertical-align: super; + font-size: 75%; + counter-increment: prosemirror-footnote; + } + + .ProseMirror { + counter-reset: prosemirror-footnote; + } + + .footnote-tooltip { + cursor: auto; + font-size: 75%; + position: absolute; + left: -30px; + top: calc(100% + 10px); + background: silver; + padding: 3px; + border-radius: 2px; + max-width: 100px; + min-width: 50px; + width: max-content; + } + + .prosemirror-attribution { + font-size: 8px; + } + + .footnote-tooltip::before { + border: 5px solid silver; + border-top-width: 0px; + border-left-color: transparent; + border-right-color: transparent; + position: absolute; + top: -5px; + left: 27px; + content: " "; + height: 0; + width: 0; + } + + + .formattedTextBox-inlineComment { + position: relative; + width: 40px; + height: 20px; + &::before { + content: "→"; + } + &:hover { + background: orange; + } + } + + .formattedTextBox-summarizer { + opacity: 0.5; + position: relative; + width: 40px; + height: 20px; + &::after { + content: "←"; + } + } + + .formattedTextBox-summarizer-collapsed { + opacity: 0.5; + position: relative; + width: 40px; + height: 20px; + &::after { + content: "..."; + } + } + + .ProseMirror { + touch-action: none; + span { + font-family: inherit; + } + + ol, ul { + counter-reset: deci1 0 multi1 0; + padding-left: 1em; + font-family: inherit; + } + ol { + margin-left: 1em; + font-family: inherit; + } + + .decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; } + .decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;} + .decimal3-ol { counter-reset: deci3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;} + .decimal4-ol { counter-reset: deci4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3em;} + .decimal5-ol { counter-reset: deci5; p {display: inline; font-family: inherit} font-size: smaller; } + .decimal6-ol { counter-reset: deci6; p {display: inline; font-family: inherit} font-size: smaller; } + .decimal7-ol { counter-reset: deci7; p {display: inline; font-family: inherit} font-size: smaller; } + + .multi1-ol { counter-reset: multi1; p {display: inline; font-family: inherit} margin-left: 0; padding-left: 1.2em } + .multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;} + .multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;} + .multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;} + + .decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em; width: 1em; content: counter(deci1) ". "; } + .decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; } + .decimal3:before { transition: 0.5s;counter-increment: deci3; display: inline-block; margin-left: -2.85em;width: 2.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) ". "; } + .decimal4:before { transition: 0.5s;counter-increment: deci4; display: inline-block; margin-left: -3.85em;width: 3.85em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) ". "; } + .decimal5:before { transition: 0.5s;counter-increment: deci5; display: inline-block; margin-left: -2em; width: 5em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) ". "; } + .decimal6:before { transition: 0.5s;counter-increment: deci6; display: inline-block; margin-left: -2em; width: 6em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) ". "; } + .decimal7:before { transition: 0.5s;counter-increment: deci7; display: inline-block; margin-left: -2em; width: 7em; content: counter(deci1) "."counter(deci2) "."counter(deci3) "."counter(deci4) "."counter(deci5) "."counter(deci6) "."counter(deci7) ". "; } + + .multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; } + .multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; } + .multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; } + .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; } + } +} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 1fab54d7e..b0b648b66 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -188,7 +188,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } const state = this._editorView.state.apply(tx); - this._editorView.updateState(state); + this?._editorView?.updateState(state); (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks); const tsel = this._editorView.state.selection.$from; @@ -1213,7 +1213,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground; if (this.props.isSelected()) { - this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props); + this._editorView && RichTextMenu.Instance?.updateFromDash(this._editorView, undefined, this.props); } else if (FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } -- cgit v1.2.3-70-g09d2 From 4b1dc65c9e0bb917c1f1f780f8a63fc0c91b4ad6 Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Tue, 16 Jun 2020 23:53:16 +0800 Subject: mobile menu bug fixes + doc button fix --- deploy/mobile/image.html | 1 + src/client/util/CurrentUserUtils.ts | 73 ++++++++++++---------- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/PresBox.scss | 5 ++ src/client/views/nodes/RadialMenu.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- .../views/presentationview/PresElementBox.scss | 2 +- src/mobile/MobileInterface.tsx | 69 +++++++------------- src/mobile/MobileMenu.scss | 4 +- 10 files changed, 77 insertions(+), 90 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html index d30ad6ac2..718550f02 100644 --- a/deploy/mobile/image.html +++ b/deploy/mobile/image.html @@ -3,6 +3,7 @@ Dash Mobile + diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 323e398be..9ce162897 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -55,6 +55,15 @@ export class CurrentUserUtils { }); } + if (doc["template-mobile-button"] === undefined) { + const queryTemplate = this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, _backgroundColor: "lightgrey" }, [this.ficon({ ignoreClick: true, icon: "mobile", backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); + doc["template-mobile-button"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "mobile button", icon: "mobile" + }); + } + if (doc["template-button-slides"] === undefined) { const slideTemplate = Docs.Create.MultirowDocument( [ @@ -165,16 +174,6 @@ export class CurrentUserUtils { }); } - // if (doc["mobile-button"] === undefined) { - // const mobileTemplate = this.mobileButton({ title: "mobile button", _backgroundColor: "lightgrey" }, [this.ficon({ ignoreClick: true, icon: "mobile", backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "text"), this.mobileButtonInfo({}, "This is a default mobile button for use in the mobile menu")])]); - // mobileTemplate.isTemplateDoc = makeTemplate(mobileTemplate); - // doc["mobile-button"] = CurrentUserUtils.ficon({ - // onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - // dragFactory: new PrefetchProxy(mobileTemplate) as any as Doc, - // removeDropProperties: new List(["dropAction"]), title: "mobile button view", icon: "mobile" - // }); - // } - if (doc["template-button-detail"] === undefined) { const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; @@ -229,6 +228,7 @@ export class CurrentUserUtils { doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc, + doc["template-mobile-button"] as Doc, doc["template-button-detail"] as Doc, doc["template-button-link"] as Doc, doc["template-button-switch"] as Doc]; @@ -378,9 +378,8 @@ export class CurrentUserUtils { if (doc.emptyWebpage === undefined) { doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 600, UseCors: true }); } - if (doc.activeMobile === undefined) { - console.log("phone setup"); - this.setupActiveMobile(doc); + if (doc.activeMobileMenu === undefined) { + this.setupActiveMobileMenu(doc); } return [ { title: "Drag a comparison box", label: "Comp", icon: "columns", ignoreClick: true, drag: 'Docs.Create.ComparisonDocument()' }, @@ -395,7 +394,7 @@ export class CurrentUserUtils { { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, - { title: "Drag a mobile view", label: "Phone", icon: "mobile", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.activeMobile as Doc }, + { title: "Drag a mobile view", label: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, { title: "Drag an instance of the device collection", label: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' }, // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc }, // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc }, @@ -449,12 +448,12 @@ export class CurrentUserUtils { return doc.myItemCreators as Doc; } - static setupActiveMobile(doc: Doc) { - if (doc.activeMobile === undefined) { + static setupActiveMobileMenu(doc: Doc) { + if (doc.activeMobileMenu === undefined) { console.log("undefined"); - doc.activeMobile = this.setupMobileMenu(); + doc.activeMobileMenu = this.setupMobileMenu(); } - return doc.activeMobile as Doc; + return doc.activeMobileMenu as Doc; } static setupMobileMenu() { @@ -466,37 +465,45 @@ export class CurrentUserUtils { static setupMobileButtons(doc?: Doc, buttons?: string[]) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ - { title: "LIBRARY", icon: "bars", click: 'switchToLibrary()', backgroundColor: "#ffd6d6", info: "Navigate and access all of your documents within their respective collections" }, - { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "#ffbfbf", info: "Use your mobile to record audio and access it on Dash Web." }, - { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "#ff9e9e", info: "Upload an image from your mobile device so it can be accessed on Dash Web" }, + { title: "LIBRARY", icon: "bars", click: 'switchToLibrary()', backgroundColor: "#ffd6d6", info: "Navigate and access all of your documents within their respective collections." }, + { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "#ffbfbf", info: "Use your phone to record and upload audio onto Dash Web." }, + { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "#ff9e9e", info: "Upload images or videos from your mobile device so they can be accessed on Dash Web." }, { title: "PRESENTATION", icon: "desktop", click: 'openMobilePresentation()', backgroundColor: "#ff8080", info: "Use your phone as a remote for you presentation." }, - { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "#ff5e5e", info: "Change your password, log out, or manage your account security" } + { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "#ff5e5e", info: "Change your password, log out, or manage your account security." } ]; - return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => this.mobileButton({ title: data.title, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, _backgroundColor: data.backgroundColor }, [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])])); + return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => + this.mobileButton({ + title: data.title, + lockedPosition: true, + onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + _backgroundColor: data.backgroundColor + }, + [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)" }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) + ); } - static mobileButton = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.MulticolumnDocument(docs, { + static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { ...opts, dropAction: undefined, removeDropProperties: new List(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, - borderRounding: "5px", boxShadow: "0 0", _chromeStatus: "disabled", - })) as any as Doc + borderRounding: "5px", boxShadow: "0 0", _chromeStatus: "disabled" + }) as any as Doc - static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.MultirowDocument(docs, { + static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { ...opts, dropAction: undefined, removeDropProperties: new List(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", _chromeStatus: "disabled", ignoreClick: true - })) as any as Doc + }) as any as Doc - static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => new PrefetchProxy(Docs.Create.TextDocument(buttonTitle, { + static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { ...opts, dropAction: undefined, title: buttonTitle, _fontSize: 37, _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)" - })) as any as Doc + }) as any as Doc - static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => new PrefetchProxy(Docs.Create.TextDocument(buttonInfo, { + static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { ...opts, dropAction: undefined, title: "info", _fontSize: 25, _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, - })) as any as Doc + }) as any as Doc static setupThumbButtons(doc: Doc) { @@ -795,7 +802,7 @@ export class CurrentUserUtils { this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupDocTemplates(doc); // sets up the template menu of templates this.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing - this.setupActiveMobile(doc); + this.setupActiveMobileMenu(doc); this.setupOverlays(doc); // documents in overlay layer this.setupDockedButtons(doc); // the bottom bar of font icons this.setupDefaultPresentation(doc); // presentation that's initially triggered diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 0a1b03522..bd0b69da8 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -476,7 +476,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) transformOrigin: "top left", }} onScroll={action(e => { - if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll; + if (!this.props.isSelected() && window.innerWidth > 1000) e.currentTarget.scrollTop = this._scroll; else this._scroll = e.currentTarget.scrollTop; })} onDrop={this.onExternalDrop.bind(this)} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1a935d9b0..2e0be1c6a 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -228,7 +228,7 @@ export class AudioBox extends ViewBoxBaseComponent
- +
*/} - {/* */} -
- {this.renderAllGroups(groups)} -
- - ); - } else { - return ( - this._editingLink = undefined)}> - ); - } + return
this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}> + {!this._editingLink ? + this.renderAllGroups(groups) : + this._editingLink = undefined)} /> + } +
; } } \ No newline at end of file diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 43dd698ad..b5d113ca0 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -175,76 +175,29 @@ @media only screen and (max-device-width: 480px) { - - .audiobox-container, - .audiobox-container-interactive { - width: max-content; - /* align-self: center; */ - left: 50%; - top: 40%; - transform: translate(-50%, 0); - height: 100px; - position: absolute; - display: flex; - pointer-events: all; - /* cursor: default; */ - } - - .audiobox-container .audiobox-buttons, - .audiobox-container-interactive .audiobox-buttons { - display: flex; - width: 500px; - /* left: 50%; */ - /* transform: translate(-50%,0); */ - align-items: center; - } - .audiobox-dictation { + font-size: 5em; + display: flex; width: 100; - height: 100; - font-size: 50; - /* background-color: white; */ + justify-content: center; + flex-direction: column; + align-items: center; } - .audiobox-container .audiobox-record-interactive, - .audiobox-container-interactive .audiobox-record-interactive { - width: 95%; - user-select: none; - font-size: 50px; - font-weight: 100; - left: 5%; + .audiobox-container .audiobox-record, + .audiobox-container-interactive .audiobox-record { + pointer-events: all; + font-size: 3em; + width: 100%; height: 100%; position: relative; + pointer-events: none; } .audiobox-container .audiobox-controls .audiobox-player .audiobox-playhead, .audiobox-container .audiobox-controls .audiobox-player .audiobox-dictation, .audiobox-container-interactive .audiobox-controls .audiobox-player .audiobox-playhead, .audiobox-container-interactive .audiobox-controls .audiobox-player .audiobox-dictation { - position: relative; - align-self: center; - width: 50px; - height: max-content; - padding: 0; - } - - .audiobox-container .audiobox-controls .audiobox-player, - .audiobox-container-interactive .audiobox-controls .audiobox-player { - margin-top: auto; - margin-bottom: auto; - width: 100%; - height: 100%; - position: relative; - padding-right: 5px; - display: flex; - } - - .audiobox-container .audiobox-controls, - .audiobox-container-interactive .audiobox-controls { - width: 500px; - height: 100%; - position: relative; - display: flex; - padding-left: 2px; + width: 70px; } } \ No newline at end of file diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index cb0582d87..d5288fff6 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -193,7 +193,7 @@ export class AudioBox extends ViewBoxBaseComponent { + private _linkButton = React.createRef(); + + @action + onLinkButtonMoved = (e: PointerEvent) => { + if (this._linkButton.current !== null) { + const linkDrag = UndoManager.StartBatch("Drag Link"); + this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, e.pageX, e.pageY, { + dragComplete: dropEv => { + const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop + if (this.props.View && linkDoc) { + !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink"); + + // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks) + // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures + // however, the dropped document isn't so accessible. What we do is set the newly created link document on the documentView + // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it. + dropEv.linkDragData?.linkDropCallback?.(dropEv.linkDragData); + runInAction(() => this.props.View._link = linkDoc); + setTimeout(action(() => this.props.View._link = undefined), 0); + } + linkDrag?.end(); + }, + hideSource: false + }); + return true; + } + return false; + } + + @observable static StartLink: DocumentView | undefined; + onLinkButtonDown = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { + if (doubleTap) { + DocumentLinksButton.StartLink = this.props.View; + } else { + DocumentLinksButton.EditLink = this.props.View; + DocumentLinksButton.EditLinkLoc = [e.clientX + 10, e.clientY]; + } + })); + } + completeLink = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e, doubleTap) => { + if (doubleTap) { + if (DocumentLinksButton.StartLink === this.props.View) { + DocumentLinksButton.StartLink = undefined; + } else { + DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View && + DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag"); + } + } + })); + } + + @observable + public static EditLink: DocumentView | undefined; + public static EditLinkLoc: number[] = [0, 0]; + + @computed + get linkButton() { + const links = DocListCast(this.props.View.props.Document.links); + return (!links.length || links[0].hidden) && !this.props.AlwaysOn ? (null) : +
+
LinkDocPreview.LinkInfo = undefined)} + onPointerEnter={action(e => LinkDocPreview.LinkInfo = { + addDocTab: this.props.View.props.addDocTab, + linkSrc: this.props.View.props.Document, + linkDoc: links[0], + Location: [e.clientX, e.clientY + 20] + })} > + {links.length ? links.length : } +
+ {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
: (null)} + {DocumentLinksButton.StartLink === this.props.View ?
: (null)} +
; + } + render() { + return this.linkButton; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d31ec8c1a..afd28534e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -42,6 +42,7 @@ import { LinkAnchorBox } from './LinkAnchorBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); import { MobileInterface } from '../../../mobile/MobileInterface'; +import { DocumentLinksButton } from './DocumentLinksButton'; library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -585,10 +586,14 @@ export class DocumentView extends DocComponent(Docu toggleLinkButtonBehavior = (): void => { if (this.Document.isLinkButton || this.layoutDoc.onClick || this.Document.ignoreClick) { this.Document.isLinkButton = false; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = false); this.Document.ignoreClick = false; this.layoutDoc.onClick = undefined; } else { this.Document.isLinkButton = true; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = true); this.Document.followLinkZoom = false; this.Document.followLinkLocation = undefined; } @@ -598,8 +603,12 @@ export class DocumentView extends DocComponent(Docu toggleFollowInPlace = (): void => { if (this.Document.isLinkButton) { this.Document.isLinkButton = false; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = false); } else { this.Document.isLinkButton = true; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = true); this.Document.followLinkZoom = true; this.Document.followLinkLocation = "inPlace"; } @@ -609,6 +618,8 @@ export class DocumentView extends DocComponent(Docu toggleFollowOnRight = (): void => { if (this.Document.isLinkButton) { this.Document.isLinkButton = false; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = false); } else { this.Document.isLinkButton = true; this.Document.followLinkZoom = false; @@ -737,10 +748,8 @@ export class DocumentView extends DocComponent(Docu const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - if (!options) { - options = { description: "Options...", subitems: optionItems, icon: "compass" }; - cm?.addItem(options); - } + optionItems.push({ description: "Toggle Show Each Link Dot", event: () => this.layoutDoc.showLinks = !this.layoutDoc.showLinks, icon: "eye" }); + !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); const existingOnClick = cm?.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -1039,7 +1048,8 @@ export class DocumentView extends DocComponent(Docu select={this.select} onClick={this.onClickHandler} layoutKey={this.finalLayoutKey} /> - {this.anchors} + {this.layoutDoc.showLinks ? this.anchors : (null)} + {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.props.dontRegisterView ? (null) : }
); } @@ -1063,7 +1073,7 @@ export class DocumentView extends DocComponent(Docu anchorPanelHeight = () => this.props.PanelHeight() || 1; @computed get anchors() { TraceMobx(); - return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => + return this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.presBox || this.props.dontRegisterView ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => (Docu PanelWidth={this.anchorPanelWidth} PanelHeight={this.anchorPanelHeight} ContentScaling={returnOne} + dontRegisterView={false} forcedBackgroundColor={returnTransparent} removeDocument={this.hideLinkAnchor} pointerEvents={false} @@ -1194,7 +1205,10 @@ export class DocumentView extends DocComponent(Docu color: StrCast(this.layoutDoc.color, "inherit"), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, - boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined, + boxShadow: this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ? + StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") : + this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : + undefined, background: finalColor, opacity: finalOpacity, fontFamily: StrCast(this.Document._fontFamily, "inherit"), diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 2b64cdab6..2bcc6168b 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -18,6 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SelectionManager } from "../../util/SelectionManager"; import { TraceMobx } from "../../../fields/util"; import { Id } from "../../../fields/FieldSymbols"; +import { LinkDocPreview } from "./LinkDocPreview"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -132,6 +133,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent LinkDocPreview.LinkInfo = undefined)} + onPointerEnter={action(e => LinkDocPreview.LinkInfo = { + addDocTab: this.props.addDocTab, + linkSrc: this.props.ContainingCollectionDoc!, + linkDoc: this.rootDoc, + Location: [e.clientX, e.clientY + 20] + })} onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu} ref={this._ref} style={{ background: c, diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx new file mode 100644 index 000000000..126dc240a --- /dev/null +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -0,0 +1,107 @@ +import { action, computed, observable, runInAction } from 'mobx'; +import { observer } from "mobx-react"; +import wiki from "wikijs"; +import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Cast, FieldValue, NumCast } from "../../../fields/Types"; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { Transform } from "../../util/Transform"; +import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; +import React = require("react"); +import { DocumentView } from './DocumentView'; + +interface Props { + linkDoc?: Doc; + linkSrc?: Doc; + href?: string; + backgroundColor: (doc: Doc) => string; + addDocTab: (document: Doc, where: string) => boolean; + location: number[]; +} +@observer +export class LinkDocPreview extends React.Component { + @observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>; + @observable _targetDoc: Opt; + @observable _toolTipText = ""; + + componentDidUpdate() { this.updatePreview() } + componentDidMount() { this.updatePreview() } + async updatePreview() { + const linkDoc = this.props.linkDoc; + const linkSrc = this.props.linkSrc; + if (this.props.href) { + if (this.props.href.startsWith("https://en.wikipedia.org/wiki/")) { + wiki().page(this.props.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500)))); + } else { + runInAction(() => this._toolTipText = "external => " + this.props.href); + } + } else if (linkDoc && linkSrc) { + const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), linkSrc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc); + const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor; + runInAction(() => { + this._toolTipText = ""; + this._targetDoc = target; + if (anchor !== this._targetDoc && anchor && this._targetDoc) { + this._targetDoc._scrollY = NumCast(anchor?.y); + } + }); + } + } + pointerDown = (e: React.PointerEvent) => { + if (this.props.linkDoc && this.props.linkSrc) { + DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc, + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + } else if (this.props.href) { + this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight"); + } + } + width = () => Math.min(350, NumCast(this._targetDoc?.[WidthSym](), 350)); + height = () => Math.min(350, NumCast(this._targetDoc?.[HeightSym](), 350)); + @computed get targetDocView() { + return !this._targetDoc ? +
+
+ {this._toolTipText} +
+
: + ; + } + + render() { + return
+ {this.targetDocView} +
; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 5e32b3ef8..839943aac 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -11,7 +11,7 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc } from "../../../../fields/Doc"; import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField'; -import { Cast, StrCast } from "../../../../fields/Types"; +import { Cast, StrCast, BoolCast } from "../../../../fields/Types"; import { unimplementedFunction, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { LinkManager } from "../../../util/LinkManager"; @@ -72,7 +72,7 @@ export default class RichTextMenu extends AntimodeMenu { super(props); RichTextMenu.Instance = this; this._canFade = false; - this.Pinned = true; + this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, @@ -147,7 +147,6 @@ export default class RichTextMenu extends AntimodeMenu { this.updateFromDash(view, lastState, this.editorProps); } - public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { if (this.view) { const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId }); @@ -750,7 +749,7 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleMenuPin = (e: React.MouseEvent) => { - this.Pinned = !this.Pinned; + Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned; if (!this.Pinned) { this.fadeOut(true); } @@ -779,7 +778,7 @@ export default class RichTextMenu extends AntimodeMenu { this.createHighlighterButton(), this.createLinkButton(), this.createBrushButton(), - this.createButton("indent", "Summarize", undefined, this.insertSummarizer), + this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), ]}
; diff --git a/src/mobile/AudioUpload.scss b/src/mobile/AudioUpload.scss index 9fe442e55..6e64d9e2e 100644 --- a/src/mobile/AudioUpload.scss +++ b/src/mobile/AudioUpload.scss @@ -5,8 +5,45 @@ justify-content: center; flex-direction: column; align-items: center; - max-width: 400px; - min-width: 400px; + margin-top: 10px; + height: 400px; + width: 600px; +} + +.upload_label { + position: relative; + font-weight: 700; + color: black; + background-color: rgba(0, 0, 0, 0); + border: solid 3px black; + margin: 10px; + font-size: 30; + height: 70px; + width: 60%; + display: inline-flex; + font-family: sans-serif; + text-transform: uppercase; + justify-content: center; + flex-direction: column; + border-radius: 10px; +} + +.restart_label { + position: relative; + font-weight: 700; + color: black; + background-color: rgba(0, 0, 0, 0); + border: solid 3px black; + margin: 10px; + font-size: 30; + height: 70px; + width: 60%; + display: inline-flex; + font-family: sans-serif; + text-transform: uppercase; + justify-content: center; + flex-direction: column; + border-radius: 10px; } .audio-upload { diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx index 7ea11ee84..f859b0ba3 100644 --- a/src/mobile/AudioUpload.tsx +++ b/src/mobile/AudioUpload.tsx @@ -5,11 +5,11 @@ import "./ImageUpload.scss"; import React = require('react'); import { DocServer } from '../client/DocServer'; import { observer } from 'mobx-react'; -import { observable, action } from 'mobx'; -import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue } from '../Utils'; +import { observable, action, computed } from 'mobx'; +import { Utils, emptyPath, returnFalse, emptyFunction, returnOne, returnZero, returnTrue, returnEmptyFilter } from '../Utils'; import { Networking } from '../client/Network'; import { Doc, Opt } from '../fields/Doc'; -import { Cast } from '../fields/Types'; +import { Cast, FieldValue } from '../fields/Types'; import { listSpec } from '../fields/Schema'; import { List } from '../fields/List'; import { Scripting } from '../client/util/Scripting'; @@ -38,185 +38,100 @@ export class AudioUpload extends React.Component { @observable status: string = ""; @observable nm: string = "Choose files"; @observable process: string = ""; + @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc; - onClick = async () => { - try { - await Docs.Prototypes.initialize(); - const imgPrev = document.getElementById("img_preview"); - const slab1 = document.getElementById("slab1"); - if (slab1) { - slab1.style.opacity = "1"; - } - if (imgPrev) { - const files: FileList | null = inputRef.current!.files; - const slab2 = document.getElementById("slab2"); - if (slab2) { - slab2.style.opacity = "1"; - } - if (files && files.length !== 0) { - this.process = "Uploading Files"; - for (let index = 0; index < files.length; ++index) { - const file = files[index]; - const res = await Networking.UploadFilesToServer(file); - const slab3 = document.getElementById("slab3"); - if (slab3) { - slab3.style.opacity = "1"; - } - res.map(async ({ result }) => { - const name = file.name; - if (result instanceof Error) { - return; - } - const path = Utils.prepend(result.accessPaths.agnostic.client); - let doc = null; - console.log("type: " + file.type); - if (file.type === "video/mp4") { - doc = Docs.Create.VideoDocument(path, { _nativeWidth: 200, _width: 200, title: name }); - } else if (file.type === "application/pdf") { - doc = Docs.Create.PdfDocument(path, { _width: 200, title: name }); - } else { - doc = Docs.Create.ImageDocument(path, { _nativeWidth: 200, _width: 200, title: name }); - } - const slab4 = document.getElementById("slab4"); - if (slab4) { - slab4.style.opacity = "1"; - } - const res = await rp.get(Utils.prepend("/getUserDocumentId")); - if (!res) { - throw new Error("No user id returned"); - } - const field = await DocServer.GetRefField(res); - let pending: Opt; - if (field instanceof Doc) { - pending = await Cast(field.mobileUpload, Doc); - } - if (pending) { - const data = await Cast(pending.data, listSpec(Doc)); - if (data) { - data.push(doc); - } else { - pending.data = new List([doc]); - } - this.status = "finished"; - const slab5 = document.getElementById("slab5"); - if (slab5) { - slab5.style.opacity = "1"; - } - this.process = "File " + (index + 1).toString() + " Uploaded"; - const slab6 = document.getElementById("slab6"); - if (slab6) { - slab6.style.opacity = "1"; - } - const slab7 = document.getElementById("slab7"); - if (slab7) { - slab7.style.opacity = "1"; - } - - } - }); - } - } else { - this.process = "No file selected"; - } - setTimeout(this.clearUpload, 3000); - } - } catch (error) { - this.error = JSON.stringify(error); - } - } - - // Updates label after a files is selected (so user knows a file is uploaded) - inputLabel = async () => { - const files: FileList | null = inputRef.current!.files; - await files; - if (files && files.length === 1) { - console.log(files); - this.nm = files[0].name; - } else if (files && files.length > 1) { - console.log(files.length); - this.nm = files.length.toString() + " files selected"; - } - } @action clearUpload = () => { - const slab1 = document.getElementById("slab1"); - if (slab1) { - slab1.style.opacity = "0.4"; - } - const slab2 = document.getElementById("slab2"); - if (slab2) { - slab2.style.opacity = "0.4"; - } - const slab3 = document.getElementById("slab3"); - if (slab3) { - slab3.style.opacity = "0.4"; + for (let i = 1; i < 8; i++) { + this.setOpacity(i, "0.2"); } - const slab4 = document.getElementById("slab4"); - if (slab4) { - slab4.style.opacity = "0.4"; - } - const slab5 = document.getElementById("slab5"); - if (slab5) { - slab5.style.opacity = "0.4"; - } - const slab6 = document.getElementById("slab6"); - if (slab6) { - slab6.style.opacity = "0.4"; - } - const slab7 = document.getElementById("slab7"); - if (slab7) { - slab7.style. - opacity = "0.4"; - } - this.nm = "Choose files"; - - if (inputRef.current) { - inputRef.current.value = ""; - } - this.process = ""; - console.log(inputRef.current!.files); + this._audioCol = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc; } - + closeUpload = () => { + this.clearUpload(); + MobileInterface.Instance.toggleAudio(); + } private get uploadInterface() { - const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio" }), Doc) as Doc; - return ( -
-
+ <> +
this.closeUpload()}>
- 1000} - PanelHeight={() => 1000} - NativeHeight={returnZero} - NativeWidth={returnZero} - renderDepth={0} - focus={emptyFunction} - backgroundColor={() => "white"} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - /> -
+ +
+ 600} + PanelHeight={() => 400} + NativeHeight={returnZero} + NativeWidth={returnZero} + renderDepth={0} + focus={emptyFunction} + backgroundColor={() => "rgba(0,0,0,0)"} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> +
+
+ Restart +
+
+ Upload +
+
+
+
+
+
+
+
+
+
+ ); } + setOpacity = (i: number, o: string) => { + const slab = document.getElementById("slab0" + i); + if (slab) { + console.log(slab?.id); + slab.style.opacity = o; + } + } + + // Pushing the audio doc onto Dash Web through the right side bar + uploadAudio = () => { + console.log("uploading"); + const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; + const audioDoc = this._audioCol; + const data = Cast(audioRightSidebar.data, listSpec(Doc)); + for (let i = 1; i < 8; i++) { + setTimeout(() => this.setOpacity(i, "1"), i * 200); + } + if (data) { + data.push(audioDoc); + } + + setTimeout(this.clearUpload, 3000); + } + @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss index b64aac338..890258918 100644 --- a/src/mobile/ImageUpload.scss +++ b/src/mobile/ImageUpload.scss @@ -9,13 +9,19 @@ min-width: 400px; .upload_label { - font-size: 3em; font-weight: 700; - color: white; - background-color: black; - display: inline-block; - margin: 10; - width: 100%; + color: black; + background-color: rgba(0, 0, 0, 0); + border: solid 3px black; + margin: 10px; + font-size: 30; + height: 70px; + width: 80%; + display: flex; + font-family: sans-serif; + text-transform: uppercase; + justify-content: center; + flex-direction: column; border-radius: 10px; } @@ -26,10 +32,6 @@ direction: ltr; } - .upload_label:hover { - background-color: darkred; - } - .button_file { text-align: center; height: 50%; @@ -49,20 +51,31 @@ } .inputfile+label { - font-size: 3em; font-weight: 700; - color: white; - background-color: black; - display: inline-block; + color: black; + background-color: rgba(0, 0, 0, 0); + border: solid 3px black; margin: 10px; + font-size: 30; + height: 70px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; margin-top: 30px; - width: 100%; + width: 80%; + display: flex; + font-family: sans-serif; + text-transform: uppercase; + justify-content: center; + flex-direction: column; border-radius: 10px; } - .inputfile:focus+label, - .inputfile+label:hover { - background-color: darkred; + .inputfile.active+label { + font-style: italic; + color: black; + background-color: lightgreen; + border: solid 3px darkgreen; } .status { @@ -71,16 +84,6 @@ } -.backgroundUpload { - height: 100vh; - top: 0; - z-index: 999; - width: 100vw; - position: absolute; - background-color: lightgrey; - opacity: 0.4; -} - .image-upload { top: 100%; opacity: 0; @@ -129,7 +132,7 @@ height: 30px; margin: 10; border-radius: 20px; - opacity: 0.3; + opacity: 0.2; background-color: black; transition: all 2s, opacity 1.5s; } \ No newline at end of file diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index b712d52cc..6a5a2dd5b 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -43,21 +43,18 @@ export class Uploader extends React.Component { await Docs.Prototypes.initialize(); const imgPrev = document.getElementById("img_preview"); // Slab 1 - const slab1 = document.getElementById("slab1"); - if (slab1) slab1.style.opacity = "1"; + this.setOpacity(1, "1"); if (imgPrev) { const files: FileList | null = inputRef.current!.files; // Slab 2 - const slab2 = document.getElementById("slab2"); - if (slab2) slab2.style.opacity = "1"; + this.setOpacity(2, "1"); if (files && files.length !== 0) { this.process = "Uploading Files"; for (let index = 0; index < files.length; ++index) { const file = files[index]; const res = await Networking.UploadFilesToServer(file); // Slab 3 - const slab3 = document.getElementById("slab3"); - if (slab3) slab3.style.opacity = "1"; + this.setOpacity(3, "1"); res.map(async ({ result }) => { const name = file.name; if (result instanceof Error) { @@ -74,8 +71,7 @@ export class Uploader extends React.Component { doc = Docs.Create.ImageDocument(path, { _nativeWidth: 400, _width: 400, title: name }); } // Slab 4 - const slab4 = document.getElementById("slab4"); - if (slab4) slab4.style.opacity = "1"; + this.setOpacity(4, "1"); const res = await rp.get(Utils.prepend("/getUserDocumentId")); if (!res) { throw new Error("No user id returned"); @@ -94,13 +90,15 @@ export class Uploader extends React.Component { if (data) data.push(doc); else pending.data = new List([doc]); this.status = "finished"; - const slab5 = document.getElementById("slab5"); - if (slab5) slab5.style.opacity = "1"; + this.setOpacity(5, "1"); // Slab 5 this.process = "File " + (index + 1).toString() + " Uploaded"; - const slab6 = document.getElementById("slab6"); - if (slab6) slab6.style.opacity = "1"; - const slab7 = document.getElementById("slab7"); - if (slab7) slab7.style.opacity = "1"; + this.setOpacity(6, "1"); // Slab 6 + this.setOpacity(7, "1"); // Slab 7 + } + console.log("i: " + index + 1); + console.log("l: " + files.length); + if ((index + 1) === files.length) { + this.process = "Uploads Completed"; } }); } @@ -129,20 +127,9 @@ export class Uploader extends React.Component { @action clearUpload = () => { - const slab1 = document.getElementById("slab1"); - if (slab1) slab1.style.opacity = "0.4"; - const slab2 = document.getElementById("slab2"); - if (slab2) slab2.style.opacity = "0.4"; - const slab3 = document.getElementById("slab3"); - if (slab3) slab3.style.opacity = "0.4"; - const slab4 = document.getElementById("slab4"); - if (slab4) slab4.style.opacity = "0.4"; - const slab5 = document.getElementById("slab5"); - if (slab5) slab5.style.opacity = "0.4"; - const slab6 = document.getElementById("slab6"); - if (slab6) slab6.style.opacity = "0.4"; - const slab7 = document.getElementById("slab7"); - if (slab7) slab7.style.opacity = "0.4"; + for (let i = 1; i < 8; i++) { + this.setOpacity(i, "0.2"); + } this.nm = "Choose files"; if (inputRef.current) { @@ -152,24 +139,33 @@ export class Uploader extends React.Component { console.log(inputRef.current!.files); } + closeUpload = () => { + this.clearUpload(); + MobileInterface.Instance.toggleUpload(); + } + + setOpacity = (i: number, o: string) => { + const slab = document.getElementById("slab" + i); + if (slab) { + console.log(slab?.id); + slab.style.opacity = o; + } + } private get uploadInterface() { return (
-
+
this.closeUpload()}>
- + +
- -     Upload + Upload
- {/*
Upload
*/} - {/*

{this.status}

-

{this.error}

*/}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index a1719c015..5894326ab 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { library } from '@fortawesome/fontawesome-svg-core'; import { - faTasks, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, + faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, @@ -43,8 +43,9 @@ import { AudioUpload } from "./AudioUpload"; import { Cast, FieldValue } from '../fields/Types'; import { CollectionView } from '../client/views/collections/CollectionView'; import { InkingStroke } from '../client/views/InkingStroke'; +import RichTextMenu from "../client/views/nodes/formattedText/RichTextMenu"; -library.add(faTasks, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, +library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, @@ -387,13 +388,13 @@ export class MobileInterface extends React.Component { return (
this.handleClick(doc)}> -
{doc.title}
-
{doc.type}
- + key={index}> +
this.handleClick(doc)}> {doc.title}
+
this.handleClick(doc)}>{doc.type}
+ this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === "collection" ? "block" : "none"}` }} /> this.openFromSidebar(doc)} icon="external-link-alt" size="lg" /> -
); +
+ ); } }); @@ -440,7 +441,7 @@ export class MobileInterface extends React.Component { } /** - * Handles the Create New Workspace button in the menu + * Handles the Create New Workspace button in the menu (taken from MainView.tsx) */ @action createNewWorkspace = async (id?: string) => { @@ -759,12 +760,7 @@ export class MobileInterface extends React.Component { toggle = this.toggleUpload; } return ( -
-
- -
- -
+ ); } @@ -815,6 +811,7 @@ export class MobileInterface extends React.Component { {this.switchMenuView()} {this.inkMenu()} +
{this.pinToPresentation()} {this.downloadDocument()} @@ -843,6 +840,6 @@ Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.I Scripting.addGlobal(function toggleMobileSidebar() { return MobileInterface.Instance.toggleSidebar(); }); Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); }); Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); }); -Scripting.addGlobal(function openWorkspaces() { return MobileInterface.Instance.openWorkspaces(); }); +Scripting.addGlobal(function openMobileWorkspaces() { return MobileInterface.Instance.openWorkspaces(); }); Scripting.addGlobal(function uploadImageMobile() { return MobileInterface.Instance.toggleUpload(); }); Scripting.addGlobal(function switchToMobileUploads() { return MobileInterface.Instance.switchToMobileUploads(); }); diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss index 53bc48034..c499e6d1e 100644 --- a/src/mobile/MobileMenu.scss +++ b/src/mobile/MobileMenu.scss @@ -176,6 +176,7 @@ body { .open { right: 20px; + font-size: 35; position: absolute; } -- cgit v1.2.3-70-g09d2 From 48e841282b896a0961d595281211b276b7a9d345 Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Fri, 26 Jun 2020 22:06:33 +0800 Subject: audio dictation --- src/client/util/CurrentUserUtils.ts | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 + src/mobile/AudioUpload.tsx | 10 +- src/mobile/MobileInterface.tsx | 116 +++++++-------------- 4 files changed, 48 insertions(+), 84 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 823a99436..e402718bf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -474,7 +474,7 @@ export class CurrentUserUtils { static setupMobileButtons(doc?: Doc, buttons?: string[]) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ - { title: "WORKSPACES", icon: "bars", click: 'openMobileWorkspaces()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " }, + { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " }, { title: "UPLOAD", icon: "upload", click: 'uploadImageMobile()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploads()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." }, { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." }, diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 3eecc98a4..b5591ca02 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -505,6 +505,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } recordDictation = () => { + console.log("recording dictation"); + console.log(this._editorView); DictationManager.Controls.listen({ interimHandler: this.setCurrentBulletContent, continuous: { indefinite: false }, @@ -523,6 +525,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } recordBullet = async () => { + console.log("recording bullet"); const completedCue = "end session"; const results = await DictationManager.Controls.listen({ interimHandler: this.setCurrentBulletContent, @@ -539,6 +542,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setCurrentBulletContent = (value: string) => { if (this._editorView) { + console.log("this._editorView"); const state = this._editorView.state; const now = Date.now(); let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(now / 1000) }); diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx index f859b0ba3..83da41d90 100644 --- a/src/mobile/AudioUpload.tsx +++ b/src/mobile/AudioUpload.tsx @@ -19,6 +19,9 @@ import { nullAudio } from '../fields/URLField'; import { Transform } from '../client/util/Transform'; import { DocumentView } from '../client/views/nodes/DocumentView'; import { MobileInterface } from './MobileInterface'; +import { DictationOverlay } from '../client/views/DictationOverlay'; +import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu'; +import { ContextMenu } from '../client/views/ContextMenu'; export interface ImageUploadProps { Document: Doc; @@ -57,6 +60,9 @@ export class AudioUpload extends React.Component { private get uploadInterface() { return ( <> + + +
this.closeUpload()}>
@@ -66,10 +72,10 @@ export class AudioUpload extends React.Component { Document={this._audioCol} DataDoc={undefined} LibraryPath={emptyPath} - addDocument={returnFalse} + addDocument={undefined} addDocTab={returnFalse} pinToPres={emptyFunction} - rootSelected={returnFalse} + rootSelected={returnTrue} removeDocument={undefined} docFilters={returnEmptyFilter} onClick={undefined} diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index ed5076402..90672e19f 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -45,6 +45,8 @@ import { Cast, FieldValue } from '../fields/Types'; import { CollectionView } from '../client/views/collections/CollectionView'; import { InkingStroke } from '../client/views/InkingStroke'; import RichTextMenu from "../client/views/nodes/formattedText/RichTextMenu"; +import { AudioBox } from "../client/views/nodes/AudioBox"; +import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox"; library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, @@ -80,13 +82,22 @@ export class MobileInterface extends React.Component { @action componentDidMount = () => { - Doc.UserDoc().activeMobile = this._homeDoc; + // Doc.UserDoc().activeMobile = this._homeDoc; this._homeDoc._viewType === "stacking" ? this.menuListView = true : this.menuListView = false; Doc.SetSelectedTool(InkTool.None); - this.switchCurrentView((userDoc: Doc) => this._homeDoc); + Doc.UserDoc().activeMobile = this._homeDoc; + AudioBox.Enabled = true; document.removeEventListener("dblclick", this.onReactDoubleClick); document.addEventListener("dblclick", this.onReactDoubleClick); + document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on + const id = FormattedTextBox.GetDocFromUrl(e.detail); + DocServer.GetRefField(id).then(doc => { + if (doc instanceof Doc) { + DocumentManager.Instance.jumpToDocument(doc, false, undefined); + } + }); + }); } @action @@ -101,10 +112,14 @@ export class MobileInterface extends React.Component { // Switch the mobile view to the given doc @action - switchCurrentView = (doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { + switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { if (!this.userDoc) return; - - Doc.UserDoc().activeMobile = doc(this.userDoc); + if (this._activeDoc === this._homeDoc) { + this._parents.push(this._activeDoc); + this._homeMenu = false; + } + this._activeDoc = doc; + Doc.UserDoc().activeMobile = doc; onSwitch && onSwitch(); this.renderView = renderView; @@ -118,21 +133,10 @@ export class MobileInterface extends React.Component { * Method called when 'Library' button is pressed on the home screen */ switchToLibrary = async () => { - this._parents.push(this._activeDoc); - this.switchCurrentView((userDoc: Doc) => this._library); - this._activeDoc = this._library; + this.switchCurrentView(this._library); this._homeMenu = false; this.toggleSidebar(); //setTimeout(this.toggleSidebar, 300); - - } - - openWorkspaces = () => { - this._parents.push(this._activeDoc); - this.switchCurrentView((userDoc: Doc) => this._library); - this._activeDoc = this._library; - this._homeMenu = false; - this.sidebarActive = true; } /** @@ -144,24 +148,20 @@ export class MobileInterface extends React.Component { if (doc === Cast(this._library, Doc) as Doc) { this._child = null; - this.userDoc.activeMobile = this._library; + this.switchCurrentView(this._library); } else if (doc === Cast(this._homeDoc, Doc) as Doc) { this._homeMenu = true; this._parents = []; - this._activeDoc = this._homeDoc; this._child = null; - this.switchCurrentView((userDoc: Doc) => this._homeDoc); + this.switchCurrentView(this._homeDoc); } else { if (doc) { this._child = doc; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._homeMenu = false; header.textContent = String(doc.title); } } - if (doc) { - this._activeDoc = doc; - } this._ink = false; } @@ -172,9 +172,8 @@ export class MobileInterface extends React.Component { if (!this._homeMenu || this.sidebarActive) { this._homeMenu = true; this._parents = []; - this._activeDoc = this._homeDoc; this._child = null; - this.switchCurrentView((userDoc: Doc) => this._homeDoc); + this.switchCurrentView(this._homeDoc); } if (this.sidebarActive) { this.toggleSidebar(); @@ -186,8 +185,7 @@ export class MobileInterface extends React.Component { */ returnMain = () => { this._parents = [this._homeDoc]; - this._activeDoc = this._library; - this.switchCurrentView((userDoc: Doc) => this._library); + this.switchCurrentView(this._library); this._homeMenu = false; this._child = null; } @@ -244,21 +242,17 @@ export class MobileInterface extends React.Component { * Navigates to the given doc and updates the sidebar. * @param doc: doc for which the method is called */ - @undoBatch handleClick = async (doc: Doc) => { const children = DocListCast(doc.data); if (doc.type !== "collection" && this.sidebarActive) { this._parents.push(this._activeDoc); - this._activeDoc = doc; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._homeMenu = false; this.toggleSidebar(); } - else if (doc.type === "collection" && children.length === 0) this.openFromSidebar(doc); else { this._parents.push(this._activeDoc); - this._activeDoc = doc; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._homeMenu = false; this._child = doc; } @@ -266,8 +260,7 @@ export class MobileInterface extends React.Component { openFromSidebar = (doc: Doc) => { this._parents.push(this._activeDoc); - this._activeDoc = doc; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._homeMenu = false; this._child = doc; //? this.toggleSidebar(); @@ -354,16 +347,14 @@ export class MobileInterface extends React.Component { // Handles when user clicks on a document in the pathbar handlePathClick = (doc: Doc, index: number) => { if (doc === this._library) { - this._activeDoc = doc; this._child = null; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._parents.length = index; } else if (doc === this._homeDoc) { this.returnHome(); } else { - this._activeDoc = doc; this._child = doc; - this.switchCurrentView((userDoc: Doc) => doc); + this.switchCurrentView(doc); this._parents.length = index; } } @@ -610,31 +601,6 @@ export class MobileInterface extends React.Component { } } - // Mobile audio doc - recordAudio = async () => { - // upload to server with known URL - if (this._activeDoc.title !== "mobile audio") { - this._parents.push(this._activeDoc); - } - const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "mobile audio" }), Doc) as Doc; - if (audioDoc) { - this._activeDoc = audioDoc; - this.switchCurrentView((userDoc: Doc) => audioDoc); - this._homeMenu = false; - } - } - - // // Pushing the audio doc onto Dash Web through the right side bar - // uploadAudio = () => { - // const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; - // const audioDoc = this._activeDoc; - // const data = Cast(audioRightSidebar.data, listSpec(Doc)); - - // if (data) { - // data.push(audioDoc); - // } - // } - // Button for pinning images to presentation pinToPresentation = () => { // Only making button available if it is an image @@ -719,15 +685,11 @@ export class MobileInterface extends React.Component { // For setting up the presentation document for the home menu setupDefaultPresentation = () => { - if (this._activeDoc.title !== "Presentation") { - this._parents.push(this._activeDoc); - } const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; if (presentation) { - this._activeDoc = presentation; - this.switchCurrentView((userDoc: Doc) => presentation); + this.switchCurrentView(presentation); this._homeMenu = false; } } @@ -801,13 +763,8 @@ export class MobileInterface extends React.Component { } switchToMobileUploads = () => { - if (this._activeDoc.title !== "Presentation") { - this._parents.push(this._activeDoc); - } const mobileUpload = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; - console.log(mobileUpload.title); - this._activeDoc = mobileUpload; - this.switchCurrentView((userDoc: Doc) => mobileUpload); + this.switchCurrentView(mobileUpload); this._homeMenu = false; } @@ -831,9 +788,7 @@ export class MobileInterface extends React.Component { {this.undo()} {this.drawInk()} {this.redo()} - {/* {this.upload()} */} {this.uploadImageButton()} - {/* {this.uploadAudioButton()} */}
{this.displayWorkspaces()} {this.renderDefaultContent()} @@ -846,14 +801,13 @@ export class MobileInterface extends React.Component { -Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }, +Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }, "changes the active document displayed on the mobile, (doc: any)"); Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); }, "opens the presentation on mobile"); Scripting.addGlobal(function toggleMobileSidebar() { return MobileInterface.Instance.toggleSidebar(); }); Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); }); Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); }); -Scripting.addGlobal(function openMobileWorkspaces() { return MobileInterface.Instance.openWorkspaces(); }); Scripting.addGlobal(function uploadImageMobile() { return MobileInterface.Instance.toggleUpload(); }); Scripting.addGlobal(function switchToMobileUploads() { return MobileInterface.Instance.switchToMobileUploads(); }); -Scripting.addGlobal(function switchToLibrary() { return MobileInterface.Instance.switchToLibrary(); }); \ No newline at end of file +Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 6a75dac53b9fab5b96f2cce4937b59f1e29928f9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 30 Jun 2020 01:08:45 -0400 Subject: added a docFilter for a substring match. fixed formattedTextBox to not keep adding user_mark storedMarks on every load --- package-lock.json | 44 ++++++---------------- src/client/util/SearchUtil.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 13 +++++-- .../views/collections/CollectionTreeView.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 26 +++++++++---- .../views/nodes/formattedText/FormattedTextBox.tsx | 9 +++-- src/client/views/nodes/formattedText/marks_rts.ts | 1 + src/fields/Doc.ts | 33 +++++++++------- 8 files changed, 69 insertions(+), 63 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/package-lock.json b/package-lock.json index 53b2fc009..957641213 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2955,8 +2955,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -2974,13 +2973,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2993,18 +2990,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -3107,8 +3101,7 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -3118,7 +3111,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3131,20 +3123,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.9.0", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3161,7 +3150,6 @@ "mkdirp": { "version": "0.5.3", "bundled": true, - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3217,8 +3205,7 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "npm-packlist": { "version": "1.4.8", @@ -3243,8 +3230,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -3254,7 +3240,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3323,8 +3308,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -3354,7 +3338,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3372,7 +3355,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3411,13 +3393,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.1.1", - "bundled": true, - "optional": true + "bundled": true } } } diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 5679c0a14..1ac68480e 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -74,7 +74,7 @@ export namespace SearchUtil { const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc); for (let i = 0; i < ids.length; i++) { const testDoc = docs[i]; - if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { theDocs.push(testDoc); theLines.push([]); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 29144abaf..b76859d90 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -7,7 +7,7 @@ import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { WebField } from "../../../fields/URLField"; -import { Cast, ScriptCast, NumCast } from "../../../fields/Types"; +import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; import { Upload } from "../../../server/SharedMediaTypes"; import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils"; @@ -136,8 +136,12 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const filteredDocs = docFilters.length && !this.props.dontRegisterView ? childDocs.filter(d => { for (const facetKey of Object.keys(filterFacets)) { const facet = filterFacets[facetKey]; - const satisfiesFacet = Object.keys(facet).some(value => - (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value)); + const satisfiesFacet = Object.keys(facet).some(value => { + if (facet[value] === "match") { + return d[facetKey] === undefined || Cast(d[facetKey], RichTextField)?.Text.includes(value); + } + return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value); + }); if (!satisfiesFacet) { return false; } @@ -471,4 +475,5 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DocumentType } from "../../documents/DocumentTypes"; import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; import { CollectionView } from "./CollectionView"; -import { SelectionManager } from "../../util/SelectionManager"; +import { SelectionManager } from "../../util/SelectionManager"; import { RichTextField } from "../../../fields/RichTextField"; + diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f6f6fb7cc..c1569a172 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -386,8 +386,8 @@ class TreeView extends React.Component { e.stopPropagation(); } - @computed - get renderBullet() { + @computed get renderBullet() { + TraceMobx(); const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; return
viewSpecScript.script.run({ doc: d }, console.log).result) : docs; } @computed get _allFacets() { + TraceMobx(); const facets = new Set(); - this.childDocs.filter(child => child).forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); + this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key))); return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort(); } @@ -387,8 +389,12 @@ export class CollectionView extends Touchable - set.add(Field.toString(child[facetHeader] as Field)), new Set())); + var rtfields = 0; + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => { + const field = child[facetHeader] as Field; + if (field instanceof RichTextField) rtfields++; + return set.add(Field.toString(field)); + }, new Set())); let nonNumbers = 0; let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; @@ -402,13 +408,17 @@ export class CollectionView extends Touchable; - if (nonNumbers / allCollectionDocs.length < .1) { - newFacet = Docs.Create.SliderDocument({ title: facetHeader }); + if (rtfields / allCollectionDocs.length > 0.1) { + newFacet = Docs.Create.TextDocument("", { _width: 200, _height: 30, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + newFacet.target = this.props.Document; + const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`; + newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); + } else if (nonNumbers / allCollectionDocs.length < .1) { + newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true }); const newFacetField = Doc.LayoutFieldKey(newFacet); const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox - newFacet.treeViewExpandedView = "layout"; - newFacet.treeViewOpen = true; const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05); const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05); newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0]; @@ -418,7 +428,6 @@ export class CollectionView extends Touchable e.stopPropagation()}> diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 90f379525..222a359d1 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -22,7 +22,7 @@ import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { createSchema, makeInterface } from "../../../../fields/Schema"; -import { Cast, DateCast, NumCast, StrCast } from "../../../../fields/Types"; +import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types"; import { TraceMobx, OVERRIDE_ACL } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; @@ -203,13 +203,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this.dataDoc[AclSym]) { if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; - this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))); if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) if (json !== curLayout?.Data) { !curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize)); !curText && tx.storedMarks?.map(m => m.type.name === "pFontFamily" && (Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily)); this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited + ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); } } else { // if we've deleted all the text in a note driven by a template, then restore the template data this.dataDoc[this.props.fieldKey] = undefined; @@ -970,7 +971,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } (selectOnLoad /* || !rtfField?.Text*/) && this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. - this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; + if (!this._editorView!.state.storedMarks || !this._editorView!.state.storedMarks.some(mark => mark.type === schema.marks.user_mark)) { + this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; + } } getFont(font: string) { switch (font) { diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index b09ac0678..54b61aa20 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -270,6 +270,7 @@ export const marks: { [index: string]: MarkSpec } = { userid: { default: "" }, modified: { default: "when?" }, // 1 second intervals since 1970 }, + excludes: "user_mark", group: "inline", toDOM(node: any) { const uid = node.attrs.userid.replace(".", "").replace("@", ""); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bef8acb06..e4d11dd4d 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -945,20 +945,27 @@ export namespace Doc { // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { + export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) { const docFilters = Cast(container._docFilters, listSpec("string"), []); - for (let i = 0; i < docFilters.length; i += 3) { - if (docFilters[i] === key && docFilters[i + 1] === value) { - docFilters.splice(i, 3); - break; + runInAction(() => { + for (let i = 0; i < docFilters.length; i += 3) { + if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) { + if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return; + docFilters.splice(i, 3); + break; + } } - } - if (typeof modifiers === "string") { - docFilters.push(key); - docFilters.push(value); - docFilters.push(modifiers); - container._docFilters = new List(docFilters); - } + if (typeof modifiers === "string") { + if (!docFilters.length && modifiers === "match" && value === undefined) { + container._docFilters = undefined; + } else { + docFilters.push(key); + docFilters.push(value); + docFilters.push(modifiers); + container._docFilters = new List(docFilters); + } + } + }) } export function readDocRangeFilter(doc: Doc, key: string) { const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []); @@ -1168,5 +1175,5 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From eb8399d057089a7b90d756fc69df096becad9a7f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 30 Jun 2020 02:21:31 -0400 Subject: added heuristic to filters to identify text fields based on being RTF or having multiple words. fixed text box padding when used as filter input. fixed selection of items in sidebar when RichTextMenu is pinned. --- src/client/views/MainView.tsx | 5 +++-- src/client/views/collections/CollectionSubView.tsx | 6 +++--- src/client/views/collections/CollectionTreeView.tsx | 6 +++--- src/client/views/collections/CollectionView.tsx | 15 +++++++++------ src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 5 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cea664543..200486279 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -383,7 +383,8 @@ export class MainView extends React.Component { doc.dockingConfig ? this.openWorkspace(doc) : CollectionDockingView.AddRightSplit(doc, libraryPath); } - mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1); + sidebarScreenToLocal = () => new Transform(0, RichTextMenu.Instance.Pinned ? -35 : 0, 1); + mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight); @computed get flyout() { const sidebarContent = this.userDoc?.["tabs-panelContainer"]; @@ -402,7 +403,7 @@ export class MainView extends React.Component { pinToPres={emptyFunction} removeDocument={undefined} onClick={undefined} - ScreenToLocalTransform={Transform.Identity} + ScreenToLocalTransform={this.sidebarScreenToLocal} ContentScaling={returnOne} NativeHeight={returnZero} NativeWidth={returnZero} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index b76859d90..f39c1ae28 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,7 +1,7 @@ import { action, computed, IReactionDisposer, reaction } from "mobx"; import { basename } from 'path'; import CursorField from "../../../fields/CursorField"; -import { Doc, Opt } from "../../../fields/Doc"; +import { Doc, Opt, Field } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; @@ -138,7 +138,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const facet = filterFacets[facetKey]; const satisfiesFacet = Object.keys(facet).some(value => { if (facet[value] === "match") { - return d[facetKey] === undefined || Cast(d[facetKey], RichTextField)?.Text.includes(value); + return d[facetKey] === undefined || Field.toString(d[facetKey] as Field).includes(value); } return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value); }); @@ -475,5 +475,5 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DocumentType } from "../../documents/DocumentTypes"; import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; import { CollectionView } from "./CollectionView"; -import { SelectionManager } from "../../util/SelectionManager"; import { RichTextField } from "../../../fields/RichTextField"; +import { SelectionManager } from "../../util/SelectionManager"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c1569a172..620b977fa 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -304,7 +304,7 @@ class TreeView extends React.Component { } rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); - rtfHeight = () => this.rtfWidth() < this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; @computed get renderContent() { TraceMobx(); @@ -332,8 +332,8 @@ class TreeView extends React.Component {
; } else { const layoutDoc = this.layoutDoc; - const panelHeight = layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight; - const panelWidth = layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth; + const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight; + const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; return
(); + const facets = new Set(["type", "text", "data", "author", "ACL"]); this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key))); return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort(); @@ -392,8 +393,9 @@ export class CollectionView extends Touchable { const field = child[facetHeader] as Field; - if (field instanceof RichTextField) rtfields++; - return set.add(Field.toString(field)); + const fieldStr = Field.toString(field); + if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++; + return set.add(fieldStr); }, new Set())); let nonNumbers = 0; @@ -408,13 +410,14 @@ export class CollectionView extends Touchable; - if (rtfields / allCollectionDocs.length > 0.1) { - newFacet = Docs.Create.TextDocument("", { _width: 200, _height: 30, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); + if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { + newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox newFacet.target = this.props.Document; + newFacet._textBoxPadding = 4; const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`; newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); - } else if (nonNumbers / allCollectionDocs.length < .1) { + } else if (nonNumbers / facetValues.length < .1) { newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true }); const newFacetField = Doc.LayoutFieldKey(newFacet); const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 222a359d1..2cb55e0fa 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1308,7 +1308,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onScroll={this.onscrolled} onDrop={this.ondrop} >
-- cgit v1.2.3-70-g09d2 From a4543fdc90f8c19e4a032294c2f7d16a3152662f Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Tue, 30 Jun 2020 13:33:04 -0500 Subject: working with linked text --- package-lock.json | 10 ++ package.json | 1 + src/client/views/MainView.tsx | 56 +++++----- src/client/views/linking/LinkMenu.tsx | 3 +- .../formattedText/FormattedTextBoxComment.tsx | 120 ++++++++++++++++----- 5 files changed, 133 insertions(+), 57 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/package-lock.json b/package-lock.json index 53b2fc009..9118cd4b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -853,6 +853,16 @@ "@types/prosemirror-view": "*" } }, + "@types/prosemirror-dev-tools": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/prosemirror-dev-tools/-/prosemirror-dev-tools-2.1.0.tgz", + "integrity": "sha512-OhnSaC4yrrEMLPRUkEWcHAIPVqgKlLkE4kISqL3cHeAYxASouSPvPMLqhBIbWkGwaozy43DjjVC1OXkxTo+y5Q==", + "dev": true, + "requires": { + "@types/prosemirror-state": "*", + "@types/prosemirror-view": "*" + } + }, "@types/prosemirror-history": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.1.tgz", diff --git a/package.json b/package.json index 8f67704cb..765002d37 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@types/passport-local": "^1.0.33", "@types/pdfjs-dist": "^2.1.4", "@types/prosemirror-commands": "^1.0.1", + "@types/prosemirror-dev-tools": "^2.1.0", "@types/prosemirror-history": "^1.0.1", "@types/prosemirror-inputrules": "^1.0.2", "@types/prosemirror-keymap": "^1.0.2", diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cea664543..daf26cfd7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -65,7 +65,7 @@ export class MainView extends React.Component { public static Instance: MainView; private _buttonBarHeight = 36; private _flyoutSizeOnDown = 0; - private _urlState: HistoryUtil.DocUrl; + private static _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); private _mainViewRef = React.createRef(); @@ -73,12 +73,12 @@ export class MainView extends React.Component { @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = true; @observable public flyoutWidth: number = 250; - private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } + private get darkScheme() { return BoolCast(Cast(MainView.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } - @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } + @computed private static get userDoc() { return Doc.UserDoc(); } + @computed private get mainContainer() { return MainView.userDoc ? FieldValue(Cast(MainView.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; } + @computed public get sidebarButtonsDoc() { return Cast(MainView.userDoc["tabs-buttons"], Doc) as Doc; } public isPointerDown = false; @@ -119,7 +119,7 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); MainView.Instance = this; - this._urlState = HistoryUtil.parseUrl(window.location) || {} as any; + MainView._urlState = HistoryUtil.parseUrl(window.location) || {} as any; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.pathname !== "/home") { @@ -128,7 +128,7 @@ export class MainView extends React.Component { const type = pathname[0]; if (type === "doc") { CurrentUserUtils.MainDocId = pathname[1]; - if (!this.userDoc) { + if (!MainView.userDoc) { runInAction(() => this.flyoutWidth = 0); DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt) => field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); @@ -172,14 +172,14 @@ export class MainView extends React.Component { initAuthenticationRouters = async () => { // Load the user's active workspace, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; - if (received && !this.userDoc) { + if (received && !MainView.userDoc) { reaction( () => CurrentUserUtils.GuestTarget, target => target && this.createNewWorkspace(), { fireImmediately: true } ); } else { - if (received && this._urlState.sharing) { + if (received && MainView._urlState.sharing) { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, initialized => initialized && received && DocServer.GetRefField(received).then(docField => { if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { @@ -188,9 +188,9 @@ export class MainView extends React.Component { }), ); } - const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc); + const doc = MainView.userDoc && await Cast(MainView.userDoc.activeWorkspace, Doc); if (doc) { - this.openWorkspace(doc); + MainView.openWorkspace(doc); } else { this.createNewWorkspace(); } @@ -199,7 +199,7 @@ export class MainView extends React.Component { @action createNewWorkspace = async (id?: string) => { - const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; + const workspaces = Cast(MainView.userDoc.myWorkspaces, Doc) as Doc; const workspaceCount = DocListCast(workspaces.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, @@ -219,19 +219,19 @@ export class MainView extends React.Component { Doc.AddDocToList(workspaces, "data", workspaceDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => this.openWorkspace(workspaceDoc), 0); + setTimeout(() => MainView.openWorkspace(workspaceDoc), 0); } @action - openWorkspace = (doc: Doc, fromHistory = false) => { + static openWorkspace = (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); - this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); + MainView.userDoc ? (MainView.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); } - const state = this._urlState; - if (state.sharing === true && !this.userDoc) { + const state = MainView._urlState; + if (state.sharing === true && !MainView.userDoc) { DocServer.Control.makeReadOnly(); } else { fromHistory || HistoryUtil.pushState({ @@ -257,7 +257,7 @@ export class MainView extends React.Component { } // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) setTimeout(async () => { - const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc); + const col = MainView.userDoc && await Cast(MainView.userDoc.rightSidebarCollection, Doc); col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); }, 100); return true; @@ -305,7 +305,7 @@ export class MainView extends React.Component { DataDoc={undefined} LibraryPath={emptyPath} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} rootSelected={returnTrue} onClick={undefined} @@ -378,15 +378,15 @@ export class MainView extends React.Component { document.removeEventListener("pointerup", this.onPointerUp); } flyoutWidthFunc = () => this.flyoutWidth; - addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { + static addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : - doc.dockingConfig ? this.openWorkspace(doc) : + doc.dockingConfig ? MainView.openWorkspace(doc) : CollectionDockingView.AddRightSplit(doc, libraryPath); } mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1); @computed get flyout() { - const sidebarContent = this.userDoc?.["tabs-panelContainer"]; + const sidebarContent = MainView.userDoc?.["tabs-panelContainer"]; if (!(sidebarContent instanceof Doc)) { return (null); } @@ -398,7 +398,7 @@ export class MainView extends React.Component { LibraryPath={emptyPath} addDocument={undefined} rootSelected={returnTrue} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} removeDocument={undefined} onClick={undefined} @@ -424,7 +424,7 @@ export class MainView extends React.Component { DataDoc={undefined} LibraryPath={emptyPath} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} NativeHeight={returnZero} NativeWidth={returnZero} @@ -458,8 +458,8 @@ export class MainView extends React.Component { } @computed get mainContent() { - const sidebar = this.userDoc?.["tabs-panelContainer"]; - return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( + const sidebar = MainView.userDoc?.["tabs-panelContainer"]; + return !MainView.userDoc || !(sidebar instanceof Doc) ? (null) : (
MainView.Instance.openWorkspace(copiedWorkspace), 0); + setTimeout(() => MainView.openWorkspace(copiedWorkspace), 0); }); diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 6b24f96b1..9fd2b98b1 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -74,7 +74,8 @@ export class LinkMenu extends React.Component { render() { const sourceDoc = this.props.docView.props.Document; const groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); - return
this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}> + return
this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}> {!this._editingLink ? this.renderAllGroups(groups) : this._editingLink = undefined)} /> diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 90f2c0aa6..a4153a40c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -16,6 +16,13 @@ import React = require("react"); import { Docs } from "../../../documents/Documents"; import wiki from "wikijs"; import { DocumentType } from "../../../documents/DocumentTypes"; +import { computed, action, observable } from "mobx"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { LinkManager } from "../../../util/LinkManager"; +import { LinkDocPreview } from "../LinkDocPreview"; +import { DocumentLinksButton } from "../DocumentLinksButton"; +import { ContextMenu } from "../../ContextMenu"; +import { MainView } from "../../MainView"; export let formattedTextBoxCommentPlugin = new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } @@ -62,6 +69,10 @@ export class FormattedTextBoxComment { static mark: Mark; static textBox: FormattedTextBox | undefined; static linkDoc: Doc | undefined; + + static targetDoc: Doc | undefined; + static _editRef = React.createRef(); + constructor(view: any) { if (!FormattedTextBoxComment.tooltip) { const root = document.getElementById("root"); @@ -75,8 +86,8 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip"; FormattedTextBoxComment.tooltip.style.pointerEvents = "all"; - FormattedTextBoxComment.tooltip.style.maxWidth = "350px"; - FormattedTextBoxComment.tooltip.style.maxHeight = "250px"; + FormattedTextBoxComment.tooltip.style.maxWidth = "225px"; + FormattedTextBoxComment.tooltip.style.maxHeight = "225px"; FormattedTextBoxComment.tooltip.style.width = "100%"; FormattedTextBoxComment.tooltip.style.height = "100%"; FormattedTextBoxComment.tooltip.style.overflow = "hidden"; @@ -106,6 +117,31 @@ export class FormattedTextBoxComment { } } + @action + public static deleteLink = (): void => { + FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; + //this.props.showLinks(); + LinkDocPreview.LinkInfo = undefined; + DocumentLinksButton.EditLink = undefined; + } + + @action + public static onContextMenu = (e: React.MouseEvent) => { + DocumentLinksButton.EditLink = undefined; + LinkDocPreview.LinkInfo = undefined; + e.preventDefault(); + ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => FormattedTextBoxComment.followDefault(), icon: "arrow-right" }); + ContextMenu.Instance.displayMenu(e.clientX, e.clientY); + } + + @action.bound + public static async followDefault() { + DocumentLinksButton.EditLink = undefined; + LinkDocPreview.LinkInfo = undefined; + FormattedTextBoxComment.targetDoc ? DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.targetDoc, + doc => MainView.addDocTabFunc(doc, "onRight"), false) : null; + } + public static Hide() { FormattedTextBoxComment.textBox = undefined; FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); @@ -119,6 +155,22 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = ""); } + @action + onClick = (e: PointerEvent) => { + if (FormattedTextBoxComment._editRef && !FormattedTextBoxComment._editRef.current?.contains(e.target as Node)) { + FormattedTextBoxComment.linkDoc = undefined; + FormattedTextBoxComment.targetDoc = undefined; + } + } + @action + componentDidMount() { + document.addEventListener("pointerdown", this.onClick); + } + + componentWillUnmount() { + document.removeEventListener("pointerdown", this.onClick); + } + static showCommentbox(set: string, view: EditorView, nbef: number) { const state = view.state; if (set !== "none") { @@ -157,6 +209,7 @@ export class FormattedTextBoxComment { (FormattedTextBoxComment.tooltipText as any).href = ""; FormattedTextBoxComment.tooltipText.style.whiteSpace = ""; FormattedTextBoxComment.tooltipText.style.overflow = ""; + // this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date if (state.selection.$from) { nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); @@ -209,33 +262,44 @@ export class FormattedTextBoxComment { target._scrollY = NumCast(anchor?.y); } if (target?.author) { + + const docPreview =
{target.title} +
+
+
+
+ +
+
+ Math.min(350, NumCast(target._width, 350))} + PanelHeight={() => Math.min(250, NumCast(target._height, 250))} + focus={emptyFunction} + whenActiveChanged={returnFalse} + bringToFront={returnFalse} + ContentScaling={returnOne} + NativeWidth={returnZero} + NativeHeight={returnZero} + /> +
; FormattedTextBoxComment.showCommentbox("", view, nbef); - ReactDOM.render( Math.min(350, NumCast(target._width, 350))} - PanelHeight={() => Math.min(250, NumCast(target._height, 250))} - focus={emptyFunction} - whenActiveChanged={returnFalse} - bringToFront={returnFalse} - ContentScaling={returnOne} - NativeWidth={returnZero} - NativeHeight={returnZero} - />, FormattedTextBoxComment.tooltipText); + ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%"; FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%"; } -- cgit v1.2.3-70-g09d2 From 3cc69c7f5ae7f7a747ab72b522ea81af38dd7f20 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Tue, 30 Jun 2020 16:45:23 -0500 Subject: slight mods to inline links --- src/client/views/nodes/LinkDocPreview.tsx | 79 +++++++++++----------- .../formattedText/FormattedTextBoxComment.tsx | 6 +- 2 files changed, 45 insertions(+), 40 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 1d2c022a7..d02896436 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -98,45 +98,46 @@ export class LinkDocPreview extends React.Component { {this._toolTipText}
: -
-
{this._targetDoc.title} -
-
-
-
- -
-
-
-
; + //
+ //
{this._targetDoc.title} + //
+ //
+ //
+ //
+ // + //
+ //
+ //
+ ; + //
; } render() { diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index a4153a40c..8eeeae051 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -73,6 +73,8 @@ export class FormattedTextBoxComment { static targetDoc: Doc | undefined; static _editRef = React.createRef(); + static openDoc: any; + constructor(view: any) { if (!FormattedTextBoxComment.tooltip) { const root = document.getElementById("root"); @@ -99,6 +101,7 @@ export class FormattedTextBoxComment { if (FormattedTextBoxComment.linkDoc && !keep && textBox) { if (FormattedTextBoxComment.linkDoc.author) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { + FormattedTextBoxComment.openDoc = textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, @@ -119,6 +122,7 @@ export class FormattedTextBoxComment { @action public static deleteLink = (): void => { + console.log(FormattedTextBoxComment.linkDoc); FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; //this.props.showLinks(); LinkDocPreview.LinkInfo = undefined; @@ -139,7 +143,7 @@ export class FormattedTextBoxComment { DocumentLinksButton.EditLink = undefined; LinkDocPreview.LinkInfo = undefined; FormattedTextBoxComment.targetDoc ? DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.targetDoc, - doc => MainView.addDocTabFunc(doc, "onRight"), false) : null; + doc => FormattedTextBoxComment, false) : null; } public static Hide() { -- cgit v1.2.3-70-g09d2 From 6af97864aabe89153487d37bf78391ff525deadd Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 30 Jun 2020 23:57:47 -0400 Subject: added left/right/center justify text buttons. changed unpinned text menu --- src/client/views/MainView.tsx | 4 +- .../formattedText/ProsemirrorExampleTransfer.ts | 24 +++--------- .../views/nodes/formattedText/RichTextMenu.tsx | 45 +++++++++++++++++----- .../views/nodes/formattedText/RichTextRules.ts | 3 -- 4 files changed, 44 insertions(+), 32 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 200486279..f6db1af66 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -5,7 +5,7 @@ import { faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, - faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown + faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight } from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -142,7 +142,7 @@ export class MainView extends React.Component { faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, - faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown); + faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 1bbcb9fa8..9d69f4be7 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -1,7 +1,6 @@ import { chainCommands, exitCode, joinDown, joinUp, lift, deleteSelection, joinBackward, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands"; import { liftTarget } from "prosemirror-transform"; import { redo, undo } from "prosemirror-history"; -import { undoInputRule } from "prosemirror-inputrules"; import { Schema } from "prosemirror-model"; import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; import { splitListItem, wrapInList, } from "prosemirror-schema-list"; @@ -12,7 +11,6 @@ import { Doc, DataSym } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; -import { update } from "lodash"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -215,10 +213,13 @@ export default function buildKeymap>(schema: S, props: any marks && tx3.setStoredMarks([...marks]); dispatch(tx3); })) { + const fromattrs = state.selection.$from.node().attrs; if (!splitBlockKeepMarks(state, (tx3: Transaction) => { - splitMetadata(marks, tx3); - if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction>) => void))) { - dispatch(tx3); + const tonode = tx3.selection.$to.node(); + const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks); + splitMetadata(marks, tx4); + if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction>) => void))) { + dispatch(tx4); } })) { return false; @@ -281,19 +282,6 @@ export default function buildKeymap>(schema: S, props: any return false; }); - // bind("^", (state: EditorState, dispatch: (tx: Transaction) => void) => { - // let newNode = schema.nodes.footnote.create({}); - // if (dispatch && state.selection.from === state.selection.to) { - // let tr = state.tr; - // tr.replaceSelectionWith(newNode); // replace insertion with a footnote. - // dispatch(tr.setSelection(new NodeSelection( // select the footnote node to open its display - // tr.doc.resolve( // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node) - // tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize)))); - // return true; - // } - // return false; - // }); - return keys; } diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 839943aac..a06fcec9a 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -412,6 +412,28 @@ export default class RichTextMenu extends AntimodeMenu { dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); return true; } + alignCenter = (state: EditorState, dispatch: any) => { + return this.alignParagraphs(state, "center", dispatch); + } + alignLeft = (state: EditorState, dispatch: any) => { + return this.alignParagraphs(state, "left", dispatch); + } + alignRight = (state: EditorState, dispatch: any) => { + return this.alignParagraphs(state, "right", dispatch); + } + + alignParagraphs(state: EditorState, align: "left" | "right" | "center", dispatch: any) { + var tr = state.tr; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + if (node.type === schema.nodes.paragraph) { + tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, align }, node.marks); + return false; + } + return true; + }); + dispatch?.(tr); + return true; + } insertBlockquote(state: EditorState, dispatch: any) { const path = (state.selection.$from as any).path; @@ -768,18 +790,21 @@ export default class RichTextMenu extends AntimodeMenu { TraceMobx(); const row1 =
{[ !this.collapsed ? this.getDragger() : (null), - this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), - this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), - this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), - this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), - this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), - this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + !this.Pinned ? (null) : <> {[ + this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + ]}, this.createColorButton(), this.createHighlighterButton(), this.createLinkButton(), this.createBrushButton(), - this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), - this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), + this.createButton("align-left", "Align Left", undefined, this.alignLeft), + this.createButton("align-center", "Align Center", undefined, this.alignCenter), + this.createButton("align-right", "Align Right", undefined, this.alignRight), ]}
; const row2 =
@@ -787,7 +812,9 @@ export default class RichTextMenu extends AntimodeMenu {
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"), this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"), - this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]} + this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes"), + this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), + this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),]}
{/*
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 6a85e3b7c..612a824fc 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -315,8 +315,6 @@ export class RichTextRules { return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; }), - - // create an inline view of a tag stored under the '#' field new InputRule( new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_;\-0-9]*)\s$/), @@ -374,7 +372,6 @@ export class RichTextRules { new InputRule( new RegExp(/%\)/), (state, match, start, end) => { - return state.tr.deleteRange(start, end).removeStoredMark(state.schema.marks.summarizeInclusive.create()); }), -- cgit v1.2.3-70-g09d2 From 9b8b6f79bef78be68a9982ea008e308bba4872b3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 11:37:48 -0400 Subject: fixed text menu to make button commands undoable. fixed hiding of menu to not disappear after pulldown command selection in a cleaner way --- src/client/views/AntimodeMenu.tsx | 9 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 38 +++++++++----- .../views/nodes/formattedText/RichTextMenu.tsx | 61 ++++++++++++++-------- 3 files changed, 68 insertions(+), 40 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index 3e4d20fea..a4634103c 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -51,18 +51,15 @@ export default abstract class AntimodeMenu extends React.Component { if (this._opacity === 0.2) { this._transitionProperty = "opacity"; this._transitionDuration = "0.1s"; - this._transitionDelay = ""; - this._opacity = 0; - this._left = this._top = -300; } if (forceOut) { this._transitionProperty = ""; this._transitionDuration = ""; - this._transitionDelay = ""; - this._opacity = 0; - this._left = this._top = -300; } + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2cb55e0fa..f4aa7919f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -86,6 +86,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; public ProseRef?: HTMLDivElement; + public get EditorView() { return this._editorView; } private _ref: React.RefObject = React.createRef(); private _scrollRef: React.RefObject = React.createRef(); private _editorView: Opt; @@ -93,7 +94,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _searchIndex = 0; private _undoTyping?: UndoManager.Batch; private _disposers: { [name: string]: IReactionDisposer } = {}; - private dropDisposer?: DragManager.DragDropDisposer; + private _dropDisposer?: DragManager.DragDropDisposer; @computed get _recording() { return this.dataDoc.audioState === "recording"; } set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; } @@ -291,8 +292,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } protected createDropTarget = (ele: HTMLDivElement) => { this.ProseRef = ele; - this.dropDisposer?.(); - ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc)); + this._dropDisposer?.(); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc)); } @undoBatch @@ -660,8 +661,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp incomingValue => { if (incomingValue !== undefined && this._editorView && !this._applyingChange) { const updatedState = JSON.parse(incomingValue); - this._editorView.updateState(EditorState.fromJSON(this.config, updatedState)); - this.tryUpdateHeight(); + if (JSON.stringify(this._editorView!.state.toJSON()) !== JSON.stringify(updatedState)) { + this._editorView.updateState(EditorState.fromJSON(this.config, updatedState)); + this.tryUpdateHeight(); + } } } ); @@ -1046,6 +1049,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @action onFocused = (e: React.FocusEvent): void => { + console.log("FOUCSS") FormattedTextBox.FocusedBox = this; this.tryUpdateHeight(); @@ -1163,18 +1167,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); } - public static HadSelection: boolean = false; - onBlur = (e: any) => { - FormattedTextBox.HadSelection = window.getSelection()?.toString() !== ""; - //DictationManager.Controls.stop(false); + public startUndoTypingBatch() { + this._undoTyping = UndoManager.StartBatch("undoTyping"); + } + + public endUndoTypingBatch() { + const wasUndoing = this._undoTyping; if (this._undoTyping) { this._undoTyping.end(); this._undoTyping = undefined; } + return wasUndoing; + } + public static HadSelection: boolean = false; + onBlur = (e: any) => { + console.log("BLURRR") + FormattedTextBox.HadSelection = window.getSelection()?.toString() !== ""; + //DictationManager.Controls.stop(false); + this.endUndoTypingBatch(); this.doLinkOnDeselect(); // move the richtextmenu offscreen - if (!RichTextMenu.Instance.Pinned && !RichTextMenu.Instance.overMenu) RichTextMenu.Instance.jumpTo(-300, -300); + if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide(); } _lastTimedMark: Mark | undefined = undefined; @@ -1208,7 +1222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp // this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark)); if (!this._undoTyping) { - this._undoTyping = UndoManager.StartBatch("undoTyping"); + this.startUndoTypingBatch(); } } @@ -1308,7 +1322,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onScroll={this.onscrolled} onDrop={this.ondrop} >
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index a06fcec9a..7c7a76e1b 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faIndent, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable } from "mobx"; +import { action, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { lift, wrapIn } from "prosemirror-commands"; import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model"; @@ -23,6 +23,7 @@ import { updateBullets } from "./ProsemirrorExampleTransfer"; import "./RichTextMenu.scss"; import { schema } from "./schema_rts"; import { TraceMobx } from "../../../../fields/util"; +import { UndoManager } from "../../../util/UndoManager"; const { toggleMark } = require("prosemirror-commands"); library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller); @@ -68,6 +69,8 @@ export default class RichTextMenu extends AntimodeMenu { @observable private currentLink: string | undefined = ""; @observable private showLinkDropdown: boolean = false; + _reaction: IReactionDisposer | undefined; + _delayHide = false; constructor(props: Readonly<{}>) { super(props); RichTextMenu.Instance = this; @@ -138,6 +141,16 @@ export default class RichTextMenu extends AntimodeMenu { ]; } + componentDidMount() { + this._reaction = reaction(() => SelectionManager.SelectedDocuments(), + () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true)); + } + componentWillUnmount() { + this._reaction?.(); + } + + public delayHide = () => { this._delayHide = true; } + @action changeView(view: EditorView) { this.view = view; @@ -147,16 +160,6 @@ export default class RichTextMenu extends AntimodeMenu { this.updateFromDash(view, lastState, this.editorProps); } - public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { - if (this.view) { - const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). - addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - return this.view.state.selection.$from.nodeAfter?.text || ""; - } - return ""; - } - @action public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { if (!view) { @@ -310,8 +313,11 @@ export default class RichTextMenu extends AntimodeMenu { function onClick(e: React.PointerEvent) { e.preventDefault(); e.stopPropagation(); - self.view && command && command(self.view.state, self.view.dispatch, self.view); - self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => { + self.view && command && command(self.view.state, self.view.dispatch, self.view); + self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view); + }, "rich text menu command"); self.setActiveMarkButtons(self.getActiveMarksOnSelection()); } @@ -338,9 +344,10 @@ export default class RichTextMenu extends AntimodeMenu { function onChange(e: React.ChangeEvent) { e.stopPropagation(); e.preventDefault(); + self.TextView.endUndoTypingBatch(); options.forEach(({ label, mark, command }) => { if (e.target.value === label) { - self.view && mark && command(mark, self.view); + UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown"); } }); } @@ -361,9 +368,10 @@ export default class RichTextMenu extends AntimodeMenu { const self = this; function onChange(val: string) { + self.TextView.endUndoTypingBatch(); options.forEach(({ label, node, command }) => { if (val === label) { - self.view && node && command(node); + UndoManager.RunInBatch(() => self.view && node && command(node), "nodes dropdown"); } }); } @@ -461,7 +469,8 @@ export default class RichTextMenu extends AntimodeMenu { function onBrushClick(e: React.PointerEvent) { e.preventDefault(); e.stopPropagation(); - self.view && self.fillBrush(self.view.state, self.view.dispatch); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush"); } let label = "Stored marks: "; @@ -528,19 +537,24 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; } @action setActiveColor(color: string) { this.activeFontColor = color; } + get TextView() { return (this.view as any).TextView as FormattedTextBox } createColorButton() { const self = this; function onColorClick(e: React.PointerEvent) { e.preventDefault(); e.stopPropagation(); - self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); + self.TextView.EditorView!.focus() } function changeColor(e: React.PointerEvent, color: string) { e.preventDefault(); e.stopPropagation(); self.setActiveColor(color); - self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); + self.TextView.EditorView!.focus() } const button = @@ -585,13 +599,15 @@ export default class RichTextMenu extends AntimodeMenu { function onHighlightClick(e: React.PointerEvent) { e.preventDefault(); e.stopPropagation(); - self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highligher"); } function changeHighlight(e: React.PointerEvent, color: string) { e.preventDefault(); e.stopPropagation(); self.setActiveHighlight(color); - self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highlighter"); } const button = @@ -631,7 +647,8 @@ export default class RichTextMenu extends AntimodeMenu { const self = this; function onLinkChange(e: React.ChangeEvent) { - self.setCurrentLink(e.target.value); + self.TextView.endUndoTypingBatch(); + UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), "link change"); } const link = this.currentLink ? this.currentLink : ""; @@ -766,7 +783,7 @@ export default class RichTextMenu extends AntimodeMenu { return ref_node; } - @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = true; } + @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } @action onPointerLeave(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } @action -- cgit v1.2.3-70-g09d2 From 044c770b22f23709ac6a1b5429631e6633217282 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 1 Jul 2020 11:04:42 -0500 Subject: modifications to text previews --- package-lock.json | 44 +++++--- src/client/views/linking/LinkMenuItem.scss | 1 - .../formattedText/FormattedTextBoxComment.scss | 57 ++++++++-- .../formattedText/FormattedTextBoxComment.tsx | 117 +++++++++++---------- 4 files changed, 141 insertions(+), 78 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/package-lock.json b/package-lock.json index 3136a9cc2..d42e286e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2976,7 +2976,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2994,11 +2995,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3011,15 +3014,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3122,7 +3128,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3132,6 +3139,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3144,17 +3152,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3171,6 +3182,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3226,7 +3238,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -3251,7 +3264,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3261,6 +3275,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3329,7 +3344,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3359,6 +3375,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3376,6 +3393,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3414,11 +3432,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } } diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index fd0954f65..1669fc39d 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -61,7 +61,6 @@ margin: 0; margin-right: 6px; border-radius: 50%; - cursor: pointer; pointer-events: auto; background-color: $dark-color; color: $light-color; diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss index 2dd63ec21..d83583db1 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss @@ -8,10 +8,12 @@ margin-bottom: 7px; -webkit-transform: translateX(-50%); transform: translateX(-50%); - } - .FormattedTextBox-tooltip:before { +} + +.FormattedTextBox-tooltip:before { content: ""; - height: 0; width: 0; + height: 0; + width: 0; position: absolute; left: 50%; margin-left: -5px; @@ -19,10 +21,12 @@ border: 5px solid transparent; border-bottom-width: 0; border-top-color: silver; - } - .FormattedTextBox-tooltip:after { +} + +.FormattedTextBox-tooltip:after { content: ""; - height: 0; width: 0; + height: 0; + width: 0; position: absolute; left: 50%; margin-left: -5px; @@ -30,4 +34,43 @@ border: 5px solid transparent; border-bottom-width: 0; border-top-color: white; - } \ No newline at end of file +} + +.FormattedTextBox-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + pointer-events: auto; + background-color: rgb(53, 146, 199); + color: rgb(150, 211, 248); + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + + &:hover { + background: rgb(53, 146, 199); + ; + } + } +} \ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 8eeeae051..15cd68c26 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -2,7 +2,7 @@ import { Mark, ResolvedPos } from "prosemirror-model"; import { EditorState, Plugin } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import * as ReactDOM from 'react-dom'; -import { Doc, DocCastAsync } from "../../../../fields/Doc"; +import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc"; import { Cast, FieldValue, NumCast } from "../../../../fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; @@ -16,13 +16,11 @@ import React = require("react"); import { Docs } from "../../../documents/Documents"; import wiki from "wikijs"; import { DocumentType } from "../../../documents/DocumentTypes"; -import { computed, action, observable } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { LinkManager } from "../../../util/LinkManager"; -import { LinkDocPreview } from "../LinkDocPreview"; -import { DocumentLinksButton } from "../DocumentLinksButton"; -import { ContextMenu } from "../../ContextMenu"; -import { MainView } from "../../MainView"; +import { action } from "mobx"; +// import { LinkManager } from "../../../util/LinkManager"; +// import { LinkDocPreview } from "../LinkDocPreview"; +// import { DocumentLinksButton } from "../DocumentLinksButton"; export let formattedTextBoxCommentPlugin = new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } @@ -70,10 +68,8 @@ export class FormattedTextBoxComment { static textBox: FormattedTextBox | undefined; static linkDoc: Doc | undefined; - static targetDoc: Doc | undefined; - static _editRef = React.createRef(); - - static openDoc: any; + static _deleteRef: Opt; + static _followRef: Opt; constructor(view: any) { if (!FormattedTextBoxComment.tooltip) { @@ -88,8 +84,8 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip"; FormattedTextBoxComment.tooltip.style.pointerEvents = "all"; - FormattedTextBoxComment.tooltip.style.maxWidth = "225px"; - FormattedTextBoxComment.tooltip.style.maxHeight = "225px"; + FormattedTextBoxComment.tooltip.style.maxWidth = "350px"; + FormattedTextBoxComment.tooltip.style.maxHeight = "250px"; FormattedTextBoxComment.tooltip.style.width = "100%"; FormattedTextBoxComment.tooltip.style.height = "100%"; FormattedTextBoxComment.tooltip.style.overflow = "hidden"; @@ -100,13 +96,25 @@ export class FormattedTextBoxComment { const textBox = FormattedTextBoxComment.textBox; if (FormattedTextBoxComment.linkDoc && !keep && textBox) { if (FormattedTextBoxComment.linkDoc.author) { + + // if (FormattedTextBoxComment._deleteRef && FormattedTextBoxComment._deleteRef.contains(e.target as any)) { + // FormattedTextBoxComment.deleteLink(); + // } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) { + // if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { + // textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + // } else { + // DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, + // (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + // } + // } else { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - FormattedTextBoxComment.openDoc = textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); } + //} + } } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) { textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight"); @@ -117,33 +125,16 @@ export class FormattedTextBoxComment { e.preventDefault(); }; root && root.appendChild(FormattedTextBoxComment.tooltip); - } + }; } @action public static deleteLink = (): void => { console.log(FormattedTextBoxComment.linkDoc); - FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; - //this.props.showLinks(); - LinkDocPreview.LinkInfo = undefined; - DocumentLinksButton.EditLink = undefined; - } - - @action - public static onContextMenu = (e: React.MouseEvent) => { - DocumentLinksButton.EditLink = undefined; - LinkDocPreview.LinkInfo = undefined; - e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => FormattedTextBoxComment.followDefault(), icon: "arrow-right" }); - ContextMenu.Instance.displayMenu(e.clientX, e.clientY); - } - - @action.bound - public static async followDefault() { - DocumentLinksButton.EditLink = undefined; - LinkDocPreview.LinkInfo = undefined; - FormattedTextBoxComment.targetDoc ? DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, FormattedTextBoxComment.targetDoc, - doc => FormattedTextBoxComment, false) : null; + // FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; + // //this.props.showLinks(); + // LinkDocPreview.LinkInfo = undefined; + // DocumentLinksButton.EditLink = undefined; } public static Hide() { @@ -159,22 +150,6 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = ""); } - @action - onClick = (e: PointerEvent) => { - if (FormattedTextBoxComment._editRef && !FormattedTextBoxComment._editRef.current?.contains(e.target as Node)) { - FormattedTextBoxComment.linkDoc = undefined; - FormattedTextBoxComment.targetDoc = undefined; - } - } - @action - componentDidMount() { - document.addEventListener("pointerdown", this.onClick); - } - - componentWillUnmount() { - document.removeEventListener("pointerdown", this.onClick); - } - static showCommentbox(set: string, view: EditorView, nbef: number) { const state = view.state; if (set !== "none") { @@ -213,7 +188,6 @@ export class FormattedTextBoxComment { (FormattedTextBoxComment.tooltipText as any).href = ""; FormattedTextBoxComment.tooltipText.style.whiteSpace = ""; FormattedTextBoxComment.tooltipText.style.overflow = ""; - // this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date if (state.selection.$from) { nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); @@ -266,12 +240,12 @@ export class FormattedTextBoxComment { target._scrollY = NumCast(anchor?.y); } if (target?.author) { - + FormattedTextBoxComment.showCommentbox("", view, nbef); const docPreview =
{target.title}
-
+
this._deleteRef = r}>
-
+
this._followRef = r}>
@@ -303,7 +277,34 @@ export class FormattedTextBoxComment { />
; FormattedTextBoxComment.showCommentbox("", view, nbef); - ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText); + + ReactDOM.render( Math.min(350, NumCast(target._width, 350))} + PanelHeight={() => Math.min(250, NumCast(target._height, 250))} + focus={emptyFunction} + whenActiveChanged={returnFalse} + bringToFront={returnFalse} + ContentScaling={returnOne} + NativeWidth={returnZero} + NativeHeight={returnZero} + />, FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%"; FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%"; } @@ -317,4 +318,4 @@ export class FormattedTextBoxComment { } destroy() { } -} +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3712228f853e80ff05eced253a0bf197bdf47bde Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 12:26:54 -0400 Subject: added several new commands to RichTextMenu --- .../views/nodes/formattedText/RichTextMenu.tsx | 73 +++++++++++++++++++++- 1 file changed, 70 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 7c7a76e1b..6fdcaceda 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faIndent, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons"; +import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; @@ -26,7 +26,7 @@ import { TraceMobx } from "../../../../fields/util"; import { UndoManager } from "../../../util/UndoManager"; const { toggleMark } = require("prosemirror-commands"); -library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller); +library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller); @observer @@ -443,6 +443,63 @@ export default class RichTextMenu extends AntimodeMenu { return true; } + insetParagraph(state: EditorState, dispatch: any) { + var tr = state.tr; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + if (node.type === schema.nodes.paragraph) { + const inset = (node.attrs.inset ? Number(node.attrs.inset) : 0) + 10; + tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, inset }, node.marks); + return false; + } + return true; + }); + dispatch?.(tr); + return true; + } + outsetParagraph(state: EditorState, dispatch: any) { + var tr = state.tr; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + if (node.type === schema.nodes.paragraph) { + const inset = Math.max(0, (node.attrs.inset ? Number(node.attrs.inset) : 0) - 10); + tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, inset }, node.marks); + return false; + } + return true; + }); + dispatch?.(tr); + return true; + } + + indentParagraph(state: EditorState, dispatch: any) { + var tr = state.tr; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + if (node.type === schema.nodes.paragraph) { + const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined; + const indent = !nodeval ? 25 : nodeval < 0 ? 0 : nodeval + 25; + tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, indent }, node.marks); + return false; + } + return true; + }); + dispatch?.(tr); + return true; + } + + hangingIndentParagraph(state: EditorState, dispatch: any) { + var tr = state.tr; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + if (node.type === schema.nodes.paragraph) { + const nodeval = node.attrs.indent ? Number(node.attrs.indent) : undefined; + const indent = !nodeval ? -25 : nodeval > 0 ? 0 : nodeval - 10; + tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, indent }, node.marks); + return false; + } + return true; + }); + dispatch?.(tr); + return true; + } + insertBlockquote(state: EditorState, dispatch: any) { const path = (state.selection.$from as any).path; if (path.length > 6 && path[path.length - 6].type === schema.nodes.blockquote) { @@ -453,6 +510,11 @@ export default class RichTextMenu extends AntimodeMenu { return true; } + insertHorizontalRule(state: EditorState, dispatch: any) { + dispatch(state.tr.replaceSelectionWith(state.schema.nodes.horizontal_rule.create()).scrollIntoView()); + return true; + } + @action toggleBrushDropdown() { this.showBrushDropdown = !this.showBrushDropdown; } // todo: add brushes to brushMap to save with a style name @@ -822,6 +884,10 @@ export default class RichTextMenu extends AntimodeMenu { this.createButton("align-left", "Align Left", undefined, this.alignLeft), this.createButton("align-center", "Align Center", undefined, this.alignCenter), this.createButton("align-right", "Align Right", undefined, this.alignRight), + this.createButton("indent", "Inset More", undefined, this.insetParagraph), + this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph), + this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph), + this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph), ]}
; const row2 =
@@ -831,7 +897,8 @@ export default class RichTextMenu extends AntimodeMenu { this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"), this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes"), this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), - this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),]} + this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), + this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),]}
{/*
-- cgit v1.2.3-70-g09d2 From 4b0874c00681335a3746ad6d37c9ce08bf9978ae Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 12:41:23 -0400 Subject: changed collpased bullets to show their current line and elide sub-bullets --- src/client/views/nodes/formattedText/nodes_rts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index afb1f57b7..f83cff9b9 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -312,7 +312,7 @@ export const nodes: { [index: string]: NodeSpec } = { const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : ""; return node.attrs.visibility ? ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, 0] : - ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, "..."]; + ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, `${node.firstChild?.textContent}...`]; } }, }; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f38485f2778c9dbcc4dd663527c58ea450acb832 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 13:53:40 -0400 Subject: fixed drag drop within text box to call updateBullets --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f4aa7919f..2a79e2f13 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -32,7 +32,7 @@ import { DocumentType } from '../../../documents/DocumentTypes'; import { DictationManager } from '../../../util/DictationManager'; import { DragManager } from "../../../util/DragManager"; import { makeTemplate } from '../../../util/DropConverter'; -import buildKeymap from "./ProsemirrorExampleTransfer"; +import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer"; import RichTextMenu from './RichTextMenu'; import { RichTextRules } from "./RichTextRules"; @@ -1227,6 +1227,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } ondrop = (eve: React.DragEvent) => { + this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema)); eve.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash. } onscrolled = (ev: React.UIEvent) => { -- cgit v1.2.3-70-g09d2 From 581ba904dc9bd02f6e2d81f42c45f38f6ad26cab Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 1 Jul 2020 13:14:02 -0500 Subject: textbox comment changes --- src/client/views/MainView.tsx | 40 +++++---- .../formattedText/FormattedTextBoxComment.scss | 4 +- .../formattedText/FormattedTextBoxComment.tsx | 97 +++++++++------------- 3 files changed, 62 insertions(+), 79 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9328198d0..200486279 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -65,7 +65,7 @@ export class MainView extends React.Component { public static Instance: MainView; private _buttonBarHeight = 36; private _flyoutSizeOnDown = 0; - private static _urlState: HistoryUtil.DocUrl; + private _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); private _mainViewRef = React.createRef(); @@ -73,12 +73,12 @@ export class MainView extends React.Component { @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = true; @observable public flyoutWidth: number = 250; - private get darkScheme() { return BoolCast(Cast(MainView.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } + private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } - @computed private static get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return MainView.userDoc ? FieldValue(Cast(MainView.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } + @computed private get userDoc() { return Doc.UserDoc(); } + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - @computed public get sidebarButtonsDoc() { return Cast(MainView.userDoc["tabs-buttons"], Doc) as Doc; } + @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; } public isPointerDown = false; @@ -119,7 +119,7 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); MainView.Instance = this; - MainView._urlState = HistoryUtil.parseUrl(window.location) || {} as any; + this._urlState = HistoryUtil.parseUrl(window.location) || {} as any; // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.pathname !== "/home") { @@ -128,7 +128,7 @@ export class MainView extends React.Component { const type = pathname[0]; if (type === "doc") { CurrentUserUtils.MainDocId = pathname[1]; - if (!MainView.userDoc) { + if (!this.userDoc) { runInAction(() => this.flyoutWidth = 0); DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt) => field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); @@ -172,14 +172,14 @@ export class MainView extends React.Component { initAuthenticationRouters = async () => { // Load the user's active workspace, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; - if (received && !MainView.userDoc) { + if (received && !this.userDoc) { reaction( () => CurrentUserUtils.GuestTarget, target => target && this.createNewWorkspace(), { fireImmediately: true } ); } else { - if (received && MainView._urlState.sharing) { + if (received && this._urlState.sharing) { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, initialized => initialized && received && DocServer.GetRefField(received).then(docField => { if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { @@ -188,7 +188,7 @@ export class MainView extends React.Component { }), ); } - const doc = MainView.userDoc && await Cast(MainView.userDoc.activeWorkspace, Doc); + const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc); if (doc) { this.openWorkspace(doc); } else { @@ -199,7 +199,7 @@ export class MainView extends React.Component { @action createNewWorkspace = async (id?: string) => { - const workspaces = Cast(MainView.userDoc.myWorkspaces, Doc) as Doc; + const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; const workspaceCount = DocListCast(workspaces.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, @@ -228,10 +228,10 @@ export class MainView extends React.Component { if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); - MainView.userDoc ? (MainView.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); + this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); } - const state = MainView._urlState; - if (state.sharing === true && !MainView.userDoc) { + const state = this._urlState; + if (state.sharing === true && !this.userDoc) { DocServer.Control.makeReadOnly(); } else { fromHistory || HistoryUtil.pushState({ @@ -257,7 +257,7 @@ export class MainView extends React.Component { } // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) setTimeout(async () => { - const col = MainView.userDoc && await Cast(MainView.userDoc.rightSidebarCollection, Doc); + const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc); col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); }, 100); return true; @@ -387,7 +387,7 @@ export class MainView extends React.Component { mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight); @computed get flyout() { - const sidebarContent = MainView.userDoc?.["tabs-panelContainer"]; + const sidebarContent = this.userDoc?.["tabs-panelContainer"]; if (!(sidebarContent instanceof Doc)) { return (null); } @@ -459,8 +459,8 @@ export class MainView extends React.Component { } @computed get mainContent() { - const sidebar = MainView.userDoc?.["tabs-panelContainer"]; - return !MainView.userDoc || !(sidebar instanceof Doc) ? (null) : ( + const sidebar = this.userDoc?.["tabs-panelContainer"]; + return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
.openWorkspace(copiedWorkspace), 0); + setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0); }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss index d83583db1..adef7f81f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss @@ -43,7 +43,7 @@ right: 0; transform: translateY(-50%); - .button { + .FormattedTextBox-button { width: 20px; height: 20px; margin: 0; @@ -57,7 +57,7 @@ text-align: center; position: relative; - .fa-icon { + .FormattedTextBox-fa-icon { position: absolute; top: 50%; left: 50%; diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 15cd68c26..c600e4861 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -18,9 +18,9 @@ import wiki from "wikijs"; import { DocumentType } from "../../../documents/DocumentTypes"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action } from "mobx"; -// import { LinkManager } from "../../../util/LinkManager"; -// import { LinkDocPreview } from "../LinkDocPreview"; -// import { DocumentLinksButton } from "../DocumentLinksButton"; +import { LinkManager } from "../../../util/LinkManager"; +import { LinkDocPreview } from "../LinkDocPreview"; +import { DocumentLinksButton } from "../DocumentLinksButton"; export let formattedTextBoxCommentPlugin = new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } @@ -97,23 +97,23 @@ export class FormattedTextBoxComment { if (FormattedTextBoxComment.linkDoc && !keep && textBox) { if (FormattedTextBoxComment.linkDoc.author) { - // if (FormattedTextBoxComment._deleteRef && FormattedTextBoxComment._deleteRef.contains(e.target as any)) { - // FormattedTextBoxComment.deleteLink(); - // } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) { - // if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - // textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); - // } else { - // DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, - // (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); - // } - // } else { - if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + if (FormattedTextBoxComment._deleteRef && FormattedTextBoxComment._deleteRef.contains(e.target as any)) { + this.deleteLink(); + } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) { + if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + } else { + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, + (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + } } else { - DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, - (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + } else { + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, + (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + } } - //} } } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) { @@ -125,16 +125,16 @@ export class FormattedTextBoxComment { e.preventDefault(); }; root && root.appendChild(FormattedTextBoxComment.tooltip); - }; + } } @action - public static deleteLink = (): void => { - console.log(FormattedTextBoxComment.linkDoc); - // FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; - // //this.props.showLinks(); - // LinkDocPreview.LinkInfo = undefined; - // DocumentLinksButton.EditLink = undefined; + deleteLink = () => { + FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null; + LinkDocPreview.LinkInfo = undefined; + DocumentLinksButton.EditLink = undefined; + //FormattedTextBoxComment.tooltipText = undefined; + FormattedTextBoxComment.Hide(); } public static Hide() { @@ -241,12 +241,22 @@ export class FormattedTextBoxComment { } if (target?.author) { FormattedTextBoxComment.showCommentbox("", view, nbef); - const docPreview =
{target.title} + const docPreview =
+ {target.title}
-
this._deleteRef = r}> -
-
this._followRef = r}> - +
this._deleteRef = r}> +
+
this._followRef = r}> +
; FormattedTextBoxComment.showCommentbox("", view, nbef); - ReactDOM.render( Math.min(350, NumCast(target._width, 350))} - PanelHeight={() => Math.min(250, NumCast(target._height, 250))} - focus={emptyFunction} - whenActiveChanged={returnFalse} - bringToFront={returnFalse} - ContentScaling={returnOne} - NativeWidth={returnZero} - NativeHeight={returnZero} - />, FormattedTextBoxComment.tooltipText); + ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%"; FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%"; -- cgit v1.2.3-70-g09d2 From 54e38339b5292c3eddca0ef9665ec0c2b4d30ba2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 14:20:53 -0400 Subject: added divider lines in richTextMen --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 -- src/client/views/nodes/formattedText/RichTextMenu.scss | 6 ++++++ src/client/views/nodes/formattedText/RichTextMenu.tsx | 7 ++++++- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2a79e2f13..7e5509e85 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1049,7 +1049,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @action onFocused = (e: React.FocusEvent): void => { - console.log("FOUCSS") FormattedTextBox.FocusedBox = this; this.tryUpdateHeight(); @@ -1181,7 +1180,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } public static HadSelection: boolean = false; onBlur = (e: any) => { - console.log("BLURRR") FormattedTextBox.HadSelection = window.getSelection()?.toString() !== ""; //DictationManager.Controls.stop(false); this.endUndoTypingBatch(); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss index 7a0718c16..fbc468292 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.scss +++ b/src/client/views/nodes/formattedText/RichTextMenu.scss @@ -77,6 +77,12 @@ color: white; } } + .richTextMenu-divider { + margin: auto; + border-left: solid #ffffff70 0.5px; + height: 20px; + width: 1px; + } } .link-menu { diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 6fdcaceda..57719a8eb 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -876,11 +876,13 @@ export default class RichTextMenu extends AntimodeMenu { this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), +
]}, this.createColorButton(), this.createHighlighterButton(), this.createLinkButton(), this.createBrushButton(), +
, this.createButton("align-left", "Align Left", undefined, this.alignLeft), this.createButton("align-center", "Align Center", undefined, this.alignCenter), this.createButton("align-right", "Align Right", undefined, this.alignRight), @@ -893,12 +895,15 @@ export default class RichTextMenu extends AntimodeMenu { const row2 =
{this.collapsed ? this.getDragger() : (null)}
+
, {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"), this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"), +
, this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes"), this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), - this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),]} + this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule), +
,]}
{/*
-- cgit v1.2.3-70-g09d2 From eff0c84e6f42bd6be1c58f1009d8d766bcf0a819 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 15:42:22 -0400 Subject: fixed warnings --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/collections/CollectionSchemaView.tsx | 8 ++++---- src/client/views/collections/CollectionSubView.tsx | 1 - src/client/views/collections/CollectionViewChromes.tsx | 6 +++--- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 14 +++++--------- src/client/views/nodes/formattedText/RichTextMenu.tsx | 8 ++++---- src/fields/Doc.ts | 2 +- 7 files changed, 18 insertions(+), 23 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ef216001c..40bc24917 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -368,7 +368,7 @@ export class CurrentUserUtils { doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300 }); } if (doc.emptyScript === undefined) { - doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script" }) + doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script" }); } if (doc.emptyDocHolder === undefined) { doc.emptyDocHolder = Docs.Create.DocumentDocument( diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 23e8a4d49..3c42a2f1c 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -581,14 +581,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { onZoomMenu = (e: React.WheelEvent) => { this.props.active(true) && e.stopPropagation(); if (this.menuCoordinates[0] > e.screenX) { - this.menuCoordinates[0] -= e.screenX //* this.scale; + this.menuCoordinates[0] -= e.screenX; //* this.scale; } else { - this.menuCoordinates[0] += e.screenX //* this.scale; + this.menuCoordinates[0] += e.screenX; //* this.scale; } if (this.menuCoordinates[1] > e.screenY) { - this.menuCoordinates[1] -= e.screenY //* this.scale; + this.menuCoordinates[1] -= e.screenY; //* this.scale; } else { - this.menuCoordinates[1] += e.screenY //* this.scale; + this.menuCoordinates[1] += e.screenY; //* this.scale; } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index ecd16d22f..ed8535ecb 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -9,7 +9,6 @@ import { ScriptField } from "../../../fields/ScriptField"; import { WebField } from "../../../fields/URLField"; import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; -import { Upload } from "../../../server/SharedMediaTypes"; import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Networking } from "../../Network"; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index ab0df88f8..4e91a2928 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -452,9 +452,9 @@ export class CollectionStackingViewChrome extends React.Component { this.props.CollectionView.props.Document._columnsSort = - this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" : - this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending"; - }; + this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" : + this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending"; + } @action resetValue = () => { this._currentKey = this.pivotField; }; render() { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 7e5509e85..664141607 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -68,15 +68,10 @@ export interface FormattedTextBoxProps { xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yMargin?: number; } - -const richTextSchema = createSchema({ - documentText: "string", -}); - export const GoogleRef = "googleDocId"; -type RichTextDocument = makeInterface<[typeof richTextSchema, typeof documentSchema]>; -const RichTextDocument = makeInterface(richTextSchema, documentSchema); +type RichTextDocument = makeInterface<[typeof documentSchema]>; +const RichTextDocument = makeInterface(documentSchema); type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @@ -661,7 +656,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp incomingValue => { if (incomingValue !== undefined && this._editorView && !this._applyingChange) { const updatedState = JSON.parse(incomingValue); - if (JSON.stringify(this._editorView!.state.toJSON()) !== JSON.stringify(updatedState)) { + if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) { this._editorView.updateState(EditorState.fromJSON(this.config, updatedState)); this.tryUpdateHeight(); } @@ -1293,7 +1288,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"), pointerEvents: interactive ? undefined : "none", fontSize: Cast(this.layoutDoc._fontSize, "number", null), - fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit") + fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"), + transition: "opacity 1s" }} onContextMenu={this.specificContextMenu} onKeyDown={this.onKeyPress} diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 57719a8eb..0fcddfc43 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -149,7 +149,7 @@ export default class RichTextMenu extends AntimodeMenu { this._reaction?.(); } - public delayHide = () => { this._delayHide = true; } + public delayHide = () => this._delayHide = true; @action changeView(view: EditorView) { @@ -599,7 +599,7 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; } @action setActiveColor(color: string) { this.activeFontColor = color; } - get TextView() { return (this.view as any).TextView as FormattedTextBox } + get TextView() { return (this.view as any).TextView as FormattedTextBox; } createColorButton() { const self = this; @@ -608,7 +608,7 @@ export default class RichTextMenu extends AntimodeMenu { e.stopPropagation(); self.TextView.endUndoTypingBatch(); UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.TextView.EditorView!.focus() + self.TextView.EditorView!.focus(); } function changeColor(e: React.PointerEvent, color: string) { e.preventDefault(); @@ -616,7 +616,7 @@ export default class RichTextMenu extends AntimodeMenu { self.setActiveColor(color); self.TextView.endUndoTypingBatch(); UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.TextView.EditorView!.focus() + self.TextView.EditorView!.focus(); } const button = diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e4d11dd4d..4a35ef018 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -965,7 +965,7 @@ export namespace Doc { container._docFilters = new List(docFilters); } } - }) + }); } export function readDocRangeFilter(doc: Doc, key: string) { const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []); -- cgit v1.2.3-70-g09d2 From 24ac6997048932c4f2717651e992f744e6a5276a Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 1 Jul 2020 17:42:38 -0500 Subject: started clicking mode popup --- src/client/views/MainView.tsx | 4 +-- .../views/collections/CollectionLinearView.scss | 42 +++++++++++++++------- .../views/collections/CollectionLinearView.tsx | 25 ++++++++++++- src/client/views/linking/LinkMenu.tsx | 6 ++-- src/client/views/linking/LinkMenuItem.tsx | 3 +- src/client/views/nodes/DocumentLinksButton.tsx | 31 +++++++++++++--- src/client/views/nodes/DocumentView.tsx | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 36 +++++++++++++++++++ 8 files changed, 124 insertions(+), 24 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f6db1af66..662232810 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -4,7 +4,7 @@ import { faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, - faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, + faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight } from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; @@ -138,7 +138,7 @@ export class MainView extends React.Component { } library.add(faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, - faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, fileSolid, + faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faTimesCircle, faWindowMaximize, faAddressCard, fileSolid, faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss index 123a27deb..90c96e08b 100644 --- a/src/client/views/collections/CollectionLinearView.scss +++ b/src/client/views/collections/CollectionLinearView.scss @@ -1,12 +1,27 @@ @import "../globalCssVariables"; @import "../_nodeModuleOverrides"; -.collectionLinearView-outer{ +.collectionLinearView-outer { overflow: hidden; - height:100%; + height: 100%; + .collectionLinearView { - display:flex; + display: flex; height: 100%; + + >span { + margin-top: "auto"; + margin-bottom: "auto"; + background: $dark-color; + color: $light-color; + display: inline-block; + border-radius: 18px; + margin-top: auto; + margin-bottom: auto; + margin-right: 3px; + cursor: pointer; + } + >label { margin-top: "auto"; margin-bottom: "auto"; @@ -17,15 +32,15 @@ font-size: 12.5px; width: 18px; height: 18px; - margin-top:auto; - margin-bottom:auto; + margin-top: auto; + margin-bottom: auto; margin-right: 3px; cursor: pointer; transition: transform 0.2s; } label p { - padding-left:5px; + padding-left: 5px; } label:hover { @@ -36,13 +51,14 @@ >input { display: none; } + >input:not(:checked)~.collectionLinearView-content { display: none; } >input:checked~label { transform: rotate(45deg); - transition: transform 0.5s; +transition: transform 0.5s; cursor: pointer; } @@ -52,12 +68,14 @@ position: relative; margin-top: auto; - .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable { - position:relative; - margin:auto; + .collectionLinearView-docBtn, + .collectionLinearView-docBtn-scalable { + position: relative; + margin: auto; margin-left: 3px; transform-origin: center 80%; } + .collectionLinearView-docBtn-scalable:hover { transform: scale(1.15); } @@ -68,10 +86,10 @@ border-radius: 18px; font-size: 15px; } - + .collectionLinearView-round-button:hover { transform: scale(1.15); } } } -} +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index f38eeaf41..29deb906a 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -13,6 +13,8 @@ import { CollectionSubView } from './CollectionSubView'; import { DocumentView } from '../nodes/DocumentView'; import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; +import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -75,6 +77,11 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { return new Transform(-translateX, -translateY, 1); } + @action + exitLongLinks = () => { + DocumentLinksButton.StartLink = undefined + } + render() { const guid = Utils.GenerateGuid(); const flexDir: any = StrCast(this.Document.flexDirection); @@ -82,7 +89,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const color = StrCast(this.props.Document.color, "white"); return
- @@ -130,6 +142,17 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
; })}
+ {DocumentLinksButton.StartLink ? e.stopPropagation()} > + + Creating Link From: {DocumentLinksButton.StartLink.title} + + + + : null}
; } diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 2a9bbed39..c36e7b67c 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -32,9 +32,9 @@ export class LinkMenu extends React.Component { DocumentLinksButton.EditLink = undefined; } - // if (this._linkMenuRef && !this._linkMenuRef.contains(e.target)) { - // DocumentLinksButton.EditLink = undefined; - // } + if (this._linkMenuRef && !this._linkMenuRef.contains(e.target as any)) { + DocumentLinksButton.EditLink = undefined; + } } @action componentDidMount() { diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index d5d31d1ba..54dca261d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -137,6 +137,7 @@ export class LinkMenuItem extends React.Component { @action.bound async followDefault() { + console.log("FOLLOWWW"); DocumentLinksButton.EditLink = undefined; LinkDocPreview.LinkInfo = undefined; DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); @@ -174,7 +175,7 @@ export class LinkMenuItem extends React.Component { {/*
*/}
-
+
this.followDefault()} onContextMenu={this.onContextMenu}>
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index c625c4cd6..d04cdd34c 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -24,6 +24,8 @@ interface DocumentLinksButtonProps { export class DocumentLinksButton extends React.Component { private _linkButton = React.createRef(); + @observable public static StartLink: DocumentView | undefined; + @action onLinkButtonMoved = (e: PointerEvent) => { if (this._linkButton.current !== null) { @@ -51,11 +53,9 @@ export class DocumentLinksButton extends React.Component { - setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { - if (doubleTap) { + if (doubleTap && this.props.InMenu) { DocumentLinksButton.StartLink = this.props.View; } else if (!!!this.props.InMenu) { DocumentLinksButton.EditLink = this.props.View; @@ -63,6 +63,17 @@ export class DocumentLinksButton extends React.Component { + if (this.props.InMenu) { + DocumentLinksButton.StartLink = this.props.View; + } else if (!!!this.props.InMenu) { + DocumentLinksButton.EditLink = this.props.View; + DocumentLinksButton.EditLinkLoc = [e.clientX + 10, e.clientY]; + } + } + completeLink = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e, doubleTap) => { if (doubleTap) { @@ -76,6 +87,15 @@ export class DocumentLinksButton extends React.Component { + if (DocumentLinksButton.StartLink === this.props.View) { + DocumentLinksButton.StartLink = undefined; + } else { + DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View && + DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag"); + } + } + @observable public static EditLink: DocumentView | undefined; public static EditLinkLoc: number[] = [0, 0]; @@ -86,7 +106,7 @@ export class DocumentLinksButton extends React.Component
LinkDocPreview.LinkInfo = undefined)} // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = { // addDocTab: this.props.View.props.addDocTab, @@ -97,7 +117,8 @@ export class DocumentLinksButton extends React.Component {links.length && !!!this.props.InMenu ? links.length : }
- {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
: (null)} + {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View ?
: (null)} {DocumentLinksButton.StartLink === this.props.View ?
: (null)}
; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 09eeaee36..6820b2047 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -116,6 +116,7 @@ export class DocumentView extends DocComponent(Docu protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; private holdDisposer?: InteractionUtils.MultiTouchEventDisposer; + public get title() { return this.props.Document.title } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2a79e2f13..dc7d8c7ca 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -58,6 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView"; import "./FormattedTextBox.scss"; import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment'; import React = require("react"); +import { DocumentManager } from '../../../util/DocumentManager'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -1047,6 +1048,40 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } + @action + onDoubleClick = (e: React.MouseEvent): void => { + + this.doLinkOnDeselect(); + FormattedTextBox._downEvent = true; + FormattedTextBoxComment.textBox = this; + if (this.props.onClick && e.button === 0 && !this.props.isSelected(false)) { + e.preventDefault(); + } + if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar + e.stopPropagation(); // if the text box is selected, then it consumes all down events + } + } + if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { + e.preventDefault(); + } + FormattedTextBoxComment.Hide(); + if (FormattedTextBoxComment.linkDoc) { + if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { + this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + } else { + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document, + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + } + } + + (e.nativeEvent as any).formattedHandled = true; + + if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) { + e.stopPropagation(); + } + } + @action onFocused = (e: React.FocusEvent): void => { console.log("FOUCSS") @@ -1317,6 +1352,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } })} + onDoubleClick={this.onDoubleClick} >
Date: Wed, 1 Jul 2020 16:47:47 -0700 Subject: added color picker for highlighter --- .../views/nodes/formattedText/RichTextMenu.tsx | 2 +- src/client/views/pdf/PDFMenu.scss | 19 ++++++ src/client/views/pdf/PDFMenu.tsx | 68 ++++++++++++++++++++-- 3 files changed, 82 insertions(+), 7 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 0fcddfc43..b9ca82243 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -933,7 +933,7 @@ interface ButtonDropdownProps { } @observer -class ButtonDropdown extends React.Component { +export class ButtonDropdown extends React.Component { @observable private showDropdown: boolean = false; private ref: HTMLDivElement | null = null; diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss index 3c08ba80d..fa43a99b2 100644 --- a/src/client/views/pdf/PDFMenu.scss +++ b/src/client/views/pdf/PDFMenu.scss @@ -3,4 +3,23 @@ width: 200px; padding: 5px; grid-template-columns: 90px 20px 90px; +} + +.color-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + + button.color-button { + width: 20px; + height: 20px; + border-radius: 15px !important; + margin: 3px; + border: 2px solid transparent !important; + padding: 3px; + + &.active { + border: 2px solid white; + } + } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 6dcf5cce6..00c56d73e 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -1,22 +1,43 @@ import React = require("react"); import "./PDFMenu.scss"; -import { observable, action, } from "mobx"; +import { observable, action, computed, } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { unimplementedFunction, returnFalse } from "../../../Utils"; +import { unimplementedFunction, returnFalse, Utils } from "../../../Utils"; import AntimodeMenu from "../AntimodeMenu"; import { Doc, Opt } from "../../../fields/Doc"; +import { ColorState } from "react-color"; +import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu"; + @observer export default class PDFMenu extends AntimodeMenu { static Instance: PDFMenu; private _commentCont = React.createRef(); + private _palette = [ + "rgba(208, 2, 27, 0.8)", + "rgba(238, 0, 0, 0.8)", + "rgba(245, 166, 35, 0.8)", + "rgba(248, 231, 28, 0.8)", + "rgba(245, 230, 95, 0.616)", + "rgba(139, 87, 42, 0.8)", + "rgba(126, 211, 33, 0.8)", + "rgba(65, 117, 5, 0.8)", + "rgba(144, 19, 254, 0.8)", + "rgba(238, 169, 184, 0.8)", + "rgba(224, 187, 228, 0.8)", + "rgba(225, 223, 211, 0.8)", + "rgba(255, 255, 255, 0.8)", + "rgba(155, 155, 155, 0.8)", + "rgba(0, 0, 0, 0.8)"]; @observable private _keyValue: string = ""; @observable private _valueValue: string = ""; @observable private _added: boolean = false; + @observable private highlightColor: string = "rgba(245, 230, 95, 0.616)"; + @observable public _colorBtn = false; @observable public Highlighting: boolean = false; @observable public Status: "pdf" | "annotation" | "" = ""; @@ -70,11 +91,47 @@ export default class PDFMenu extends AntimodeMenu { @action highlightClicked = (e: React.MouseEvent) => { - if (!this.Highlight("rgba(245, 230, 95, 0.616)") && this.Pinned) { // yellowish highlight color for a marker type highlight + if (!this.Highlight(this.highlightColor) && this.Pinned) { this.Highlighting = !this.Highlighting; } } + @computed get highlighter() { + const button = + ; + + const dropdownContent = +
+

Change highlighter color:

+
+ {this._palette.map(color => { + if (color) { + return this.highlightColor === color ? + : + ; + } + })} +
+
; + return ( + + ); + } + + @action + changeHighlightColor = (color: string, e: React.PointerEvent) => { + const col: ColorState = { + hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, + rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", + }; + e.preventDefault(); + e.stopPropagation(); + this.highlightColor = Utils.colorString(col); + } + deleteClicked = (e: React.PointerEvent) => { this.Delete(); } @@ -101,12 +158,11 @@ export default class PDFMenu extends AntimodeMenu { render() { const buttons = this.Status === "pdf" ? [ - , + this.highlighter, , + , ] : [ , -- cgit v1.2.3-70-g09d2 From fcbf3a161839111ac609ec737cd56c71efde18a1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 21:13:10 -0400 Subject: added basic synchronizatoin between links and their hypertext anchors. more work is needed to handle overlapping anchors. --- src/client/util/LinkManager.ts | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 39 ++++++++++++++- .../views/nodes/formattedText/RichTextMenu.tsx | 32 ++++--------- .../nodes/formattedText/prosemirrorPatches.js | 55 ++++++++++++++++++++++ 4 files changed, 103 insertions(+), 25 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 9b4dc2630..749fabfcc 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -49,7 +49,7 @@ export class LinkManager { } public deleteLink(linkDoc: Doc): boolean { - if (LinkManager.Instance.LinkManagerDoc) { + if (LinkManager.Instance.LinkManagerDoc && linkDoc instanceof Doc) { Doc.RemoveDocFromList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc); return true; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 664141607..f32c17563 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -8,7 +8,7 @@ import { baseKeymap, selectAll } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { inputRules } from 'prosemirror-inputrules'; import { keymap } from "prosemirror-keymap"; -import { Fragment, Mark, Node, Slice } from "prosemirror-model"; +import { Fragment, Mark, Node, Slice, Schema } from "prosemirror-model"; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state"; import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; @@ -16,6 +16,7 @@ import { DateField } from '../../../../fields/DateField'; import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym } from "../../../../fields/Doc"; import { documentSchema } from '../../../../fields/documentSchemas'; import applyDevTools = require("prosemirror-dev-tools"); +import { removeMarkWithAttrs } from "./prosemirrorPatches"; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; @@ -56,7 +57,7 @@ import { DocumentButtonBar } from '../../DocumentButtonBar'; import { AudioBox } from '../AudioBox'; import { FieldView, FieldViewProps } from "../FieldView"; import "./FormattedTextBox.scss"; -import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment'; +import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment'; import React = require("react"); library.add(faEdit); @@ -87,6 +88,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _editorView: Opt; private _applyingChange: boolean = false; private _searchIndex = 0; + private _cachedLinks: Doc[] = []; private _undoTyping?: UndoManager.Batch; private _disposers: { [name: string]: IReactionDisposer } = {}; private _dropDisposer?: DragManager.DragDropDisposer; @@ -141,6 +143,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } + // removes all hyperlink anchors for the removed linkDoc + // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one. + // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing. + public RemoveLinkFromDoc(linkDoc?: Doc) { + const state = this._editorView?.state; + if (state && linkDoc && this._editorView) { + var allHrefs: any[] = []; + state.doc.nodesBetween(0, state.doc.nodeSize - 2, (node: any, pos: number, parent: any) => { + const foundMark = findLinkMark(node.marks); + const newHrefs = foundMark?.attrs.allHrefs.filter((a: any) => a.href.includes(linkDoc[Id])) || []; + allHrefs = newHrefs.length ? newHrefs : allHrefs; + return true; + }); + if (allHrefs.length) { + this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.link, { allHrefs })); + } + } + } + // removes all the specified link referneces from the selection. + // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references. + public RemoveLinkFromSelection(allHrefs: { href: string, title: string, linkId: string, targetId: string }[]) { + const state = this._editorView?.state; + if (state && this._editorView) { + this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allHrefs })); + } + } + linkOnDeselect: Map = new Map(); doLinkOnDeselect() { @@ -626,6 +655,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } componentDidMount() { + this._cachedLinks = DocListCast(this.Document.links); + this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + newLinks => { + this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); + this._cachedLinks = newLinks; + }); this._disposers.buttonBar = reaction( () => DocumentButtonBar.Instance, instance => { diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 0fcddfc43..6366f299d 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -772,28 +772,16 @@ export default class RichTextMenu extends AntimodeMenu { } deleteLink = () => { - if (!this.view) return; - - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type === this.view!.state.schema.marks.link); - const href = link!.attrs.allHrefs.length > 0 ? link!.attrs.allHrefs[0].href : undefined; - if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - if (linkclicked) { - DocServer.GetRefField(linkclicked).then(async linkDoc => { - if (linkDoc instanceof Doc) { - LinkManager.Instance.deleteLink(linkDoc); - this.view!.dispatch(this.view!.state.tr.removeMark(this.view!.state.selection.from, this.view!.state.selection.to, this.view!.state.schema.marks.link)); - } - }); - } - } else { - if (node) { - const { tr, schema, selection } = this.view.state; - const extension = this.linkExtend(selection.$anchor, href); - this.view.dispatch(tr.removeMark(extension.from, extension.to, schema.marks.link)); - } + if (this.view) { + const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.link); + if (link) { + const allHrefs = link.attrs.allHrefs.slice(); + this.TextView.RemoveLinkFromSelection(link.attrs.allHrefs); + // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected. + allHrefs.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { + const linkId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)) + }); } } } diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js index 763961958..0969ea4ef 100644 --- a/src/client/views/nodes/formattedText/prosemirrorPatches.js +++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js @@ -9,6 +9,7 @@ var prosemirrorModel = require('prosemirror-model'); exports.liftListItem = liftListItem; exports.sinkListItem = sinkListItem; exports.wrappingInputRule = wrappingInputRule; +exports.removeMarkWithAttrs = removeMarkWithAttrs; // :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool // Create a command to lift the list item around the selection up into // a wrapping list. @@ -139,3 +140,57 @@ function wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate, customWith } +// :: ([Mark]) → ?Mark +// Tests whether there is a mark of this type in the given set. +function isInSetWithAttrs(mark, set, attrs) { + for (var i = 0; i < set.length; i++) { + if (set[i].type == mark) { + if (Array.from(Object.keys(attrs)).reduce((p, akey) => { + return p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey]); + }, true)) { + return set[i]; + } + } + } +}; + +// :: (number, number, ?union) → this +// Remove marks from inline nodes between `from` and `to`. When `mark` +// is a single mark, remove precisely that mark. When it is a mark type, +// remove all marks of that type. When it is null, remove all marks of +// any type. +function removeMarkWithAttrs(tr, from, to, mark, attrs) { + if (mark === void 0) mark = null; + + var matched = [], step = 0; + tr.doc.nodesBetween(from, to, function (node, pos) { + if (!node.isInline) { return } + step++; + var toRemove = null; + if (mark) { + if (isInSetWithAttrs(mark, node.marks, attrs)) { toRemove = [mark]; } + } else { + toRemove = node.marks; + } + if (toRemove && toRemove.length) { + var end = Math.min(pos + node.nodeSize, to); + for (var i = 0; i < toRemove.length; i++) { + var style = toRemove[i], found$1 = (void 0); + for (var j = 0; j < matched.length; j++) { + var m = matched[j]; + if (m.step == step - 1 && style.eq(matched[j].style)) { found$1 = m; } + } + if (found$1) { + found$1.to = end; + found$1.step = step; + } else { + matched.push({ style: style, from: Math.max(pos, from), to: end, step: step }); + } + } + } + }); + matched.forEach(function (m) { return tr.step(new prosemirrorTransform.RemoveMarkStep(m.from, m.to, m.style)); }); + return tr +}; + + -- cgit v1.2.3-70-g09d2 From 19a48bfe9b2087d23ab6bbb384f8edc18ed07d81 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 21:26:35 -0400 Subject: renamed link to linkAnchor and allHrefs to allLinks in rich text code --- .../views/nodes/formattedText/FormattedTextBox.tsx | 36 +++++++++++----------- .../formattedText/FormattedTextBoxComment.tsx | 4 +-- .../views/nodes/formattedText/RichTextMenu.tsx | 18 +++++------ .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/formattedText/marks_rts.ts | 20 ++++++------ src/fields/RichTextUtils.ts | 2 +- 6 files changed, 41 insertions(+), 41 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f32c17563..9c7c46ae9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -149,24 +149,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public RemoveLinkFromDoc(linkDoc?: Doc) { const state = this._editorView?.state; if (state && linkDoc && this._editorView) { - var allHrefs: any[] = []; + var allLinks: any[] = []; state.doc.nodesBetween(0, state.doc.nodeSize - 2, (node: any, pos: number, parent: any) => { const foundMark = findLinkMark(node.marks); - const newHrefs = foundMark?.attrs.allHrefs.filter((a: any) => a.href.includes(linkDoc[Id])) || []; - allHrefs = newHrefs.length ? newHrefs : allHrefs; + const newHrefs = foundMark?.attrs.allLinks.filter((a: any) => a.href.includes(linkDoc[Id])) || []; + allLinks = newHrefs.length ? newHrefs : allLinks; return true; }); - if (allHrefs.length) { - this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.link, { allHrefs })); + if (allLinks.length) { + this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allLinks })); } } } // removes all the specified link referneces from the selection. // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references. - public RemoveLinkFromSelection(allHrefs: { href: string, title: string, linkId: string, targetId: string }[]) { + public RemoveLinkFromSelection(allLinks: { href: string, title: string, linkId: string, targetId: string }[]) { const state = this._editorView?.state; if (state && this._editorView) { - this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allHrefs })); + this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allLinks })); } } @@ -206,8 +206,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.linkOnDeselect.set(key, value); const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); - const allHrefs = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }]; - const link = this._editorView.state.schema.marks.link.create({ allHrefs, location: "onRight", title: value }); + const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }]; + const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value }); const mval = this._editorView.state.schema.marks.metadataVal.create(); const offset = (tx.selection.to === range!.end - 1 ? -1 : 0); tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval); @@ -275,8 +275,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const lastSel = Math.min(flattened.length - 1, this._searchIndex); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; const alink = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: target }, "automatic")!; - const allHrefs = [{ href: Utils.prepend("/doc/" + alink[Id]), title: "a link", targetId: target[Id], linkId: alink[Id] }]; - const link = this._editorView.state.schema.marks.link.create({ allHrefs, title: "a link", location }); + const allLinks = [{ href: Utils.prepend("/doc/" + alink[Id]), title: "a link", targetId: target[Id], linkId: alink[Id] }]; + const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, title: "a link", location }); this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link)); } } @@ -643,9 +643,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = state.tr.addMark(sel.from, sel.to, splitter); sel.from !== sel.to && tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { - const allHrefs = [{ href, title, targetId, linkId }]; - allHrefs.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.link.name)?.attrs.allHrefs ?? [])); - const link = state.schema.marks.link.create({ allHrefs, title, location, linkId }); + const allLinks = [{ href, title, targetId, linkId }]; + allLinks.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.linkAnchor.name)?.attrs.allLinks ?? [])); + const link = state.schema.marks.linkAnchor.create({ allLinks, title, location, linkId }); tr = tr.addMark(pos, pos + node.nodeSize, link); } }); @@ -766,8 +766,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return node.copy(content.frag); } const marks = [...node.marks]; - const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link); - return linkIndex !== -1 && marks[linkIndex].attrs.allHrefs.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined; + const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor); + return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined; }; let start = 0; @@ -949,8 +949,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const allHrefs = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; - const link = view.state.schema.mark(view.state.schema.marks.link, { allHrefs, location: "onRight", title, docref: true }); + const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; + const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 90f2c0aa6..4c90b6afd 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -27,7 +27,7 @@ export function findUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid); } export function findLinkMark(marks: Mark[]): Mark | undefined { - return marks.find(m => m.type === schema.marks.link); + return marks.find(m => m.type === schema.marks.linkAnchor); } export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) { let before = 0; @@ -182,7 +182,7 @@ export class FormattedTextBoxComment { state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node)); child = child || (nbef && state.selection.$from.nodeBefore); const mark = child ? findLinkMark(child.marks) : undefined; - const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allHrefs.find((item: { href: string }) => item.href)?.href || forceUrl; + const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl; if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) { FormattedTextBoxComment.tooltipText.textContent = "external => " + href; (FormattedTextBoxComment.tooltipText as any).href = href; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 6366f299d..3ec75e680 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -737,7 +737,7 @@ export default class RichTextMenu extends AntimodeMenu { const node = this.view.state.selection.$from.nodeAfter; const link = node && node.marks.find(m => m.type.name === "link"); if (link) { - const href = link.attrs.allHrefs.length > 0 ? link.attrs.allHrefs[0].href : undefined; + const href = link.attrs.allLinks.length > 0 ? link.attrs.allLinks[0].href : undefined; if (href) { if (href.indexOf(Utils.prepend("/doc/")) === 0) { const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; @@ -773,27 +773,27 @@ export default class RichTextMenu extends AntimodeMenu { deleteLink = () => { if (this.view) { - const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.link); + const link = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.linkAnchor); if (link) { - const allHrefs = link.attrs.allHrefs.slice(); - this.TextView.RemoveLinkFromSelection(link.attrs.allHrefs); + const allLinks = link.attrs.allLinks.slice(); + this.TextView.RemoveLinkFromSelection(link.attrs.allLinks); // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected. - allHrefs.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { + allLinks.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { const linkId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)) + linkId && DocServer.GetRefField(linkId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); }); } } } linkExtend($start: ResolvedPos, href: string) { - const mark = this.view!.state.schema.marks.link; + const mark = this.view!.state.schema.marks.linkAnchor; let startIndex = $start.index(); let endIndex = $start.indexAfter(); - while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) startIndex--; - while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allHrefs.find((item: { href: string }) => item.href === href)).length) endIndex++; + while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allLinks.find((item: { href: string }) => item.href === href)).length) startIndex--; + while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allLinks.find((item: { href: string }) => item.href === href)).length) endIndex++; let startPos = $start.start(); let endPos = startPos; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 612a824fc..ca30dde9d 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -279,7 +279,7 @@ export class RichTextRules { DocUtils.Publish(target, docid, returnFalse, returnFalse); DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to"); }); - const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid }); + const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid }); return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link); } return state.tr; diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 54b61aa20..3d7d71b14 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -17,12 +17,12 @@ export const marks: { [index: string]: MarkSpec } = { return ["div", { className: "dummy" }, 0]; } }, - // :: MarkSpec A link. Has `href` and `title` attributes. `title` + // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title` // defaults to the empty string. Rendered and parsed as an `` // element. - link: { + linkAnchor: { attrs: { - allHrefs: { default: [] as { href: string, title: string, linkId: string, targetId: string }[] }, + allLinks: { default: [] as { href: string, title: string, linkId: string, targetId: string }[] }, showPreview: { default: true }, location: { default: null }, title: { default: null }, @@ -31,22 +31,22 @@ export const marks: { [index: string]: MarkSpec } = { inclusive: false, parseDOM: [{ tag: "a[href]", getAttrs(dom: any) { - return { allHrefs: [{ href: dom.getAttribute("href"), title: dom.getAttribute("title"), linkId: dom.getAttribute("linkids"), targetId: dom.getAttribute("targetids") }], location: dom.getAttribute("location"), }; + return { allLinks: [{ href: dom.getAttribute("href"), title: dom.getAttribute("title"), linkId: dom.getAttribute("linkids"), targetId: dom.getAttribute("targetids") }], location: dom.getAttribute("location"), }; } }], toDOM(node: any) { - const targetids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, ""); - const linkids = node.attrs.allHrefs.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, ""); + const targetids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, ""); + const linkids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, ""); return node.attrs.docref && node.attrs.title ? - ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allHrefs[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] : - node.attrs.allHrefs.length === 1 ? - ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}`, href: node.attrs.allHrefs[0].href }, 0] : + ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] : + node.attrs.allLinks.length === 1 ? + ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href }, 0] : ["div", { class: "prosemirror-anchor" }, ["span", { class: "prosemirror-linkBtn" }, ["a", { ...node.attrs, class: linkids, targetids, title: `${node.attrs.title}` }, 0], ["input", { class: "prosemirror-hrefoptions" }], ], - ["div", { class: "prosemirror-links" }, ...node.attrs.allHrefs.map((item: { href: string, title: string }) => + ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) => ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title] )] ]; diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index 7c7bf3e12..a590c88c4 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -392,7 +392,7 @@ export namespace RichTextUtils { const { attrs } = mark; switch (converted) { case "link": - let url = attrs.allHrefs.length ? attrs.allHrefs[0].href : ""; + let url = attrs.allLinks.length ? attrs.allLinks[0].href : ""; const delimiter = "/doc/"; const alreadyShared = "?sharing=true"; if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) { -- cgit v1.2.3-70-g09d2 From 713b200804fa84d654aeb0051e086641d55d95d9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 2 Jul 2020 10:17:26 -0400 Subject: started some cleanup for Mobile .. lookslike workspaces view is broken now --- src/client/util/CurrentUserUtils.ts | 3 +- src/client/views/AntimodeMenu.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 7 +- src/client/views/nodes/PDFBox.tsx | 3 - src/client/views/nodes/RadialMenu.tsx | 2 - .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +- src/mobile/MobileInterface.tsx | 393 +++++++++------------ 7 files changed, 170 insertions(+), 246 deletions(-) (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e86f72684..6906c0bb3 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -32,7 +32,6 @@ export class CurrentUserUtils { public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } @computed public static get UserDocument() { return Doc.UserDoc(); } - @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).inkPen as Doc; } @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; @@ -365,7 +364,7 @@ export class CurrentUserUtils { } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc]; + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; DocListCastAsync(templateIconsDoc.data).then(async curIcons => { await Promise.all(curIcons!); requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index 3e4d20fea..b8f77932d 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -131,7 +131,7 @@ export default abstract class AntimodeMenu extends React.Component { } protected getDragger = () => { - return
; + return
; } protected getElement(buttons: JSX.Element[]) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0ec676379..8d122baeb 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -29,7 +29,6 @@ import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from '../collections/CollectionView'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; @@ -185,7 +184,7 @@ export class DocumentView extends DocComponent(Docu // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 }); RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 }); // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Pin", event: () => DockedFrameRenderer.PinDoc(this.props.Document), icon: "map-pin", selected: -1 }); + RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 }); SelectionManager.DeselectAll(); @@ -945,7 +944,7 @@ export class DocumentView extends DocComponent(Docu recommendations.documentIconHeight = 150; recommendations.sourceDoc = this.props.Document; recommendations.sourceDocContext = this.props.ContainingCollectionView!.props.Document; - CollectionDockingView.AddRightSplit(recommendations, undefined); + this.props.addDocTab(recommendations, "onRight"); // RecommendationsBox.Instance.displayRecommendations(e.pageX + 100, e.pageY); } @@ -977,7 +976,7 @@ export class DocumentView extends DocComponent(Docu body.href = urls[i]; bodies.push(body); } - CollectionDockingView.AddRightSplit(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), undefined); + this.props.addDocTab(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), "onRight"); this._showKPQuery = true; this._queries = kps.toString(); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 54f44a0f9..1c5825a8f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -226,9 +226,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent 600)); - console.log("_nativeHeight: " + this.Document._nativeHeight); - console.log("%: " + `${100 / this.contentScaling}%`); const classname = "pdfBox" + (this.active() ? "-interactive" : ""); return
{ - console.log("caught"); te.stopPropagation(); te.preventDefault(); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ae1db76de..270fcb01c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -190,7 +190,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } const state = this._editorView.state.apply(tx); - this?._editorView?.updateState(state); + this._editorView?.updateState(state); (tx.storedMarks && !this._editorView.state.storedMarks) && (this._editorView.state.storedMarks = tx.storedMarks); const tsel = this._editorView.state.selection.$from; @@ -512,8 +512,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } recordDictation = () => { - console.log("recording dictation"); - console.log(this._editorView); DictationManager.Controls.listen({ interimHandler: this.setCurrentBulletContent, continuous: { indefinite: false }, @@ -532,7 +530,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } recordBullet = async () => { - console.log("recording bullet"); const completedCue = "end session"; const results = await DictationManager.Controls.listen({ interimHandler: this.setCurrentBulletContent, @@ -549,7 +546,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setCurrentBulletContent = (value: string) => { if (this._editorView) { - console.log("this._editorView"); const state = this._editorView.state; const now = Date.now(); let mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(now / 1000) }); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 1b934df93..34894250a 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -13,7 +13,7 @@ import { action, computed, observable, reaction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast } from '../fields/Doc'; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils'; +import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils'; import { Docs, DocumentOptions } from '../client/documents/Documents'; import { Scripting } from '../client/util/Scripting'; import { DocumentView } from '../client/views/nodes/DocumentView'; @@ -35,7 +35,6 @@ import { AudioUpload } from "./AudioUpload"; import { Cast, FieldValue } from '../fields/Types'; import RichTextMenu from "../client/views/nodes/formattedText/RichTextMenu"; import { AudioBox } from "../client/views/nodes/AudioBox"; -import { Compute } from "google-auth-library"; library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, @@ -47,22 +46,22 @@ library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngl @observer export class MobileInterface extends React.Component { @observable static Instance: MobileInterface; - @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } - @observable private mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(this.userDoc); - @observable private renderView?: () => JSX.Element; - @observable private sidebarActive: boolean = false; //to toggle sidebar display - @observable private imageUploadActive: boolean = false; //to toggle image upload - @observable private audioUploadActive: boolean = false; - @observable private menuListView: boolean = false; //to switch between menu view (list / icon) + @observable private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc()); + @observable private _sidebarActive: boolean = false; //to toggle sidebar display + @observable private _imageUploadActive: boolean = false; //to toggle image upload + @observable private _audioUploadActive: boolean = false; + @observable private _menuListView: boolean = false; //to switch between menu view (list / icon) @observable private _ink: boolean = false; //toggle whether ink is being dispalyed + @observable private renderView?: () => JSX.Element; - public _activeDoc: Doc = this.mainDoc; // doc updated as the active mobile page is updated (initially home menu) - public _homeDoc: Doc = this.mainDoc; // home menu as a document + private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu) + private _homeDoc: Doc = this._mainDoc; // home menu as a document private _homeMenu: boolean = true; // to determine whether currently at home menu private _child: Doc | null = null; // currently selected document private _parents: Array = []; // array of parent docs (for pathbar) - private _library: Doc = CurrentUserUtils.setupLibrary(this.userDoc); // to access documents in Dash Web + private _library: Doc = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web + + @computed private get mainContainer() { return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } constructor(props: Readonly<{}>) { super(props); @@ -72,7 +71,7 @@ export class MobileInterface extends React.Component { @action componentDidMount = () => { // if the home menu is in list view -> adjust the menu toggle appropriately - this._homeDoc._viewType === "stacking" ? this.menuListView = true : this.menuListView = false; + this._menuListView = this._homeDoc._viewType === "stacking" ? true : false; Doc.SetSelectedTool(InkTool.None); // ink should intially be set to none Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home AudioBox.Enabled = true; @@ -95,14 +94,14 @@ export class MobileInterface extends React.Component { // Switch the mobile view to the given doc @action switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { - if (!this.userDoc) return; + if (!Doc.UserDoc()) return; if (this._activeDoc === this._homeDoc) { this._parents.push(this._activeDoc); this._homeMenu = false; } this._activeDoc = doc; Doc.UserDoc().activeMobile = doc; - onSwitch && onSwitch(); + onSwitch?.(); this.renderView = renderView; // Ensures that switching to home is not registed @@ -113,7 +112,7 @@ export class MobileInterface extends React.Component { // For toggling the hamburger menu @action toggleSidebar = () => { - this.sidebarActive = !this.sidebarActive; + this._sidebarActive = !this._sidebarActive; if (this._ink) { this.onSwitchInking(); @@ -126,7 +125,6 @@ export class MobileInterface extends React.Component { this.switchCurrentView(this._library); this._homeMenu = false; this.toggleSidebar(); - //setTimeout(this.toggleSidebar, 300); } /** @@ -146,13 +144,11 @@ export class MobileInterface extends React.Component { this._child = null; this.switchCurrentView(this._homeDoc); // Case 3: Parent document is any document - } else { - if (doc) { - this._child = doc; - this.switchCurrentView(doc); - this._homeMenu = false; - header.textContent = String(doc.title); - } + } else if (doc) { + this._child = doc; + this.switchCurrentView(doc); + this._homeMenu = false; + header.textContent = String(doc.title); } this._ink = false; // turns ink off } @@ -161,13 +157,13 @@ export class MobileInterface extends React.Component { * Return 'Home", which implies returning to 'Home' menu buttons */ returnHome = () => { - if (!this._homeMenu || this.sidebarActive) { + if (!this._homeMenu || this._sidebarActive) { this._homeMenu = true; this._parents = []; this._child = null; this.switchCurrentView(this._homeDoc); } - if (this.sidebarActive) { + if (this._sidebarActive) { this.toggleSidebar(); } } @@ -185,42 +181,38 @@ export class MobileInterface extends React.Component { /** * DocumentView for graphic display of all documents */ + whitebackground = () => "white"; @computed get displayWorkspaces() { - if (this.mainContainer) { - const backgroundColor = () => "white"; - return ( -
- -
- ); - } + return !this.mainContainer ? (null) : +
+ +
; } - /** * Note: window.innerWidth and window.screen.width compute different values. * window.screen.width is the display size, however window.innerWidth is the @@ -235,8 +227,7 @@ export class MobileInterface extends React.Component { * @param doc: doc for which the method is called */ handleClick = async (doc: Doc) => { - const children = DocListCast(doc.data); - if (doc.type !== "collection" && this.sidebarActive) { + if (doc.type !== "collection" && this._sidebarActive) { this._parents.push(this._activeDoc); this.switchCurrentView(doc); this._homeMenu = false; @@ -267,12 +258,7 @@ export class MobileInterface extends React.Component { * Handles creation of array which is then rendered in renderPathbar() */ createPathname = () => { - const docArray = []; - this._parents.map((doc: Doc, index: any) => { - docArray.push(doc); - }); - docArray.push(this._activeDoc); - return docArray; + return [...this._parents, this._activeDoc]; } // Renders the graphical pathbar @@ -280,24 +266,21 @@ export class MobileInterface extends React.Component { const docArray = this.createPathname(); const items = docArray.map((doc: Doc, index: any) => { if (index === 0) { - return ( - <> - {this._homeMenu ? -
-
this.handlePathClick(doc, index)}>{doc.title} -
-
- : -
-
this.handlePathClick(doc, index)}>{doc.title} -
-
} - ); + return this._homeMenu ? +
+
this.handlePathClick(doc, index)}>{doc.title} +
+
+ : +
+
this.handlePathClick(doc, index)}>{doc.title} +
+
; } else if (doc === this._activeDoc) { return ( @@ -357,14 +340,14 @@ export class MobileInterface extends React.Component { } // Renders the contents of the menu and sidebar - renderDefaultContent = () => { + @computed get renderDefaultContent() { if (this._homeMenu) { return (
-
this.stop(e)}>
+
e.stopPropagation()}>