From 0546ecf205b7d2b76f341a7157beebf95fb888a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 1 Aug 2021 22:43:46 -0400 Subject: made url server references relative. --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 140d39929..f7e9ee028 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; const anchor = Docs.Create.TextanchorDocument(); const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!; - const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }]; + const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }]; const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location }); tr = tr.addMark(flattened[i].from, flattened[i].to, link); }); @@ -705,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) }); - const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]); + const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); 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)) { @@ -1042,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; + const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }]; const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); -- cgit v1.2.3-70-g09d2 From 6d9c65bf95091b0d5ee7e3097995fc9e2ead4415 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 5 Aug 2021 11:21:04 -0400 Subject: improved opening sidebar when following a link to a sidebar annotation. Still needs work when link is from outside of the sidebar document and on first click. --- src/client/views/SidebarAnnos.tsx | 5 +++-- src/client/views/nodes/PDFBox.tsx | 26 +++++++++++++++------- src/client/views/nodes/WebBox.tsx | 24 ++++++++++++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++++++++++++----- 4 files changed, 53 insertions(+), 23 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 7b11723c3..f1d168a22 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -23,6 +23,7 @@ interface ExtraProps { layoutDoc: Doc; rootDoc: Doc; dataDoc: Doc; + showSidebar: boolean; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean; @@ -75,7 +76,7 @@ export class SidebarAnnos extends React.Component { get sidebarKey() { return this.props.fieldKey + "-sidebar"; } filtersHeight = () => 38; screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1); - panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); + panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); @@ -103,7 +104,7 @@ export class SidebarAnnos extends React.Component { {user} ; }; - return !this.props.layoutDoc._showSidebar ? (null) : + return !this.props.showSidebar ? (null) :
{ + if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; this._initialScrollTarget = doc; return this._pdfViewer?.scrollFocus(doc, smooth); @@ -141,15 +144,18 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } - toggleSidebar = action(() => { + toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - this.layoutDoc.nativeWidth = nativeWidth * ratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + if (preview) this._showSidebar = true; + else { + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + } }); settingsPanel() { const pageBtns = <> @@ -202,7 +208,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent
; } - sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; @@ -223,6 +229,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._sidebarRef.current?.anchorMenuClick; + @observable _showSidebar = false; + @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } + @computed get renderPdfView() { TraceMobx(); @@ -255,6 +264,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { + if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; if (doc !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); @@ -449,17 +452,20 @@ export class WebBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } - toggleSidebar = action(() => { + toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - this.layoutDoc.nativeWidth = nativeWidth * ratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + if (preview) this._showSidebar; + else { + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + } }); - sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); @computed get content() { return
) }
; + } + @observable _showSidebar = false; + @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } showInfo = action((anno: Opt) => this._overlayAnnoInfo = anno); setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; @@ -548,10 +557,11 @@ export class WebBox extends ViewBoxAnnotatableComponent { + toggleSidebar = (preview: boolean = false) => { const prevWidth = this.sidebarWidth(); - this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%"; - this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + if (preview) this._showSidebar = true; + else this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%"; + + this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); } sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false); @@ -726,6 +731,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } scrollFocus = (textAnchor: Doc, smooth: boolean) => { + if (DocListCast(this.Document[this.fieldKey + "-sidebar"]).includes(textAnchor) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } const textAnchorId = textAnchor[Id]; const findAnchorFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; @@ -1468,10 +1476,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return ComponentTag === CollectionStackingView ? - {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || this.Document._singleLine ? (null) : this.sidebarHandle} {!this.layoutDoc._showAudio ? (null) : this.audioHandle} -- cgit v1.2.3-70-g09d2 From 5550d58f16a0df090cde5a3605f6307b17e70758 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 5 Aug 2021 17:22:42 -0400 Subject: fixed so that links to sidebar annos show the sidebar in a LinkPreview --- src/client/views/SidebarAnnos.tsx | 3 +- src/client/views/collections/CollectionView.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 68 ++++++++++++++-------- src/client/views/nodes/WebBox.tsx | 35 ++++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 2 - 6 files changed, 70 insertions(+), 44 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index f1d168a22..1f9763d18 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -24,6 +24,7 @@ interface ExtraProps { rootDoc: Doc; dataDoc: Doc; showSidebar: boolean; + nativeWidth: number; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean; @@ -76,7 +77,7 @@ export class SidebarAnnos extends React.Component { get sidebarKey() { return this.props.fieldKey + "-sidebar"; } filtersHeight = () => 38; screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1); - panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); + panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2718cbbbf..2ae06d2f4 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -245,10 +245,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { - console.log("Setting sub", subView); - return this._subView = subView - }), + SetSubView: action((subView: any) => this._subView = subView), addDocument: this.addDocument, moveDocument: this.moveDocument, removeDocument: this.removeDocument, diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fc8fa4eef..b1f2070f8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -9,7 +9,7 @@ import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast, BoolCast } from '../../../fields/Types'; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; -import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils'; +import { Utils, setupMoveUpEvents, emptyFunction, returnOne } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch } from '../../util/UndoManager'; @@ -31,6 +31,7 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer export class PDFBox extends ViewBoxAnnotatableComponent(PdfDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } + public static openSidebarWidth = 250; private _searchString: string = ""; private _initialScrollTarget: Opt; private _pdfViewer: PDFViewer | undefined; @@ -146,11 +147,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } + @observable _previewNativeWidth: Opt = undefined; + @observable _previewWidth: Opt = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - if (preview) this._showSidebar = true; + if (preview) { + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this._showSidebar = true; + } else { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; @@ -208,7 +215,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent ; } - sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : + this._previewWidth ? PDFBox.openSidebarWidth : + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; @@ -232,38 +241,51 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + return 1; + } @computed get renderPdfView() { TraceMobx(); + const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const scale = previewScale * (this.props.scaling?.() || 1); return
600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
- +
+ +
(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } + public static openSidebarWidth = 250; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _mainCont: React.RefObject = React.createRef(); private _outerRef: React.RefObject = React.createRef(); @@ -454,18 +455,27 @@ export class WebBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } + @observable _previewNativeWidth: Opt = undefined; + @observable _previewWidth: Opt = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - if (preview) this._showSidebar; + if (preview) { + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this._showSidebar = true; + } else { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } }); - sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : + this._previewWidth ? WebBox.openSidebarWidth : + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / + NumCast(this.layoutDoc.nativeWidth); @computed get content() { return
this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; render() { - const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; - const scale = this.props.scaling?.() || 1; + const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined; + const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const scale = previewScale * (this.props.scaling?.() || 1); return (
-
+
{ e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} onPointerDown={this.onMarqueeDown} > -
+
{this.content} void; setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; - sidebarWidth: () => number; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); } @@ -550,7 +549,6 @@ export class PDFViewer extends React.Component { onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} style={{ overflowX: this._zoomed !== 1 ? "scroll" : undefined, - width: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeWidth(this.props.Document) - this.props.sidebarWidth() / this.contentScaling : `calc(${100 / this.contentScaling}% - ${this.props.sidebarWidth() / this.contentScaling}px)`, height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`, transform: `scale(${this.contentScaling})` }} > -- cgit v1.2.3-70-g09d2 From d756ffd3daa24270361648454062e98f190ae964 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 Aug 2021 10:34:57 -0400 Subject: fixed webboxes so that you can link to an annotation and it will restore the url as well. a bunch of warnings cleanup. --- src/client/util/DocumentManager.ts | 6 ++---- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/InkStrokeProperties.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 4 ++-- src/client/views/linking/LinkPopup.tsx | 1 - src/client/views/nodes/DocumentLinksButton.tsx | 4 ++-- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 15 ++++++++++----- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/fields/Doc.ts | 2 +- 12 files changed, 22 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 27ae0447a..cb0ee411c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -163,7 +163,8 @@ export class DocumentManager { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; const targetDocContext = contextDoc || annotatedDoc; - var targetDocContextView = targetDocContext && getFirstDocView(targetDocContext); + const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || + (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; if (!docView && annoContainerView) { annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below @@ -181,9 +182,6 @@ export class DocumentManager { if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); } else { // otherwise try to get a view of the context of the target - if (annoContainerView && wasHidden) { // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above - targetDocContextView = annoContainerView; - } if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext._viewTransition = "transform 500ms"; targetDocContextView.props.focus(targetDocContextView.rootDoc, { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index df1e6899d..5f09a322c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -355,7 +355,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
- {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink != doc ?
+ {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink !== doc ?
: (null)} {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) :
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 76ca5b5ec..6444e4451 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -283,7 +283,7 @@ export class InkStrokeProperties { */ @action rotatePoint = (target: PointData, origin: PointData, angle: number) => { - let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; + const rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y; const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y; rotatedTarget.X = newX + origin.X; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a9b5ce465..227635c9b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -93,7 +93,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()]; childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" : - this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined; + this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2ae06d2f4..e65ebf075 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -236,7 +236,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { const stack: HTMLDivElement = tab.contentItem.parent; const header: HTMLDivElement = tab; stack.onscroll = action((e: any) => { - console.log('scrolling...') - }) + console.log('scrolling...'); + }); const moreInfoDrag = document.createElement("div"); moreInfoDrag.className = "lm_iconWrap"; tab._disposers.buttonDisposer = reaction(() => this.view, view => diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 2c4b718f4..df469c53b 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -54,7 +54,6 @@ export class LinkPopup extends React.Component { @action onLinkChange = (e: React.ChangeEvent) => { this.linkURL = e.target.value; - console.log(this.linkURL) } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index b63174e54..7648e866e 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -114,7 +114,7 @@ export class DocumentLinksButton extends React.Component - ) + ); } render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b1f2070f8..23236cf20 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -217,7 +217,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent !this.SidebarShown ? 0 : this._previewWidth ? PDFBox.openSidebarWidth : - (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth) specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 751d63711..ca281d68f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { - if (StrCast(doc.data) !== this._url) this.submitURL(StrCast(doc.data)); + if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl)); if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { this.toggleSidebar(!smooth); } @@ -191,7 +191,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { + (doc instanceof Doc ? [doc] : doc).forEach(doc => doc.webUrl = this._url); + return this.addDocument(doc, annotationKey); + } + sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); - return this.addDocument(doc, sidebarKey); + return this.addDocumentWrapper(doc, sidebarKey); } sidebarBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e, down, delta) => { @@ -482,7 +487,7 @@ export class WebBox extends ViewBoxAnnotatableComponent !this.SidebarShown ? 0 : this._previewWidth ? WebBox.openSidebarWidth : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / - NumCast(this.layoutDoc.nativeWidth); + NumCast(this.layoutDoc.nativeWidth) @computed get content() { return
{ this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); - console.log("highlight") return undefined; }); /** diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6dcf34a3a..0cbfaf067 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1214,7 +1214,7 @@ export namespace Doc { case DocumentType.RTF: return "sticky-note"; case DocumentType.COL: const folder: IconProp = isOpen ? "folder-open" : "folder"; - const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right" + const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right"; return !doc?.isFolder ? folder : chevron; case DocumentType.WEB: return "globe-asia"; case DocumentType.SCREENSHOT: return "photo-video"; -- cgit v1.2.3-70-g09d2 From e7bbdd3b489fea1c508af53345cd0d1f31685cb9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 Aug 2021 18:03:38 -0400 Subject: collabortion fixes: added new acl for allowing people to edit their own text within the same note, fixed playground fields to write to the server without updating other clients. --- src/client/DocServer.ts | 5 +- src/client/documents/Documents.ts | 10 ++-- src/client/util/CurrentUserUtils.ts | 6 +- src/client/util/SharingManager.tsx | 13 ++-- src/client/views/DocComponent.tsx | 6 +- src/client/views/MainView.tsx | 2 +- src/client/views/MarqueeAnnotator.tsx | 4 +- src/client/views/PropertiesView.tsx | 14 ++--- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++- .../formattedText/ProsemirrorExampleTransfer.ts | 70 ++++++++++++++-------- src/fields/Doc.ts | 6 +- src/fields/util.ts | 22 ++++--- 13 files changed, 105 insertions(+), 69 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 59278d2af..d9ae7d64c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -59,7 +59,10 @@ export namespace DocServer { export var PlaygroundFields: string[]; export function setPlaygroundFields(livePlaygroundFields: string[]) { DocServer.PlaygroundFields = livePlaygroundFields; - livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground)); + livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); + } + export function IsPlaygroundField(field: string) { + return DocServer.PlaygroundFields?.includes(field.replace(/^_/, "")); } export function setFieldWriteMode(field: string, writeMode: WriteMode) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 24f777e88..93f0880a4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -577,7 +577,7 @@ export namespace Docs { dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -588,7 +588,7 @@ export namespace Docs { viewProps.author = Doc.CurrentUserEmail; viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); @@ -699,7 +699,7 @@ export namespace Docs { I.author = Doc.CurrentUserEmail; I.rotation = 0; I.data = new InkField(points); - I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; I[Initializing] = false; return I; @@ -1038,8 +1038,8 @@ export namespace DocUtils { title: ComputedField.MakeFunction("generateLinkTitle(self)") as any, "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined, "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined, - "acl-Public": SharingPermissions.Add, - "_acl-Public": SharingPermissions.Add, + "acl-Public": SharingPermissions.Augment, + "_acl-Public": SharingPermissions.Augment, layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null), linkDisplay: true, hidden: true, linkRelationship, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1af6607a7..14c43fb1c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -917,7 +917,7 @@ export class CurrentUserUtils { linkDocs = new Doc(linkDatabaseId, true); (linkDocs as Doc).author = Doc.CurrentUserEmail; (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; + (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; } doc.myLinkDatabase = new PrefetchProxy(linkDocs); } @@ -926,10 +926,10 @@ export class CurrentUserUtils { if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, _chromeHidden: true, boxShadow: "0 0", }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; + (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; } if (sharedDocs instanceof Doc) { Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d283510b7..a8972b988 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import Select from "react-select"; import * as RequestPromise from "request-promise"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util"; @@ -85,7 +85,8 @@ export class SharingManager extends React.Component<{}> { private AclMap = new Map([ [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); @@ -101,7 +102,7 @@ export class SharingManager extends React.Component<{}> { this.targetDoc = target_doc || target?.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; - this.permissions = SharingPermissions.Add; + this.permissions = SharingPermissions.Augment; }); } @@ -366,10 +367,10 @@ export class SharingManager extends React.Component<{}> { const dropdownValues: string[] = Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift("-multiple-"); if (override) dropdownValues.unshift("None"); - return dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => + return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => ( ) ); @@ -546,7 +547,7 @@ export class SharingManager extends React.Component<{}> { ) : (
- {permissions === SharingPermissions.Add ? "Can Augment" : permissions} + {permissions}
)}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fc36c7e43..99c695a4a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,4 +1,4 @@ -import { Doc, Opt, DataSym, AclReadonly, AclAddonly, AclPrivate, AclEdit, AclSym, DocListCastAsync, DocListCast, AclAdmin } from '../../fields/Doc'; +import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; @@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent

effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); + const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); if (docs.length) { setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin Doc.SetInPlace(doc, "isPushpin", undefined, true); @@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent

{ if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 005e46836..8f37172a0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -107,7 +107,7 @@ export class MainView extends React.Component { new InkStrokeProperties(); this._sidebarContent.proto = undefined; if (!MainView.Live) { - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", "panX", "panY", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 805cda95c..a3a3bce56 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -1,6 +1,6 @@ import { action, observable, ObservableMap, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { NumCast } from "../../fields/Types"; @@ -156,7 +156,7 @@ export class MarqueeAnnotator extends React.Component { highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); + const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc); return annotationDoc as Doc ?? undefined; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 8136edf04..de437e1df 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -5,7 +5,7 @@ import { intersection } from "lodash"; import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; @@ -342,12 +342,9 @@ export class PropertiesView extends React.Component { return ; } @@ -402,7 +399,8 @@ export class PropertiesView extends React.Component { [AclUnset, "None"], [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 846d28214..19da7ea00 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,6 +1,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; @@ -298,7 +298,7 @@ export class MarqueeView extends React.Component json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\""); - if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) { const accumTags = [] as string[]; state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => { if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) { @@ -1401,6 +1401,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._rules!.EnteringStyle = false; } e.stopPropagation(); + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) { + e.preventDefault(); + } + } switch (e.key) { case "Escape": this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index d5c77786c..1f78b2204 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,13 +7,14 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; import { Utils } from "../../../../Utils"; import { listSpec } from "../../../../fields/Schema"; import { List } from "../../../../fields/List"; +import { GetEffectiveAcl } from "../../../../fields/util"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -70,25 +71,39 @@ export function buildKeymap>(schema: S, props: any, mapKey return false; }; + const canEdit = (state: any) => { + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + GetEffectiveAcl(props.Document) === AclAugment) { + return false; + } + } + return true; + } + + const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch); + //History commands bind("Mod-z", undo); bind("Shift-Mod-z", redo); !mac && bind("Mod-y", redo); //Commands to modify Mark - bind("Mod-b", toggleMark(schema.marks.strong)); - bind("Mod-B", toggleMark(schema.marks.strong)); + bind("Mod-b", toggleEditableMark(schema.marks.strong)); + bind("Mod-B", toggleEditableMark(schema.marks.strong)); - bind("Mod-e", toggleMark(schema.marks.em)); - bind("Mod-E", toggleMark(schema.marks.em)); + bind("Mod-e", toggleEditableMark(schema.marks.em)); + bind("Mod-E", toggleEditableMark(schema.marks.em)); - bind("Mod-*", toggleMark(schema.marks.code)); + bind("Mod-*", toggleEditableMark(schema.marks.code)); - bind("Mod-u", toggleMark(schema.marks.underline)); - bind("Mod-U", toggleMark(schema.marks.underline)); + bind("Mod-u", toggleEditableMark(schema.marks.underline)); + bind("Mod-U", toggleEditableMark(schema.marks.underline)); //Commands for lists - bind("Ctrl-i", wrapInList(schema.nodes.ordered_list)); + bind("Ctrl-i", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); @@ -96,6 +111,7 @@ export function buildKeymap>(schema: S, props: any, mapKey if (!props.LayoutTemplateString) return addTextBox(false, true); return true; } + if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -121,6 +137,7 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab); if (props.Document._singleLine) return true; + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { @@ -140,24 +157,19 @@ export function buildKeymap>(schema: S, props: any, mapKey }); //Commands to modify BlockType - bind("Ctrl->", wrapIn(schema.nodes.blockquote)); - bind("Alt-\\", setBlockType(schema.nodes.paragraph)); - bind("Shift-Ctrl-\\", setBlockType(schema.nodes.code_block)); + bind("Ctrl->", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any))); + bind("Alt-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any)); + bind("Shift-Ctrl-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any)); - bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))); - }); + bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() })))); for (let i = 1; i <= 6; i++) { - bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i })); + bind("Shift-Ctrl-" + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any)); } //Command to create a horizontal break line const hr = schema.nodes.horizontal_rule; - bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()); - return true; - }); + bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView())); //Command to unselect all bind("Escape", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -173,13 +185,15 @@ export function buildKeymap>(schema: S, props: any, mapKey }; //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => addTextBox(false, true)); + bind("Alt-Enter", () => addTextBox(false, true)); //Command to create a text document to the bottom of the selected textbox - bind("Ctrl-Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => addTextBox(true, true)); + bind("Ctrl-Enter", () => addTextBox(true, true)); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { + if (!canEdit(state)) return true; + if (!deleteSelection(state, (tx: Transaction>) => { dispatch(updateBullets(tx, schema)); })) { @@ -200,6 +214,9 @@ export function buildKeymap>(schema: S, props: any, mapKey //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { if (addTextBox(true, false)) return true; + + if (!canEdit(state)) return true; + const trange = state.selection.$from.blockRange(state.selection.$to); const path = (state.selection.$from as any).path; const depth = trange ? liftTarget(trange) : undefined; @@ -238,18 +255,19 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a blank space bind("Space", (state: EditorState, dispatch: (tx: Transaction) => void) => { + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); dispatch(splitMetadata(marks, state.tr)); return false; }); - bind("Alt-ArrowUp", joinUp); - bind("Alt-ArrowDown", joinDown); - bind("Mod-BracketLeft", lift); + bind("Alt-ArrowUp", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any)); + bind("Alt-ArrowDown", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any)); + bind("Mod-BracketLeft", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any)); const cmd = chainCommands(exitCode, (state, dispatch) => { if (dispatch) { - dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); + canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); return true; } return false; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 0cbfaf067..85ea3cfa9 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -90,7 +90,8 @@ export const DirectLinksSym = Symbol("DirectLinks"); export const AclUnset = Symbol("AclUnset"); export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); -export const AclAddonly = Symbol("AclAddonly"); +export const AclAugment = Symbol("AclAugment"); +export const AclSelfEdit = Symbol("AclSelfEdit"); export const AclEdit = Symbol("AclEdit"); export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); @@ -102,7 +103,8 @@ const AclMap = new Map([ ["None", AclUnset], [SharingPermissions.None, AclPrivate], [SharingPermissions.View, AclReadonly], - [SharingPermissions.Add, AclAddonly], + [SharingPermissions.Augment, AclAugment], + [SharingPermissions.SelfEdit, AclSelfEdit], [SharingPermissions.Edit, AclEdit], [SharingPermissions.Admin, AclAdmin] ]); diff --git a/src/fields/util.ts b/src/fields/util.ts index 526e5af72..2bb6b45c2 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -14,6 +14,7 @@ import CursorField from "./CursorField"; import { List } from "./List"; import { SnappingManager } from "../client/util/SnappingManager"; import { computedFn } from "mobx-utils"; +import { RichTextField } from "./RichTextField"; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); @@ -77,7 +78,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number const fromServer = target[UpdatingFromServer]; const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Default) && !DocServer.Control.isReadOnly();// && !playgroundMode; + const writeToServer = + (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) && + !DocServer.Control.isReadOnly(); if (writeToDoc) { if (value === undefined) { @@ -157,9 +160,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) { */ export enum SharingPermissions { Admin = "Admin", - Edit = "Can Edit", - Add = "Can Augment", - View = "Can View", + Edit = "Edit", + SelfEdit = "Self Edit", + Augment = "Augment", + View = "View", None = "Not Shared" } @@ -176,7 +180,7 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { function getPropAcl(target: any, prop: string | symbol | number) { if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent - if (prop && DocServer.PlaygroundFields?.includes(prop.toString())) return AclEdit; // playground props are always editable + if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); } @@ -192,7 +196,8 @@ function getEffectiveAcl(target: any, user?: string): symbol { HierarchyMapping = HierarchyMapping || new Map([ [AclPrivate, 0], [AclReadonly, 1], - [AclAddonly, 2], + [AclAugment, 2], + [AclSelfEdit, 2.5], [AclEdit, 3], [AclAdmin, 4] ]); @@ -235,6 +240,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Not Shared", 0], ["Can View", 1], ["Can Augment", 2], + ["Self Edit", 2.5], ["Can Edit", 3], ["Admin", 4] ]); @@ -294,7 +300,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; const effectiveAcl = getPropAcl(target, prop); - if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true; + if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true; // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true; -- cgit v1.2.3-70-g09d2 From 81ae5175aebf7e14a781a73d4a7cbaec0b8f3e4a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 9 Aug 2021 14:26:14 -0400 Subject: fixed augment only text to resize correctly with autoheight. fixed acls to cache correctly for new documents. fixed links to be shared across users. fixed links to always default to public. --- src/client/DocServer.ts | 2 +- src/client/documents/Documents.ts | 8 ++- src/client/util/CurrentUserUtils.ts | 16 +++--- src/client/util/LinkManager.ts | 67 ++++++++++++++-------- src/client/util/SettingsManager.tsx | 3 +- src/client/util/SharingManager.tsx | 4 +- src/client/views/MainView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 20 ++++--- src/fields/util.ts | 4 +- 9 files changed, 79 insertions(+), 47 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 5712a1d64..f4e3a22cf 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,6 @@ import * as io from 'socket.io-client'; import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message"; -import { Opt, Doc, UpdatingFromServer } from '../fields/Doc'; +import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../fields/RefField'; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 93f0880a4..48886aa3b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,7 @@ import { action, runInAction } from "mobx"; import { basename, extname } from "path"; import { DateField } from "../../fields/DateField"; -import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing, updateCachedAcls } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { HtmlField } from "../../fields/HtmlField"; import { InkField } from "../../fields/InkField"; @@ -577,7 +577,7 @@ export namespace Docs { dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -588,13 +588,15 @@ export namespace Docs { viewProps.author = Doc.CurrentUserEmail; viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + viewProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); + updateCachedAcls(dataDoc); + updateCachedAcls(viewDoc); return viewDoc; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 14c43fb1c..34990e121 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; +import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -19,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; @@ -31,13 +33,10 @@ import { LinkManager } from "./LinkManager"; import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; -import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { SharingManager } from "./SharingManager"; -import { computedFn } from "mobx-utils"; import { ColorScheme } from "./SettingsManager"; -import { Colors } from "../views/global/globalEnums"; +import { SharingManager } from "./SharingManager"; +import { SnappingManager } from "./SnappingManager"; +import { UndoManager } from "./UndoManager"; export let resolvedPorts: { server: number, socket: number }; @@ -915,6 +914,7 @@ export class CurrentUserUtils { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); if (!linkDocs) { linkDocs = new Doc(linkDatabaseId, true); + (linkDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; (linkDocs as Doc).author = Doc.CurrentUserEmail; (linkDocs as Doc).data = new List([]); (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; @@ -1029,7 +1029,7 @@ export class CurrentUserUtils { doc.fontFamily = StrCast(doc.fontFamily, "Arial"); doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); - doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true); + doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 8abfd740c..933b98a8c 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,4 +1,4 @@ -import { observable, observe, action } from "mobx"; +import { observable, observe, action, reaction, computed } from "mobx"; import { computedFn } from "mobx-utils"; import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; @@ -26,36 +26,41 @@ type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => vo export class LinkManager { @observable static _instance: LinkManager; - @observable static userDocs: Doc[] = []; + @observable static userLinkDBs: Doc[] = []; public static currentLink: Opt; public static get Instance() { return LinkManager._instance; } + public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb); + static links: Doc[] = []; constructor() { LinkManager._instance = this; setTimeout(() => { - LinkManager.userDocs = [Doc.LinkDBDoc().data as Doc, ...SharingManager.Instance.users.map(user => user.linkDatabase)]; - const addLinkToDoc = action((link: Doc): any => { + LinkManager.userLinkDBs = []; + const addLinkToDoc = (link: Doc) => { const a1 = link?.anchor1; const a2 = link?.anchor2; - if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => addLinkToDoc(link))); - if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { - Doc.GetProto(a1)[DirectLinksSym].add(link); - Doc.GetProto(a2)[DirectLinksSym].add(link); - Doc.GetProto(link)[DirectLinksSym].add(link); - } - }); - const remLinkFromDoc = action((link: Doc): any => { + Promise.all([a1, a2]).then(action(() => { + if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { + Doc.GetProto(a1)[DirectLinksSym].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(link); + } + })); + } + const remLinkFromDoc = (link: Doc) => { const a1 = link?.anchor1; const a2 = link?.anchor2; - if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => remLinkFromDoc(link))); - if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { - Doc.GetProto(a1)[DirectLinksSym].delete(link); - Doc.GetProto(a2)[DirectLinksSym].delete(link); - Doc.GetProto(link)[DirectLinksSym].delete(link); - } - }); - const watchUserLinks = (userLinks: List) => { + Promise.all([a1, a2]).then(action(() => { + if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { + Doc.GetProto(a1)[DirectLinksSym].delete(link); + Doc.GetProto(a2)[DirectLinksSym].delete(link); + Doc.GetProto(link)[DirectLinksSym].delete(link); + } + })); + } + const watchUserLinkDB = (userLinkDBDoc: Doc) => { + LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields - observe(userLinks, change => { + observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes) switch (change.type as any) { case "splice": (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link))); @@ -64,13 +69,29 @@ export class LinkManager { case "update": //let oldValue = change.oldValue; } }, true); + observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link) + change => { + switch (change.type as any) { + case "update": + Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => { + const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length); + const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length); + + const added = newDocs?.filter(link => !(oldDocs || []).includes(link)); + const removed = oldDocs?.filter(link => !(newDocs || []).includes(link)); + added?.forEach((link: any) => addLinkToDoc(toRealField(link))); + removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); + }); + } + }, true); }; - observe(LinkManager.userDocs, change => { + observe(LinkManager.userLinkDBs, change => { switch (change.type as any) { - case "splice": (change as any).added.forEach(watchUserLinks); break; + case "splice": (change as any).added.forEach(watchUserLinkDB); break; case "update": //let oldValue = change.oldValue; } }, true); + LinkManager.addLinkDB(Doc.LinkDBDoc()); }); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 3987497b8..bd91db779 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -268,7 +268,8 @@ export class SettingsManager extends React.Component<{}> {

- Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} /> + Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
Default access private
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index a8972b988..6c4556250 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -23,6 +23,7 @@ import { GroupManager, UserOptions } from "./GroupManager"; import { GroupMemberView } from "./GroupMemberView"; import { SelectionManager } from "./SelectionManager"; import "./SharingManager.scss"; +import { LinkManager } from "./LinkManager"; export interface User { email: string; @@ -154,10 +155,11 @@ export class SharingManager extends React.Component<{}> { } }); return Promise.all(evaluating).then(() => { - runInAction(() => { + runInAction(async () => { for (const sharer of sharingDocs) { if (!this.users.find(user => user.user.email === sharer.user.email)) { this.users.push(sharer); + LinkManager.addLinkDB(sharer.linkDatabase); } } }); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2be3c3135..3d7446cbe 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -108,7 +108,7 @@ export class MainView extends React.Component { this._sidebarContent.proto = undefined; if (!MainView.Live) { DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", - "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", + "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f7bed2fa1..9bba15b28 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -431,6 +431,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.ProseRef = ele; this._dropDisposer?.(); ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc)); + // if (this.autoHeight) this.tryUpdateScrollHeight(); } @undoBatch @@ -1438,16 +1439,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } } - tryUpdateScrollHeight() { + tryUpdateScrollHeight = () => { if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const proseHeight = !this.ProseRef ? 0 : Array.from(this.ProseRef.children[0].children).reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; - if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { - setScrollHeight(); - } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; + if (children) { + const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { + setScrollHeight(); + } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } } } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 2bb6b45c2..439c4d333 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -141,7 +141,9 @@ export function denormalizeEmail(email: string) { export function inheritParentAcls(parent: Doc, child: Doc) { const dataDoc = parent[DataSym]; for (const key of Object.keys(dataDoc)) { - key.startsWith("acl") && distributeAcls(key, dataDoc[key], child); + // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. + const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key]; + key.startsWith("acl") && distributeAcls(key, permission, child); } } -- cgit v1.2.3-70-g09d2 From 136b900ed0b939c2a10b601470f764dcb50809ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 12 Aug 2021 17:07:41 -0400 Subject: fixed textboxes which were generally a line too short when opened in contentfitting view (e.g. stackingView or pivotView) --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9bba15b28..1058070f8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1444,12 +1444,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; if (children) { - const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); + setTimeout(() => { + proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + setScrollHeight(); + }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } } -- cgit v1.2.3-70-g09d2 From bd41980b6d8849c5ae875fbe5325f88373b3640b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 Aug 2021 11:58:21 -0400 Subject: fixed canEdit func in prosemirrorExampleTransfer to prevent error when hitting enter at end of uneditable doc. fixed scrollheight setting error in formattedtextbox caused by assigning null --- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- .../nodes/formattedText/ProsemirrorExampleTransfer.ts | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 1058070f8..b0b8ce75d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -535,7 +535,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); const min = Math.round(Date.now() / 1000 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() })); - setTimeout(() => this.updateHighlights()); + setTimeout(this.updateHighlights); } if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) { addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); @@ -882,7 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } }, ); - if (this._recording) setTimeout(() => this.recordDictation()); + if (this._recording) setTimeout(this.recordDictation); } var quickScroll: string | undefined = ""; this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), @@ -1453,7 +1453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setTimeout(() => { proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - setScrollHeight(); + scrollHeight && setScrollHeight(); }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 1f78b2204..eff400a98 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; @@ -72,13 +72,16 @@ export function buildKeymap>(schema: S, props: any, mapKey }; const canEdit = (state: any) => { - for (var i = state.selection.from; i < state.selection.to; i++) { - const node = state.doc.resolve(i); - if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && - mark.attrs.userid !== Doc.CurrentUserEmail) && - GetEffectiveAcl(props.Document) === AclAugment) { - return false; - } + switch (GetEffectiveAcl(props.Document)) { + case AclAugment: return false; + case AclSelfEdit: + for (var i = state.selection.from; i < state.selection.to; i++) { + const marks = state.doc.resolve(i)?.marks?.(); + if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) { + return false; + } + } + break; } return true; } -- cgit v1.2.3-70-g09d2 From cc89a0c16246afde8bb3d891a4ede492b6644b1f Mon Sep 17 00:00:00 2001 From: geireann Date: Wed, 18 Aug 2021 11:01:57 -0400 Subject: added search linking --- src/Utils.ts | 1 + src/client/views/MarqueeAnnotator.tsx | 2 ++ src/client/views/linking/LinkPopup.tsx | 5 ++++- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 + src/client/views/pdf/AnchorMenu.tsx | 12 +++++------- src/client/views/search/SearchBox.tsx | 14 +++++++++++--- src/client/views/topbar/TopBar.scss | 1 + 7 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 194c38a6f..f9ca8646f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -439,6 +439,7 @@ export function emptyFunction() { } export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); } + export type Without = Pick>; export type Predicate = (entry: [K, V]) => boolean; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index a3a3bce56..fa1a4cfea 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -68,6 +68,8 @@ export class MarqueeAnnotator extends React.Component { AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true)); AnchorMenu.Instance.Highlight = this.highlight; AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations); + AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor; + /** * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation. * It also initiates a Drag/Drop interaction to place the text annotation. diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 85fd91d9f..c8be9069c 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -11,9 +11,11 @@ import { SearchBox } from '../search/SearchBox'; import { DefaultStyleProvider } from '../StyleProvider'; import './LinkPopup.scss'; import React = require("react"); +import { Doc, Opt } from '../../../fields/Doc'; interface LinkPopupProps { showPopup: boolean; + linkFrom?: () => Doc | undefined; // groupType: string; // linkDoc: Doc; // docView: DocumentView; @@ -48,6 +50,7 @@ export class LinkPopup extends React.Component { render() { const popupVisibility = this.props.showPopup ? "block" : "none"; + const linkDoc = this.props.linkFrom ? this.props.linkFrom : undefined; return (
{/*
@@ -67,7 +70,7 @@ export class LinkPopup extends React.Component { { @observable public Highlighting: boolean = false; @observable public Status: "marquee" | "annotation" | "" = ""; + public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search + public OnClick: (e: PointerEvent) => void = unimplementedFunction; - public OnLink: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; public GetAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; @@ -67,15 +68,12 @@ export class AnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction(() => SelectionManager.Views(), selected => { - AnchorMenu.Instance.fadeOut(true) this._showLinkPopup = false; + console.log("unmount"); + AnchorMenu.Instance.fadeOut(true) }); } - componentWillUnmount() { - console.log("unmount"); - } - pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e: PointerEvent) => { this.StartDrag(e, this._commentCont.current!); @@ -159,7 +157,7 @@ export class AnchorMenu extends AntimodeMenu { , - + ] : [ {"Remove Link Anchor"}
}>
: -
+
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 092823603..9cc4b1f9a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -17,8 +17,8 @@ import { InkingStroke } from "../InkingStroke"; import { StyleProp } from "../StyleProvider"; import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; -import { FieldViewProps } from "./FieldView"; import React = require("react"); +import Color = require("color"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; @@ -164,6 +164,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this._contentView = r)} /> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 153176afc..6708a08ee 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,17 +1,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, OmitKeys, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; +import { StyleProp } from '../StyleProvider'; import "./ComparisonBox.scss"; -import { DocumentView } from './DocumentView'; +import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import React = require("react"); @@ -71,6 +72,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent, props: Opt, property: string): any => { + if (property === StyleProp.PointerEvents) return "none"; + return this.props.styleProvider?.(doc, props, property); + } + render() { const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%"; const clearButton = (which: string) => { @@ -84,6 +90,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent @@ -102,7 +111,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent +
{displayBox("after", 1, this.props.PanelWidth() - 3)}
{displayBox("before", 0, 0)} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bb259da3e..11fb5cdb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -84,6 +84,7 @@ export interface DocComponentView { reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. + isAnyChildContentActive?: () => boolean; // is any child content of the document active getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; @@ -182,7 +183,7 @@ export class DocumentViewInternal extends DocComponent; // needs to be accessed from DocumentView wrapper class + @observable _componentView: Opt; // needs to be accessed from DocumentView wrapper class private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -778,8 +779,14 @@ export class DocumentViewInternal extends DocComponent this.ContentScale; onClickFunc = () => this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; - setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false; + setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); + isContentActive = (outsideReaction?: boolean) => { + return CurrentUserUtils.SelectedTool !== InkTool.None || + this.props.Document.forceActive || + this.props.isSelected(outsideReaction) || + this._componentView?.isAnyChildContentActive?.() || + this.props.isContentActive() ? true : false; + } @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -794,7 +801,7 @@ export class DocumentViewInternal extends DocComponent; return
; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations) this._marqueeing = [e.clientX, e.clientY]; @@ -368,7 +368,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; const curPage = this.Document._curPage || 1; - return !this.isContentActive() ? (null) : + return !this.props.isContentActive() ? (null) :
[KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} - onPointerDown={e => e.stopPropagation()} style={{ display: this.isContentActive() ? "flex" : "none" }}> + onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> @@ -229,7 +229,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent
{this.props.Document.title} @@ -268,7 +268,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent {this.settingsPanel()}
; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index d4df30b48..484dec7e2 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -307,7 +307,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
: -
+
} placement="bottom"> -
+ {"playback"}
} key="play" placement="bottom"> +
, - {"timecode"}
} placement="bottom"> + {"timecode"}
} key="time" placement="bottom">
{formatTime(curTime)} {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}
, - {"view full screen"}
} placement="bottom"> + {"view full screen"}
} key="full" placement="bottom">
]; return
{[...(VideoBox._nativeControls ? [] : nonNativeControls), - {"snapshot current frame"}
} placement="bottom"> + {"snapshot current frame"}
} key="snap" placement="bottom">
, - {"show annotation timeline"}
} placement="bottom"> + {"show annotation timeline"}
} key="timeline" placement="bottom">
@@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this._clicking = false; - if (this.isContentActive()) { + if (this.props.isContentActive()) { const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100)); } @@ -438,7 +438,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent; setTimeout(action(() => this._clicking = false), 500); - }, this.isContentActive(), this.isContentActive()); + }, this.props.isContentActive(), this.props.isContentActive()); }); onResetDown = (e: React.PointerEvent) => { @@ -529,12 +529,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent @@ -546,7 +546,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -570,7 +570,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent (this.props.scaling?.() || 1) * this.heightPercent / 100; marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0]; - timelineDocFilter = () => ["_timelineLabel:true:x"]; + timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`]; render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad; @@ -592,7 +592,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -484,7 +484,7 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.urlContent}
; @@ -529,7 +529,6 @@ export class WebBox extends ViewBoxAnnotatableComponent; return ( -
+
{renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()} {this.annotationLayer}
@@ -588,10 +587,9 @@ export class WebBox extends ViewBoxAnnotatableComponent diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d1027dfd7..4b1d76d00 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if ((e.nativeEvent as any).formattedHandled) { console.log("handled"); } - if (!(e.nativeEvent as any).formattedHandled && this.isContentActive(true)) { + if (!(e.nativeEvent as any).formattedHandled && this.props.isContentActive(true)) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); @@ -1481,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; - return (!annotated && !this.isContentActive()) ? (null) :
: = 10 ? "-selected" : ""; return (
this.isContentActive() && e.stopPropagation()} + onWheel={e => this.props.isContentActive() && e.stopPropagation()} style={{ transform: this.props.dontScale ? undefined : `scale(${scale})`, transformOrigin: this.props.dontScale ? undefined : "top left", diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 75e3f81fb..42bec38da 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -69,7 +69,6 @@ export class AnchorMenu extends AntimodeMenu { this._disposer = reaction(() => SelectionManager.Views(), selected => { this._showLinkPopup = false; - console.log("unmount"); AnchorMenu.Instance.fadeOut(true) }); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 734d9127c..bc35d2126 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -512,7 +512,6 @@ export class PDFViewer extends React.Component { const renderAnnotations = (docFilters?: () => string[]) => { transform: `scale(${this._zoomed})` }}> {renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 17f41fac8..b09ff93d0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1092,6 +1092,9 @@ export namespace Doc { const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1); return isTransparent(StrCast(doc[key])); } + if (typeof value === "string") { + value = value.replace(`,${Utils.noRecursionHack}`, ""); + } const fieldVal = doc[key]; if (Cast(fieldVal, listSpec("string"), []).length) { const vals = Cast(fieldVal, listSpec("string"), []); -- cgit v1.2.3-70-g09d2 From c1fb227c364af5defb99adc793e850d681caa36a Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 26 Aug 2021 06:34:54 -0400 Subject: really close --- src/client/util/CurrentUserUtils.ts | 40 +- src/client/views/MainView.tsx | 2 + .../collectionLinearView/CollectionLinearView.scss | 6 +- src/client/views/nodes/button/FontIconBox.tsx | 160 ++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- .../views/nodes/formattedText/RichTextMenu.tsx | 494 ++++----------------- src/server/index.ts | 2 +- 7 files changed, 227 insertions(+), 479 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cd2803373..8f75947d1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -51,6 +51,7 @@ interface Button { numBtnMax?: number; switchToggle?: boolean; script?: string; + checkResult?: string; width?: number; list?: string[]; ignoreClick?: boolean; @@ -958,29 +959,28 @@ export class CurrentUserUtils { }, { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize' }, { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor' }, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, - { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' }, + { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' }, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, - { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script: 'setAlignment("left")' }, - { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script: 'setAlignment("center")' }, - { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script: 'setAlignment("right")' }, + { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left")', checkResult: 'setAlignment("left", true)' }, + { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center")', checkResult: 'setAlignment("center", true)' }, + { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right")', checkResult: 'setAlignment("right", true)' }, ]; return tools; } static inkTools(doc: Doc) { const tools: Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', script: 'setActiveInkTool("pen" , true)' }, - { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script: 'setActiveInkTool("highlighter", true)' }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' }, - { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' }, + { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' }, + { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' }, { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth' }, { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script: 'setStrokeColor' }, - ]; return tools; } @@ -995,8 +995,8 @@ export class CurrentUserUtils { width: 50, switchToggle: true, icon: "eye", - ignoreClick: true, - script: 'toggleSchemaShow' + click: 'toggleSchemaShow()', + checkResult: 'toggleSchemaShow(true)' }, ]; return tools; @@ -1029,7 +1029,7 @@ export class CurrentUserUtils { title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor", hidden: 'selectedDocumentType()' }, // Only when a document is selected - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script: 'toggleOverlay', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available @@ -1042,7 +1042,7 @@ export class CurrentUserUtils { static async setupContextMenuButtons(doc: Doc) { const docList: Doc[] = []; - (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => { + (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => { const menuDocList: Doc[] = []; if (subMenu) { // default is textTools @@ -1064,7 +1064,7 @@ export class CurrentUserUtils { tools = CurrentUserUtils.textTools(doc); break; } - tools.map(({ title, toolTip, icon, btnType, numBtnType, click, script, width, list, ignoreClick, switchToggle }) => { + tools.map(({ title, toolTip, icon, btnType, numBtnType, click, script, width, list, ignoreClick, switchToggle, checkResult }) => { menuDocList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 25, _nativeHeight: 25, @@ -1085,7 +1085,7 @@ export class CurrentUserUtils { title, switchToggle, color: Colors.WHITE, - backgroundColor: "transparent", + backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent", _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined @@ -1098,7 +1098,7 @@ export class CurrentUserUtils { linearViewExpandable: true, icon: title, _height: 30, - backgroundColor: "transparent", + backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent", linearViewIsExpanded: expanded ? ComputedField.MakeFunction(expanded) as any : undefined, hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, }, menuDocList)); @@ -1276,6 +1276,7 @@ export class CurrentUserUtils { doc._raiseWhenDragged = true; doc._showLabel = false; doc._showMenuLabel = true; + doc.textAlign = StrCast(doc.textAlign, "left"); doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0, 0)"); doc.activeInkWidth = NumCast(doc.activeInkWidth, 1); doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); @@ -1592,7 +1593,6 @@ Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colTyp const parentDoc: Doc = Cast(selected.context, Doc, null); selected = parentDoc; } - console.log(selected, docType, colType); if (selected && docType && selected.type === docType) return false; else if (selected && colType && selected.viewType === colType) return false; else if (selected && !colType && !docType) return false; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index dcdf6b9c3..52dbd451c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -62,6 +62,7 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -596,6 +597,7 @@ export class MainView extends React.Component { + {this.snapLines}
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index 24c9bc9cb..f249eb1f5 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -98,11 +98,7 @@ transition: transform 0.2s; align-items: center; justify-content: center; - transition: 0.1s; - - &:hover { - transform: scale(1.05); - } + transition: 0.15s; } >input { diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 53cd017b0..e09e15a60 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -3,7 +3,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { StringifyOptions } from 'querystring'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; import { Doc, StrListCast } from '../../../../fields/Doc'; @@ -11,6 +10,7 @@ import { InkTool } from '../../../../fields/InkField'; import { createSchema, makeInterface } from '../../../../fields/Schema'; import { ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types'; +import { WebField } from '../../../../fields/URLField'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Scripting } from "../../../util/Scripting"; import { SelectionManager } from '../../../util/SelectionManager'; @@ -20,14 +20,13 @@ import { CollectionViewType } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; +import { GestureOverlay } from '../../GestureOverlay'; import { Colors } from '../../global/globalEnums'; +import { SetActiveInkColor } from '../../InkingStroke'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; -import { RichTextMenu } from '../formattedText/RichTextMenu'; import './FontIconBox.scss'; -import { SetActiveInkWidth, SetActiveInkColor } from '../../InkingStroke'; -import { GestureOverlay } from '../../GestureOverlay'; -import { WebField } from '../../../../fields/URLField'; +import { RichTextMenu } from '../formattedText/RichTextMenu'; const FontIconSchema = createSchema({ icon: "string", }); @@ -241,7 +240,7 @@ export class FontIconBox extends DocComponent(Fon let text: string | undefined; let dropdown = true; let icon: IconProp = "caret-down"; - + let noneSelected:boolean = false; if (script === 'setView') { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; @@ -253,6 +252,7 @@ export class FontIconBox extends DocComponent(Fon icon = Doc.toIcon(selected.Document); } else { dropdown = false; + noneSelected = true; text = "None selected"; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; @@ -298,7 +298,7 @@ export class FontIconBox extends DocComponent(Fon
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> - {dropdown ? (null) : } + {dropdown || noneSelected ? (null) : }
{text && text[0].toUpperCase() + text.slice(1)}
@@ -395,22 +395,6 @@ export class FontIconBox extends DocComponent(Fon // Determine the type of toggle button const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); - // Script for running the toggle - const script: string = StrCast(this.rootDoc.script); - - // Script for checking the outcome of the toggle - let checkScript:string; - if (StrCast(this.rootDoc.script).includes("(")){ - checkScript = StrCast(this.rootDoc.script) - - } else { - checkScript = StrCast(this.rootDoc.script) + "(true)"; - } - - // Function to run the script - const runScript = () => ScriptField.MakeScript(script + "()")?.script.run(); - const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result; - // Colors const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); @@ -423,13 +407,10 @@ export class FontIconBox extends DocComponent(Fon if (switchToggle) { return ( -
+
@@ -438,10 +419,8 @@ export class FontIconBox extends DocComponent(Fon } else { return (
- + style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}> + {label}
); @@ -595,7 +574,8 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult && selected) { - return NumCast(selected.Document.z) === 1; + if(NumCast(selected.Document.z) === 1) return Colors.MEDIUM_BLUE; + else return "transparent"; } selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); }); @@ -606,43 +586,109 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { * toggleBold * toggleUnderline * toggleItalic + * setAlignment + * toggleBold + * toggleItalic + * toggleUnderline **/ +Scripting.addGlobal(function toggleBold(){ + const editorView = RichTextMenu.Instance.TextView?.EditorView; +}); + // toggle: Set overlay status of selected document -Scripting.addGlobal(function setFont(font: string) { +Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) { SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font); + const editorView = RichTextMenu.Instance.TextView?.EditorView; + editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView); Doc.UserDoc()._fontFamily = font; return Doc.UserDoc()._fontFamily; }); +Scripting.addGlobal(function getActiveTextInfo(info:"family" | "size" | "color" | "highlight"){ + const editorView = RichTextMenu.Instance.TextView?.EditorView; + const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); + switch(info){ + case "family": return style?.activeColors[0]; + case "size": return style?.activeSizes[0]; + case "color": return style?.activeColors[0]; + case "highlight": return style?.activeHighlights[0]; + } +}) + +Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + let active:string; + if (editorView){ + active = editorView?.state && RichTextMenu.Instance.getActiveAlignment(); + } else { + active = StrCast(Doc.UserDoc().textAlign); + } + if (active === align) return Colors.MEDIUM_BLUE; + else return "transparent"; + } + SelectionManager.Views().map(dv => dv.props.Document.textAlign = align); + switch(align){ + case "left": + editorView?.state && RichTextMenu.Instance.alignLeft( editorView, editorView.dispatch); + break; + case "center": + editorView?.state && RichTextMenu.Instance.alignCenter(editorView, editorView.dispatch); + break; + case "right": + editorView?.state && RichTextMenu.Instance.alignRight(editorView, editorView.dispatch); + break; + default: + break; + } + Doc.UserDoc().textAlign = align; +}); + // toggle: Set overlay status of selected document Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) { - const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined; + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + const editorView = RichTextMenu.Instance.TextView?.EditorView; + if (checkResult) { if (selected) { - console.log("[Font color] (selected): " + StrCast(selected._fontColor)); - return selected._fontColor; + console.log("[Font color] (selected): " + StrCast(selected.rootDoc._fontColor)); + return selected.rootDoc._fontColor; } else { console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor)); return Doc.UserDoc()._fontColor; } } - if (selected) selected._fontColor = color; + + if (selected) { + selected.rootDoc._fontColor = color; + if (color) { + editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); + } + } Doc.UserDoc()._fontColor = color; }); -// // toggle: Set overlay status of selected document -// Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) { -// const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; -// if (checkResult) { -// if (selected) { -// return selected.Document._fontColor; -// } else { -// return Doc.UserDoc()._fontColor; -// } -// } -// Doc.UserDoc()._fontColor = color; -// }); +// toggle: Set overlay status of selected document +Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + const editorView = RichTextMenu.Instance.TextView?.EditorView; + + if (checkResult) { + if (selected) { + return selected.Document._fontHighlight; + } else { + return Doc.UserDoc()._fontHighlight; + } + } + if (selected) { + selected.rootDoc._fontColor = color; + if (color) { + editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch); + } + } + Doc.UserDoc()._fontHighlight = color; +}); @@ -652,12 +698,15 @@ Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) { const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10); return size; } + const editorView = RichTextMenu.Instance.TextView?.EditorView; + editorView?.state && RichTextMenu.Instance.setFontSize(Number(size), editorView); Doc.UserDoc()._fontSize = size + "px"; }); Scripting.addGlobal(function toggleBold(checkResult?: boolean) { if (checkResult) { - return Doc.UserDoc().bold; + if (Doc.UserDoc().bold) return Colors.MEDIUM_BLUE; + else return "transparent"; } SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic); Doc.UserDoc().bold = !Doc.UserDoc().bold; @@ -666,7 +715,8 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) { Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) { if (checkResult) { - return Doc.UserDoc().underline; + if (Doc.UserDoc().underline) return Colors.MEDIUM_BLUE; + else return "transparent"; } SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic); Doc.UserDoc().underline = !Doc.UserDoc().underline; @@ -675,7 +725,8 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) { Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { if (checkResult) { - return Doc.UserDoc().italic; + if (Doc.UserDoc().italic) return Colors.MEDIUM_BLUE; + else return "transparent"; } SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic); Doc.UserDoc().italic = !Doc.UserDoc().italic; @@ -693,7 +744,8 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) { if (checkResult) { - return Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool; + if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool) return Colors.MEDIUM_BLUE; + else return "transparent"; } if (tool === "circle") { Doc.UserDoc().activeInkTool = "pen"; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d1027dfd7..f4053d69f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -214,7 +214,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setupAnchorMenu = () => { AnchorMenu.Instance.Status = "marquee"; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { - this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); + this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch); return undefined; }); AnchorMenu.Instance.onMakeAnchor = this.getAnchor; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index c081b8e26..e32eccb62 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -37,11 +37,6 @@ export class RichTextMenu extends AntimodeMenu { public editorProps: FieldViewProps & FormattedTextBoxProps | undefined; public _brushMap: Map> = new Map(); - private fontSizeOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; - private fontFamilyOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; - private listTypeOptions: { node: NodeType | any | null, title: string, label: string, command: any, style?: {} }[]; - private fontColors: (string | undefined)[]; - private highlightColors: (string | undefined)[]; @observable private collapsed: boolean = false; @observable private boldActive: boolean = false; @@ -76,70 +71,6 @@ export class RichTextMenu extends AntimodeMenu { this._canFade = false; //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); runInAction(() => this.Pinned = true); - - this.fontSizeOptions = [ - { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize }, - { mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true }, - { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option - ]; - - this.fontFamilyOptions = [ - { mark: schema.marks.pFontFamily.create({ family: "Times New Roman" }), title: "Set font family", label: "Times New Roman", command: this.changeFontFamily, style: { fontFamily: "Times New Roman" } }, - { mark: schema.marks.pFontFamily.create({ family: "Arial" }), title: "Set font family", label: "Arial", command: this.changeFontFamily, style: { fontFamily: "Arial" } }, - { mark: schema.marks.pFontFamily.create({ family: "Georgia" }), title: "Set font family", label: "Georgia", command: this.changeFontFamily, style: { fontFamily: "Georgia" } }, - { mark: schema.marks.pFontFamily.create({ family: "Comic Sans MS" }), title: "Set font family", label: "Comic Sans MS", command: this.changeFontFamily, style: { fontFamily: "Comic Sans MS" } }, - { mark: schema.marks.pFontFamily.create({ family: "Tahoma" }), title: "Set font family", label: "Tahoma", command: this.changeFontFamily, style: { fontFamily: "Tahoma" } }, - { mark: schema.marks.pFontFamily.create({ family: "Impact" }), title: "Set font family", label: "Impact", command: this.changeFontFamily, style: { fontFamily: "Impact" } }, - { mark: schema.marks.pFontFamily.create({ family: "Crimson Text" }), title: "Set font family", label: "Crimson Text", command: this.changeFontFamily, style: { fontFamily: "Crimson Text" } }, - { mark: null, title: "", label: "various", command: unimplementedFunction, hidden: true }, - // { mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true }, - ]; - - this.listTypeOptions = [ - { node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "A.1", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "" }), title: "Set list type", label: "", command: this.changeListType }, - //{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType }, - ]; - - this.fontColors = [ - DarkPastelSchemaPalette.get("pink2"), - DarkPastelSchemaPalette.get("purple4"), - DarkPastelSchemaPalette.get("bluegreen1"), - DarkPastelSchemaPalette.get("yellow4"), - DarkPastelSchemaPalette.get("red2"), - DarkPastelSchemaPalette.get("bluegreen7"), - DarkPastelSchemaPalette.get("bluegreen5"), - DarkPastelSchemaPalette.get("orange1"), - "#757472", - "#000" - ]; - - this.highlightColors = [ - PastelSchemaPalette.get("pink2"), - PastelSchemaPalette.get("purple4"), - PastelSchemaPalette.get("bluegreen1"), - PastelSchemaPalette.get("yellow4"), - PastelSchemaPalette.get("red2"), - PastelSchemaPalette.get("bluegreen7"), - PastelSchemaPalette.get("bluegreen5"), - PastelSchemaPalette.get("orange1"), - "white", - "transparent" - ]; } componentDidMount() { @@ -348,102 +279,38 @@ export class RichTextMenu extends AntimodeMenu { }); } - createButton(faIcon: string, title: string, isActive: boolean = false, command?: any, onclick?: any) { - const self = this; - function onClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - 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()); - } - - return ( - {title}
} key={title} placement="bottom"> - - - ); - } - - createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => void): JSX.Element { - const items = options.map(({ title, label, hidden, style }) => { - if (hidden) { - return ; - } - return ; - }); - - const self = this; - function onChange(e: React.ChangeEvent) { - e.stopPropagation(); - e.preventDefault(); - self.TextView?.endUndoTypingBatch(); - UndoManager.RunInBatch(() => { - options.forEach(({ label, mark, command }) => { - if (e.target.value === label && mark) { - if (!self.TextView?.props.isSelected(true)) { - switch (mark.type) { - case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break; - case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break; - } - } - else self.view && mark && command(mark, self.view); - } - }); - }, "text mark dropdown"); - } - - return {key}
} placement="bottom"> - - ; - } - - createNodesDropdown(activeMap: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => {}): JSX.Element { - const activeOption = activeMap === "bullet" ? ":" : activeMap === "decimal" ? "1.1" : activeMap === "multi" ? "A.1" : ""; - const items = options.map(({ title, label, hidden, style }) => { - if (hidden) { - return ; - } - return ; - }); - const self = this; - function onChange(val: string) { - self.TextView.endUndoTypingBatch(); - options.forEach(({ label, node, command }) => { - if (val === label && node) { - if (self.TextView.props.isSelected(true)) { - UndoManager.RunInBatch(() => self.view && node && command(node), "nodes dropdown"); - setter(val); - } - } - }); - } - - return {key}
} placement="bottom"> - - ; - } - - changeFontSize = (mark: Mark, view: EditorView) => { - const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }); + setFontSize = (size:number, view: EditorView) => { + const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size }); this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); view.focus(); this.updateMenu(view, undefined, this.props); } - changeFontFamily = (mark: Mark, view: EditorView) => { - const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }); + setFontFamily = (family:string, view: EditorView) => { + const fmark = view.state.schema.marks.pFontFamily.create({ family: family }); this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); view.focus(); this.updateMenu(view, undefined, this.props); } + setHighlight(color: String, view: EditorView, dispatch: any) { + const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color }); + if (view.state.selection.empty) return false; + view.focus(); + this.setMark(highlightMark, view.state, dispatch, false); + } + + setColor(color: String, view: EditorView, dispatch: any) { + const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color }); + if (view.state.selection.empty) { + dispatch(view.state.tr.addStoredMark(colorMark)); + return false; + } + this.setMark(colorMark, view.state, dispatch, true); + view.focus(); + } + // TODO: remove doesn't work //remove all node type and apply the passed-in one to the selected text changeListType = (nodeType: Node | undefined) => { @@ -491,23 +358,24 @@ export 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.TextView.props.isSelected(true) && this.alignParagraphs(state, "center", dispatch); + alignCenter = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "center", dispatch); } - alignLeft = (state: EditorState, dispatch: any) => { - return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "left", dispatch); + alignLeft = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "left", dispatch); } - alignRight = (state: EditorState, dispatch: any) => { - return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "right", dispatch); + alignRight = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "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) => { + alignParagraphs(view: EditorView, align: "left" | "right" | "center", dispatch: any) { + var tr = view.state.tr; + view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) { tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, align }, node.marks); return false; } + view.focus(); return true; }); dispatch?.(tr); @@ -598,47 +466,6 @@ export class RichTextMenu extends AntimodeMenu { } _brushNameRef = React.createRef(); - createBrushButton() { - const self = this; - const onBrushClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - self.TextView.endUndoTypingBatch(); - UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush"); - }; - - let label = "Stored marks: "; - if (this.brushMarks && this.brushMarks.size > 0) { - this.brushMarks.forEach((mark: Mark) => { - const markType = mark.type; - label += markType.name; - label += ", "; - }); - label = label.substring(0, label.length - 2); - } else { - label = "No marks are currently stored"; - } - - //onPointerDown={onBrushClick} - - const button = style brush
} placement="bottom"> - - ; - - const dropdownContent = -
-

{label}

- - -
; - - return ( - - ); - } - @action clearBrush() { RichTextMenu.Instance.brushMarks = new Set(); @@ -667,123 +494,14 @@ export 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 TextViewFieldKey() { return this.TextView?.props.fieldKey; } - createColorButton() { - const self = this; - function onColorClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - self.TextView.endUndoTypingBatch(); - if (self.view) { - UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.view.focus(); - self.updateMenu(self.view, undefined, self.props); - } - } - function changeColor(e: React.PointerEvent, color: string) { - e.preventDefault(); - e.stopPropagation(); - self.setActiveColor(color); - self.TextView.endUndoTypingBatch(); - if (self.view) { - UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.view.focus(); - self.updateMenu(self.view, undefined, self.props); - } - } - - // onPointerDown={onColorClick} - const button = set font color
} placement="bottom"> - - ; + - const dropdownContent = -
-

Change font color:

-
- {this.fontColors.map(color => { - if (color) { - return this.activeFontColor === color ? - : - ; - } - })} -
-
; - - return ( - - ); - } - - public insertColor(color: String, state: EditorState, dispatch: any) { - const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color }); - if (state.selection.empty) { - dispatch(state.tr.addStoredMark(colorMark)); - return false; - } - this.setMark(colorMark, state, dispatch, true); - } - - @action toggleHighlightDropdown() { this.showHighlightDropdown = !this.showHighlightDropdown; } @action setActiveHighlight(color: string) { this.activeHighlightColor = color; } - createHighlighterButton() { - const self = this; - function onHighlightClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - 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.TextView.endUndoTypingBatch(); - UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highlighter"); - } - - //onPointerDown={onHighlightClick} - const button = set highlight color
} placement="bottom"> - - ; - - const dropdownContent = -
-

Change highlight color:

-
- {this.highlightColors.map(color => { - if (color) { - return this.activeHighlightColor === color ? - : - ; - } - })} -
-
; - - return ( - - ); - } - insertHighlight(color: String, state: EditorState, dispatch: any) { - if (state.selection.empty) return false; - toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch); - } - - @action toggleLinkDropdown() { this.showLinkDropdown = !this.showLinkDropdown; } @action setCurrentLink(link: string) { this.currentLink = link; } createLinkButton() { @@ -922,93 +640,73 @@ export class RichTextMenu extends AntimodeMenu { return ref_node; } - @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } - @action onPointerLeave(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } - - @action - toggleMenuPin = (e: React.MouseEvent) => { - Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned; - if (!this.Pinned) { - this.fadeOut(true); - } - } - - @action - protected toggleCollapse = (e: React.MouseEvent) => { - this.collapsed = !this.collapsed; - setTimeout(() => { - const x = Math.min(this._left, window.innerWidth - RichTextMenu.Instance.width); - RichTextMenu.Instance.jumpTo(x, this._top, true); - }, 0); - } - render() { - TraceMobx(); - const row1 =
{[ - //!this.collapsed ? this.getDragger() : (null), - // !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.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("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), - this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), - this.createButton("align-right", "Align Right", this.activeAlignment === "right", 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 =
- {this.collapsed ? this.getDragger() : (null)} -
-
- {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { - this.activeFontSize = val; - SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); - })), - this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { - this.activeFontFamily = val; - SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); - })), -
, - this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), - 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) - ]} -
- {/*
- {
- -
} - -
*/} -
; + // TraceMobx(); + // const row1 =
{[ + // //!this.collapsed ? this.getDragger() : (null), + // // !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.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("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), + // this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), + // this.createButton("align-right", "Align Right", this.activeAlignment === "right", 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 =
+ // {this.collapsed ? this.getDragger() : (null)} + //
+ //
+ // {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { + // this.activeFontSize = val; + // SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); + // })), + // this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { + // this.activeFontFamily = val; + // SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); + // })), + //
, + // this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), + // 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) + // ]} + //
+ // {/*
+ // {
+ // + //
} + // + //
*/} + //
; return ( -
- {this.getElementWithRows([row1, row2], 2, false)} +
+ {/* {this.getElementWithRows([row1, row2], 2, false)} */}
); } diff --git a/src/server/index.ts b/src/server/index.ts index 9687c3b23..f8c32103b 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -16,6 +16,7 @@ import UserManager from './ApiManagers/UserManager'; import UtilManager from './ApiManagers/UtilManager'; import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader'; import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils"; +import { DashSessionAgent } from "./DashSession/DashSessionAgent"; import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent"; import { DashUploadUtils } from './DashUploadUtils'; import { Database } from './database'; @@ -23,7 +24,6 @@ import { Logger } from "./ProcessFactory"; import RouteManager, { Method, PublicHandler } from './RouteManager'; import RouteSubscriber from './RouteSubscriber'; import initializeServer, { resolvedPorts } from './server_Initialization'; -import { DashSessionAgent } from "./DashSession/DashSessionAgent"; export const AdminPriviliges: Map = new Map(); export const onWindows = process.platform === "win32"; -- cgit v1.2.3-70-g09d2 From f915c08c71d1384add30db49e4c74461d3cf0dcd Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 30 Aug 2021 16:39:26 -0400 Subject: reverted textboxes to not flicker when they have bullets --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index a9beb67de..f45b9de7a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1451,11 +1451,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); - setTimeout(() => { - proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - scrollHeight && setScrollHeight(); - }, 10); + // setTimeout(() => { + // proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + // scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + // scrollHeight && setScrollHeight(); + // }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } } -- cgit v1.2.3-70-g09d2 From 12d8267533d9b646247914e965b3cf7c32019e4b Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 2 Sep 2021 04:45:01 -0400 Subject: Many updates - Fixed image uploads so it clones rather than makes an alias - Updated context menu - Removed tools panel from novice mode in favour of ":" menu - Added explainers to menus in novice mode - Re-added the trails button - Changed UI for text, PDF & websites so that it is more consistent --- src/client/documents/Documents.ts | 52 +++++---- src/client/util/CurrentUserUtils.ts | 93 ++++++++-------- src/client/views/ContextMenu.scss | 2 +- src/client/views/ContextMenuItem.tsx | 4 +- src/client/views/MainViewModal.scss | 3 +- src/client/views/PropertiesButtons.scss | 41 +++++-- src/client/views/PropertiesButtons.tsx | 53 +++++---- src/client/views/SidebarAnnos.tsx | 3 +- src/client/views/StyleProvider.tsx | 4 +- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionStackingView.scss | 24 +++++ .../views/collections/CollectionStackingView.tsx | 119 +++++++++++++++------ .../views/collections/CollectionTreeView.tsx | 95 ++++++++++++---- src/client/views/collections/TabDocView.tsx | 3 +- src/client/views/collections/TreeView.tsx | 5 +- .../collectionLinearView/CollectionLinearView.scss | 6 +- .../collectionSchema/CollectionSchemaHeaders.tsx | 2 +- .../collectionSchema/CollectionSchemaView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/PDFBox.scss | 16 ++- src/client/views/nodes/PDFBox.tsx | 13 ++- src/client/views/nodes/WebBox.scss | 9 +- src/client/views/nodes/WebBox.tsx | 13 ++- src/client/views/nodes/button/FontIconBox.scss | 29 ++++- src/client/views/nodes/button/FontIconBox.tsx | 15 +-- .../nodes/formattedText/FormattedTextBox.scss | 21 +++- .../views/nodes/formattedText/FormattedTextBox.tsx | 16 ++- src/client/views/search/SearchBox.tsx | 2 +- src/client/views/topbar/TopBar.scss | 4 +- src/client/views/topbar/TopBar.tsx | 3 +- 31 files changed, 460 insertions(+), 202 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c4a713dc6..bd247bd01 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -59,6 +59,7 @@ import { WebBox } from "../views/nodes/WebBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -272,7 +273,12 @@ export class DocumentOptions { treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. - treeViewShowClearButton?: boolean; // whether a clear button should be displayed + + // Action Button + buttonMenu?: boolean; // whether a action button should be displayed + buttonMenuDoc?: Doc; + explainer?:string; + treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view _treeViewOpen?: boolean; // whether this document is expanded in a tree view (note: need _ and regular versions since this can be specified for both proto and layout docs) treeViewOpen?: boolean; // whether this document is expanded in a tree view @@ -1165,7 +1171,7 @@ export namespace DocUtils { export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void { !simpleMenu && ContextMenu.Instance.addItem({ - description: "Add Note ...", + description: "Quick Notes", subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({ description: ":" + StrCast(note.title), event: undoBatch((args: { x: number, y: number }) => { @@ -1178,12 +1184,12 @@ export namespace DocUtils { textDoc[textDoc.layoutKey] = note; docTextAdder(textDoc); }), - icon: "eye" + icon: StrCast(note.icon) as IconProp })) as ContextMenuProps[], - icon: "eye" + icon: "sticky-note" }); - ContextMenu.Instance.addItem({ - description: ":=math", event: () => { + const math:ContextMenuProps = ({ + description: ":Math", event: () => { const created = Docs.Create.EquationDocument(); if (created) { created.author = Doc.CurrentUserEmail; @@ -1194,25 +1200,27 @@ export namespace DocUtils { EquationBox.SelectOnLoad = created[Id]; docAdder?.(created); } - }, icon: "compress-arrows-alt" + }, icon: "calculator" }); + const documentList:ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ + description: ":" + StrCast(dragDoc.title), + event: undoBatch((args: { x: number, y: number }) => { + const newDoc = Doc.copyDragFactory(dragDoc); + if (newDoc) { + newDoc.author = Doc.CurrentUserEmail; + newDoc.x = x; + newDoc.y = y; + if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; + docAdder?.(newDoc); + } + }), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + documentList.push(math) ContextMenu.Instance.addItem({ description: "Create document", - subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ - description: ":" + StrCast(dragDoc.title), - event: undoBatch((args: { x: number, y: number }) => { - const newDoc = Doc.copyDragFactory(dragDoc); - if (newDoc) { - newDoc.author = Doc.CurrentUserEmail; - newDoc.x = x; - newDoc.y = y; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; - docAdder?.(newDoc); - } - }), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[], - icon: "eye" + subitems: documentList, + icon: "file" }); }// applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f12ac02f3..f6ec74482 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -84,7 +84,7 @@ export class CurrentUserUtils { title: "NEW MOBILE BUTTON", onClick: undefined, }, - [this.ficon({ + [this.createToolButton({ ignoreClick: true, icon: "mobile", btnType: ButtonType.ToolButton, @@ -92,7 +92,7 @@ export class CurrentUserUtils { }), 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({ + doc["template-mobile-button"] = CurrentUserUtils.createToolButton({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ToolButton, }); @@ -107,7 +107,7 @@ export class CurrentUserUtils { { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } ); slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - doc["template-button-slides"] = CurrentUserUtils.ficon({ + doc["template-button-slides"] = CurrentUserUtils.createToolButton({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card", btnType: ButtonType.ToolButton @@ -154,7 +154,7 @@ export class CurrentUserUtils { }; linkTemplate.header = new RichTextField(JSON.stringify(rtf2), ""); - doc["template-button-link"] = CurrentUserUtils.ficon({ + doc["template-button-link"] = CurrentUserUtils.createToolButton({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true, btnType: ButtonType.ToolButton @@ -186,7 +186,7 @@ export class CurrentUserUtils { const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); box.isTemplateDoc = makeTemplate(box, true, "switch"); - doc["template-button-switch"] = CurrentUserUtils.ficon({ + doc["template-button-switch"] = CurrentUserUtils.createToolButton({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, btnType: ButtonType.ToolButton @@ -236,7 +236,7 @@ export class CurrentUserUtils { short.title = "A Short Description"; long.title = "Long Description"; - doc["template-button-detail"] = CurrentUserUtils.ficon({ + doc["template-button-detail"] = CurrentUserUtils.createToolButton({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(detailView) as any as Doc, title: "detailView", @@ -275,7 +275,7 @@ export class CurrentUserUtils { static setupNoteTemplates(doc: Doc) { if (doc["template-note-Note"] === undefined) { const noteView = Docs.Create.TextDocument("", { - title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true, + title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true, icon: "sticky-note", _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Note"); @@ -283,7 +283,7 @@ export class CurrentUserUtils { } if (doc["template-note-Idea"] === undefined) { const noteView = Docs.Create.TextDocument("", { - title: "text", backgroundColor: "pink", system: true, + title: "text", backgroundColor: "pink", system: true, icon: "lightbulb", _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea"); @@ -291,7 +291,7 @@ export class CurrentUserUtils { } if (doc["template-note-Topic"] === undefined) { const noteView = Docs.Create.TextDocument("", { - title: "text", backgroundColor: "lightblue", system: true, + title: "text", backgroundColor: "lightblue", system: true, icon: "book-open", _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic"); @@ -467,7 +467,7 @@ export class CurrentUserUtils { ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyComparison === undefined) { - doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "comparison box", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.emptyScript === undefined) { doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List(["system"]) }); @@ -512,7 +512,7 @@ export class CurrentUserUtils { { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc }, { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc }, { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, - { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc, noviceMode: true }, + { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc }, { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, @@ -561,7 +561,7 @@ export class CurrentUserUtils { if (dragCreatorSet === undefined) { doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, - _forceActive: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true })); } else { @@ -574,29 +574,31 @@ export class CurrentUserUtils { return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, - { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' }, - { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, - { title: "Import", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, + { title: "File Manager", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' }, + { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, + { title: "Uploads", target: Cast(doc.myUploadDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, - { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' }, + { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, ]; } static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) => + const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments, hidden }) => Docs.Create.FontIconDocument({ icon, btnType: ButtonType.MenuButton, _stayInCollection: true, _hideContextMenu: true, + _chromeHidden: true, system: true, dontUndo: true, title, target, + hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), _width: 60, @@ -611,8 +613,6 @@ export class CurrentUserUtils { this.searchBtn = menuBtn; } }); - // hack -- last button is assumed to be the userDoc - menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()"); menuBtns.forEach(menuBtn => { if (menuBtn.title === "Search") { @@ -684,7 +684,7 @@ export class CurrentUserUtils { onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, backgroundColor: data.backgroundColor, system: true }, - [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) + [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) ); } @@ -820,6 +820,7 @@ export class CurrentUserUtils { const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); + (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "share", "times"]); // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); } @@ -860,18 +861,21 @@ export class CurrentUserUtils { } static setupRecentlyClosedDocs(doc: Doc) { - // setup Recently Closed library item if (doc.myRecentlyClosedDocs === undefined) { - const clearDocs = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript(`getProto(self).data = new List([])`), toolTip: "Empty", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.TextButton, _width: 200, buttonText: "Empty Recently Closed", icon: "trash", system: true }); - doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([clearDocs], { - title: "My Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true, + const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); + const clearDocsButton:Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true }); + doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, + explainer: "Documents that you close will appear here so you can easily access them again. You can choose to clear this menu in which case you might lose these documents." + })); - const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List([clearAll!]); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); + (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Empty recently closed"]); + (doc.myRecentlyClosedDocs as any as Doc).contextMenuIcons = new List(["trash"]); + } } @@ -916,7 +920,7 @@ export class CurrentUserUtils { static async setupSidebarButtons(doc: Doc) { CurrentUserUtils.setupSidebarContainer(doc); await CurrentUserUtils.setupToolsBtnPanel(doc); - CurrentUserUtils.setupImportSidebar(doc); + CurrentUserUtils.setupUploadSidebar(doc); CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupFilesystem(doc); @@ -930,17 +934,17 @@ export class CurrentUserUtils { _lockedPosition: true, system: true, flexDirection: "row" })) as any as Doc - static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, _dropAction: "alias", _removeDropProperties: new List(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true + static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ + ...opts, btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _removeDropProperties: new List(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc) { if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true }); + doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("undo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true }); } if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true }); + doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("redo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true }); } if (doc.dockedBtns === undefined) { doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); @@ -1039,7 +1043,7 @@ export class CurrentUserUtils { // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available - // { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected + { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected { title: "Schema", type: "schemaTools", subMenu: true, hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected ]; } @@ -1172,13 +1176,15 @@ export class CurrentUserUtils { } doc.myLinkDatabase = new PrefetchProxy(linkDocs); } + // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document if (doc.mySharedDocs === undefined) { let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, + _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, _chromeHidden: true, boxShadow: "0 0", + explainer: "All of the documents that you receive will appear here. They must be shared by other users. If you receive a Dashboard you can add it to your Dashboards by right clicking and clicking 'Add to Dashboards'" }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; } @@ -1189,18 +1195,21 @@ export class CurrentUserUtils { sharedDocs.childContextMenuFilters = new List([dashboardFilter!,]); sharedDocs.childContextMenuScripts = new List([addToDashboards!,]); sharedDocs.childContextMenuLabels = new List(["Add to Dashboards",]); + sharedDocs.childContextMenuIcons = new List(["user-plus",]); + } doc.mySharedDocs = new PrefetchProxy(sharedDocs); } } // Import sidebar is where shared documents are contained - static setupImportSidebar(doc: Doc) { - if (doc.myImportDocs === undefined) { - const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Upload from computer", _width: 100, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.TextButton, buttonText: "Upload from computer", icon: "upload", system: true }); - doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([newUpload], { - title: "My Uploads", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, - childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, + static setupUploadSidebar(doc: Doc) { + if (doc.myUploadDocs === undefined) { + const newUploadButton:Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Upload from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.ClickButton, buttonText: "Upload", icon: "upload", system: true }); + doc.myUploadDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { + title: "My Uploads", _forceActive: true, buttonMenu: true, buttonMenuDoc: newUploadButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, + childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, + explainer: "All of the documents that are imported into Dash will go here and stay here unless they are explicitly removed." })); } } @@ -1303,7 +1312,7 @@ export class CurrentUserUtils { doc.filterDocCount = 0; 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.setupImportSidebar(doc); // sets up the import sidebar + this.setupUploadSidebar(doc); // sets up the import sidebar this.setupSearchSidebar(doc); // sets up the search sidebar this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupOverlays(doc); // documents in overlay layer @@ -1430,7 +1439,7 @@ export class CurrentUserUtils { } } } else if (input.files && input.files.length !== 0) { - const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); + const importDocs = Cast(Doc.UserDoc().myUploadDocs, Doc, null); const disposer = OverlayView.ShowSpinner(); DocListCastAsync(importDocs.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 41a6dc569..47ae0424b 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -130,7 +130,7 @@ } .contextMenu-inlineMenu { - border-top: solid 1px; + // border-top: solid 1px; //TODO:glr clean } .contextMenu-item:hover { diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 168fcb888..c3921d846 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -116,12 +116,12 @@ export class ContextMenuItem extends React.Component {this.props.icon ? ( - + ) : null}
+ style={{ alignItems: "center", alignSelf: "center" }} > {this.props.description}
diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss index 03cb5cc84..0648e31c5 100644 --- a/src/client/views/MainViewModal.scss +++ b/src/client/views/MainViewModal.scss @@ -6,6 +6,7 @@ height: 100%; .dialogue-box { + padding: 10px; position: absolute; z-index: 1000; text-align: center; @@ -13,7 +14,7 @@ align-self: center; align-content: center; background: white; - border-radius: 10px; + // border-radius: 10px; box-shadow: #00000044 5px 5px 10px; transform: translate(-50%, -50%); top: 50%; diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss index 484522bc7..77686a710 100644 --- a/src/client/views/PropertiesButtons.scss +++ b/src/client/views/PropertiesButtons.scss @@ -67,10 +67,12 @@ $linkGap : 3px; } .onClickFlyout-editScript { + cursor: pointer; text-align: center; - border: 0.5px solid grey; + margin-top: 5px; + border: 0.5px solid $medium-gray; background-color: rgb(230, 230, 230); - border-radius: 9px; + border-radius: 5px; padding: 4px; } @@ -84,9 +86,32 @@ $linkGap : 3px; margin-bottom: 8px; } +.propertiesButton-dropdownList { + width: 100%; + height: fit-content; + top: 100%; + z-index: 21; + + .list-item { + cursor: pointer; + color: $black; + width: 100%; + height: 25px; + font-weight: 400; + display: flex; + justify-content: left; + align-items: center; + padding-left: 5px; + } + + .list-item:hover { + background-color: lightgrey; + } +} + .propertiesButtons-title { - background: #121721; - color: white; + background: $dark-gray; + color: $white; font-size: 6px; width: 37px; padding: 3px; @@ -111,17 +136,11 @@ $linkGap : 3px; margin-left: 4px; &:hover { - background: $medium-gray; - transform: scale(1.05); + filter:brightness(0.85); cursor: pointer; } } -.propertiesButtons-linker:hover { - cursor: pointer; - transform: scale(1.05); -} - @-moz-keyframes spin { 100% { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index dcab9bc1e..4fde1124f 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -15,6 +15,7 @@ import { InkingStroke } from './InkingStroke'; import { DocumentView } from './nodes/DocumentView'; import './PropertiesButtons.scss'; import React = require("react"); +import { Colors } from "./global/globalEnums"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -128,11 +129,11 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch @action - handleOptionChange = (e: any) => { - this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value); + handleOptionChange = (onClick: string) => { + this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick); SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => { docView.noOnClick(); - switch (e.target.value) { + switch (onClick) { case "enterPortal": docView.makeIntoPortal(); break; case "toggleDetail": docView.setToggleDetail(); break; case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break; @@ -149,20 +150,34 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get onClickFlyout() { - const makeLabel = (value: string, label: string) =>
- -
; + const buttonList = [ + ["nothing", "Select Document"], + ["enterPortal", "Enter Portal"], + ["toggleDetail", "Toggle Detail"], + ["linkInPlace", "Follow Link"], + ["linkOnRight", "Open Link on Right"] + ] + const currentSelection = this.selectedDoc.onClickBehavior; + // Get items to place into the list + + const list = buttonList.map((value) => { + const click = () => { + this.handleOptionChange(value[0]); + }; + return
+ {value[1]} +
; + }); return
-
- {makeLabel("nothing", "Select Document")} - {makeLabel("enterPortal", "Enter Portal")} - {makeLabel("toggleDetail", "Toggle Detail")} - {makeLabel("linkInPlace", "Follow Link")} - {makeLabel("linkOnRight", "Open Link on Right")} -
+
+
+ {list} +
+
{Doc.UserDoc().noviceMode ? (null) :
Edit onClick Script
}
; } @@ -190,7 +205,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isFreeForm = this.selectedDoc?._viewType === CollectionViewType.Freeform; const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree; const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) =>
{ele}
; - + const isNovice = Doc.UserDoc().noviceMode; return !this.selectedDoc ? (null) :
{toggle(this.titleButton)} @@ -198,7 +213,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.lockButton)} {toggle(this.dictationButton)} {toggle(this.onClickButton)} - {toggle(this.fitWidthButton)} + {toggle(this.fitWidthButton, { display: isNovice ? "none" : "" })} {toggle(this.fitContentButton, { display: !isFreeForm ? "none" : "" })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })} {toggle(this.maskButton, { display: !isInk ? "none" : "" })} @@ -207,7 +222,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.snapButton, { display: isCollection ? "" : "none" })} {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })} {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })} - {toggle(this.perspectiveButton, { display: !isCollection ? "none" : "" })} + {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? "none" : "" })}
; } } \ No newline at end of file diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 0e3663f65..01b79ffd2 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -106,9 +106,10 @@ export class SidebarAnnos extends React.Component { {user}
; }; + // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos return !this.props.showSidebar ? (null) :
, props: Opt, props: Opt (props?.PanelHeight() || 0) ? 5 : 10) : 0; - case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) || + case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; case StyleProp.BackgroundColor: { if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 6bdeaf722..6a22acae8 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {this.renderColors(this._col)}
+ >Hide Column
; } diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 4b123c8b6..2f002736d 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -14,6 +14,30 @@ width: 100%; } +// TODO:glr Turn this into a seperate class +.documentButtonMenu { + position: relative; + height: fit-content; + border-bottom: $standard-border; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + align-content: center; + padding: 5px 0 5px 0; + + .documentExplanation { + width: 90%; + margin: 5px; + font-size: 11px; + background-color: $light-blue; + color: $medium-blue; + padding: 10px; + border-radius: 10px; + border: solid 2px $medium-blue; + } +} + .collectionStackingView, .collectionMasonryView { height: 100%; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4fedda1bd..3c9084f64 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; @@ -11,7 +11,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils"; import { DocUtils, Docs } from "../../documents/Documents"; import { DragManager, dropActionType } from "../../util/DragManager"; import { SnappingManager } from "../../util/SnappingManager"; @@ -23,12 +23,14 @@ import { EditableView } from "../EditableView"; import { LightboxView } from "../LightboxView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView"; -import { StyleProp } from "../StyleProvider"; +import { StyleProp, DefaultStyleProvider } from "../StyleProvider"; import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow"; import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; +import { FontIconBox } from "../nodes/button/FontIconBox"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const _global = (window /* browser */ || global /* node */) as any; type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; @@ -514,6 +516,47 @@ export class CollectionStackingView extends CollectionSubView this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0)); } + @computed get buttonMenu() { + const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); + // TODO:glr Allow support for multiple buttons + if (menuDoc){ + const width: number = NumCast(menuDoc._width, 30); + const height: number = NumCast(menuDoc._height, 30); + console.log(menuDoc.title, width, height); + return (
+ 35} + PanelHeight={() => 35} + renderDepth={this.props.renderDepth} + focus={emptyFunction} + styleProvider={this.props.styleProvider} + layerProvider={this.props.layerProvider} + docViewPath={returnEmptyDoclist} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={this.props.docFilters} + docRangeFilters={this.props.docRangeFilters} + searchFilterDocs={this.props.searchFilterDocs} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> +
+ ); + } + } + @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); } @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); } @@ -529,34 +572,50 @@ export class CollectionStackingView extends CollectionSubView -
this._scroll = e.currentTarget.scrollTop)} - onDrop={this.onExternalDrop.bind(this)} - onContextMenu={this.onContextMenu} - onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} > - {this.renderedSections} - {!this.showAddAGroup ? (null) : -
- -
} - {/* {this.chromeHidden || !this.props.isSelected() ? (null) : - } */} -
+ <> + {buttonMenu || noviceExplainer ?
+ {buttonMenu ? this.buttonMenu : null} + {Doc.UserDoc().isNovice && noviceExplainer ? +
+ {noviceExplainer} +
+ : null + } +
: null} +
+
this._scroll = e.currentTarget.scrollTop)} + onDrop={this.onExternalDrop.bind(this)} + onContextMenu={this.onContextMenu} + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} > + {this.renderedSections} + {!this.showAddAGroup ? (null) : +
+ +
} + {/* {this.chromeHidden || !this.props.isSelected() ? (null) : + } */} +
+
+ + ); } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 89d42439e..4d62a1af4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -8,7 +8,7 @@ import { Document, listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils'; +import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyFunction } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; @@ -26,6 +26,7 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import { TreeView } from "./TreeView"; import React = require("react"); +import { Transform } from '../../util/Transform'; const _global = (window /* browser */ || global /* node */) as any; export type collectionTreeViewProps = { @@ -221,8 +222,9 @@ export class CollectionTreeView extends CollectionSubView { const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []); - const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []); - return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label })); + const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []); + const icons = StrListCast(this.doc.childContextMenuIcons); + return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); } @computed get treeViewElements() { TraceMobx(); @@ -265,13 +267,48 @@ export class CollectionTreeView extends CollectionSubView - -
; + @computed get buttonMenu() { + const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); + // To create a multibutton menu add a CollectionLinearView + if (menuDoc){ + + const width: number = NumCast(menuDoc._width, 30); + const height: number = NumCast(menuDoc._height, 30); + console.log(menuDoc.title, width, height); + return (
+ 35} + PanelHeight={() => 35} + renderDepth={this.props.renderDepth + 1} + focus={emptyFunction} + styleProvider={this.props.styleProvider} + layerProvider={this.props.layerProvider} + docViewPath={returnEmptyDoclist} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={this.props.docFilters} + docRangeFilters={this.props.docRangeFilters} + searchFilterDocs={this.props.searchFilterDocs} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> +
); + } } + + @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); } @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); } @@ -295,22 +332,34 @@ export class CollectionTreeView extends CollectionSubView this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined; + const buttonMenu = this.rootDoc.buttonMenu; + const noviceExplainer = this.rootDoc.explainer; return !(this.doc instanceof Doc) || !this.treeChildren ? (null) : -
-
e.stopPropagation()} - onDrop={this.onTreeDrop} - ref={this.createTreeDropTarget}> + <> {this.titleBar} - {this.renderClearButton} -
    - {this.treeViewElements} -
-
-
; +
+ {buttonMenu || noviceExplainer ?
+ {buttonMenu ? this.buttonMenu : null} + {Doc.UserDoc().noviceMode && noviceExplainer ? +
+ {noviceExplainer} +
+ : null + } +
: null} +
e.stopPropagation()} + onDrop={this.onTreeDrop} + ref={this.createTreeDropTarget}> +
    + {this.treeViewElements} +
+
+
+ ; } } \ No newline at end of file diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 889db1ef6..34cb6ec55 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -387,9 +387,10 @@ export class TabDocView extends React.Component { background={this.miniMapColor} document={this._document} tabView={this.tabView} /> - {this._document.hideMinimap ? "Open minimap" : "Close minimap"}
}> + {this._document.hideMinimap ? "Open minimap" : "Close minimap"}
}>
{ childContextMenuItems = () => { const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []); const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []); - return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label })); + const icons = StrListCast(this.doc.childContextMenuIcons); + return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); @@ -835,7 +836,7 @@ export class TreeView extends React.Component { dontRegisterView: boolean | undefined, observerHeight: (ref: any) => void, unobserveHeight: (ref: any) => void, - contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[]) + contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[]) ) { const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField); if (viewSpecScript) { diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index f249eb1f5..8fe804466 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -98,7 +98,11 @@ transition: transform 0.2s; align-items: center; justify-content: center; - transition: 0.15s; + transition: 0.2s; + + &:hover{ + filter: brightness(0.85); + } } >input { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index b28c32e0e..a25f962df 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -191,7 +191,7 @@ export class CollectionSchemaColumnMenu extends React.Component {this.renderSorting()} {this.renderColors()}
- +
} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index fed64b620..dfe99ffc8 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {this.renderColors(this._col)}
+ >Hide Column
; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index f5a7e61aa..8962d29f0 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -638,12 +638,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<
) : ( - +
)}
) : ( diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7463e192f..98ae279db 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -635,7 +635,7 @@ export class DocumentViewInternal extends DocComponent { const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { - const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ".portal" }); + const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" }); DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); } this.Document.followLinkLocation = "inPlace"; @@ -679,7 +679,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, "add:right"), icon: "eye" }); - appearanceItems.push({ + !Doc.UserDoc().noviceMode && appearanceItems.push({ description: "Add a Field", event: () => { const alias = Doc.MakeAlias(this.rootDoc); alias.layout = FormattedTextBox.LayoutString("newfield"); diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 72dec6e4c..f44355929 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -1,3 +1,5 @@ +@import "../global/globalCssVariables.scss"; + .pdfBox, .pdfBox-interactive { display: inline-block; @@ -18,12 +20,13 @@ top: 0; left: 0; + // glr: This should really be the same component as text and PDFs .pdfBox-sidebarBtn { - background: #121721; + background: $black; height: 25px; width: 25px; - right: 0; - color: white; + right: 5px; + color: $white; display: flex; position: absolute; align-items: center; @@ -31,6 +34,13 @@ border-radius: 3px; pointer-events: all; z-index: 1; // so it appears on top of the document's title, if shown + + box-shadow: $standard-box-shadow; + transition: 0.2s; + + &:hover{ + filter: brightness(0.85); + } } .pdfBox-pageNums { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5e07229c1..3a399bfdb 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -24,6 +24,7 @@ import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); import { AnchorMenu } from '../pdf/AnchorMenu'; +import { Colors } from '../global/globalEnums'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -208,11 +209,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pageControls = !this._pageControls)} /> {this._pageControls ? pageBtns : (null)}
- + +
; } sidebarWidth = () => !this.SidebarShown ? 0 : diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 19b69ff5a..417a17d96 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -10,7 +10,7 @@ background: #121721; height: 25px; width: 25px; - right: 0; + right: 5px; display: flex; position: absolute; align-items: center; @@ -18,6 +18,13 @@ border-radius: 3px; pointer-events: all; z-index: 1; // so it appears on top of the document's title, if shown + + box-shadow: $standard-box-shadow; + transition: 0.2s; + + &:hover{ + filter: brightness(0.85); + } } .pdfViewerDash-dragAnnotationBox { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 719e5a796..1f85219d6 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -33,6 +33,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from "./LinkDocPreview"; import "./WebBox.scss"; import React = require("react"); +import { Colors } from "../global/globalEnums"; const _global = (window /* browser */ || global /* node */) as any; const htmlToText = require("html-to-text"); @@ -590,11 +591,15 @@ export class WebBox extends ViewBoxAnnotatableComponent - + +
); } } diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index 48fb2c8dc..a2da35fe1 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -7,6 +7,7 @@ align-items: center; font-size: 80%; border-radius: $standard-border-radius; + transition: 0.15s; .menuButton-wrap { grid-column: 1; @@ -29,6 +30,9 @@ font-family: 'ROBOTO'; text-transform: uppercase; font-weight: bold; + transition: 0.15s; + + } .fontIconBox-icon { @@ -50,7 +54,18 @@ } &.textBtn { + display: grid; + /* grid-row: auto; */ + grid-auto-flow: column; + cursor: pointer; width: 100%; + justify-content: center; + align-items: center; + justify-items: center; + + &:hover { + filter:brightness(0.85) !important; + } } &.tglBtn { @@ -127,13 +142,13 @@ } &:hover { - background-color: rgba(0, 0, 0, 0.3) !important; + background-color: rgba(0, 0, 0, 0.3); } } &.toolBtn { cursor: pointer; - width: 40px; + width: 100%; border-radius: 100%; svg { @@ -143,7 +158,7 @@ } &.menuBtn { - cursor: pointer; + cursor: pointer !important; border-radius: 0px; flex-direction: column; @@ -151,6 +166,10 @@ width: 45% !important; height: 45%; } + + &:hover{ + filter: brightness(0.85); + } } @@ -163,8 +182,8 @@ .colorButton-color { margin-top: 3px; - width: 90%; - height: 6px; + width: 80%; + height: 3px; } .menuButton-dropdownBox { diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index bccf733f0..1b27d562a 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -253,11 +253,12 @@ export class FontIconBox extends DocComponent(Fon } else if (selected) { dropdown = false; text = StrCast(selected.Document.type); + if (text === DocumentType.RTF) text = "Text"; icon = Doc.toIcon(selected.Document); } else { dropdown = false; - noneSelected = true; - text = "None selected"; + icon = "globe-asia"; + text = "User Default"; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; } else if (script === 'setFont') { @@ -302,7 +303,7 @@ export class FontIconBox extends DocComponent(Fon
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> - {dropdown || noneSelected ? (null) : } + {dropdown || noneSelected ? (null) : }
{text && text[0].toUpperCase() + text.slice(1)}
@@ -487,7 +488,7 @@ export class FontIconBox extends DocComponent(Fon
; const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : -
+
{this.label}
; @@ -508,7 +509,7 @@ export class FontIconBox extends DocComponent(Fon switch (this.type) { case ButtonType.TextButton: button = ( -
+
{buttonText ?
@@ -558,7 +559,7 @@ export class FontIconBox extends DocComponent(Fon break; case ButtonType.MenuButton: const trailsIcon = ; + style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />; button = (
{this.icon === "pres-trail" ? trailsIcon : } @@ -605,7 +606,7 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult && selected) { - if (NumCast(selected.Document.z) === 1) return Colors.MEDIUM_BLUE; + if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE; else return "transparent"; } selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 3cedab1a4..56f5c5631 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -66,14 +66,25 @@ audiotag:hover { .formattedTextBox-sidebar-handle { position: absolute; - top: 0; + top: 5px; //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views - width: 10px; + width: 25px; height: 100%; - max-height: 35px; - background: lightgray; - border-radius: 20px; + max-height: 25px; + color: white; + background: $medium-gray; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; cursor:grabbing; + box-shadow: $standard-box-shadow; + // transition: 0.2s; + + &:hover{ + filter: brightness(0.85); + } + } .formattedTextBox-sidebar, diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f45b9de7a..468edf6cc 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -63,6 +63,8 @@ import { SummaryView } from "./SummaryView"; import applyDevTools = require("prosemirror-dev-tools"); import React = require("react"); import { SidebarAnnos } from '../../SidebarAnnos'; +import { Colors } from '../../global/globalEnums'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; const translateGoogleApi = require("translate-google-api"); export interface FormattedTextBoxProps { @@ -574,11 +576,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" }); const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null); DocListCast(noteTypesDoc?.data).forEach(note => { + const icon: IconProp = StrCast(note.icon) as IconProp; changeItems.push({ description: StrCast(note.title), event: undoBatch(() => { Doc.setNativeView(this.rootDoc); DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); - }), icon: "eye" + }), icon: icon }); }); !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }); @@ -1481,11 +1484,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; + const color = !annotated ? Colors.WHITE : Colors.BLACK; + const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : "")); return (!annotated && !this.props.isContentActive()) ? (null) :
; + left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 30px"}))`, + backgroundColor: backgroundColor, + color: color + }} > + +
; } @computed get sidebarCollection() { const renderComponent = (tag: string) => { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 260ddfc90..e70b2bc19 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -114,7 +114,7 @@ export class SearchBox extends ViewBoxBaseComponent
-
+
window.open( + "https://brown-dash.github.io/Dash-Documentation/", "_blank")}> {"Help"}
SettingsManager.Instance.open()}> -- cgit v1.2.3-70-g09d2 From 5c6946fd211f6b0ee5d860ade968f4353344974d Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 2 Sep 2021 12:14:20 -0400 Subject: fixed autoHeight for formattedText to work better with stacking and contentfitting views. --- src/client/views/PropertiesButtons.tsx | 4 ++-- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 0bcdbac19..9fe2122e6 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -218,8 +218,8 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })} {toggle(this.maskButton, { display: !isInk ? "none" : "" })} {toggle(this.chromeButton, { display: !isCollection || isNovice ? "none" : "" })} - {toggle(this.gridButton, { display: isCollection ? "" : "none" })} - {toggle(this.snapButton, { display: isCollection ? "" : "none" })} + {toggle(this.gridButton, { display: !isCollection ? "none" : "" })} + {toggle(this.snapButton, { display: !isCollection ? "none" : "" })} {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })} {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })} {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? "none" : "" })} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 468edf6cc..59d43f8d7 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -905,7 +905,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, { fireImmediately: true } ); quickScroll = undefined; - this.tryUpdateScrollHeight(); + setTimeout(this.tryUpdateScrollHeight, 10); } pushToGoogleDoc = async () => { @@ -1492,8 +1492,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp backgroundColor: backgroundColor, color: color }} > - -
; + +
; } @computed get sidebarCollection() { const renderComponent = (tag: string) => { -- cgit v1.2.3-70-g09d2 From 80baae4dcf49f818dca4dbfe340013e0e9d197bb Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 3 Sep 2021 03:58:21 -0400 Subject: fixed drawing of link lines - doesn't generate duplicates, and works with text regions, scroll locations, and document anchors. --- src/client/documents/Documents.ts | 9 +++--- src/client/util/DocumentManager.ts | 37 ++++++++++++++-------- .../CollectionFreeFormLinkView.tsx | 8 ++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 24 ++++++++++---- src/client/views/nodes/PDFBox.tsx | 10 +++--- src/client/views/nodes/WebBox.tsx | 10 +++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 8 files changed, 63 insertions(+), 39 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bd247bd01..671e27408 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -157,6 +157,7 @@ export class DocumentOptions { z?: number; // whether document is in overlay (1) or not (0 or undefined) author?: string; _layoutKey?: string; + unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf) type?: string; title?: string; "acl-Public"?: string; // public permissions @@ -277,7 +278,7 @@ export class DocumentOptions { // Action Button buttonMenu?: boolean; // whether a action button should be displayed buttonMenuDoc?: Doc; - explainer?:string; + explainer?: string; treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view _treeViewOpen?: boolean; // whether this document is expanded in a tree view (note: need _ and regular versions since this can be specified for both proto and layout docs) @@ -1188,7 +1189,7 @@ export namespace DocUtils { })) as ContextMenuProps[], icon: "sticky-note" }); - const math:ContextMenuProps = ({ + const math: ContextMenuProps = ({ description: ":Math", event: () => { const created = Docs.Create.EquationDocument(); if (created) { @@ -1202,7 +1203,7 @@ export namespace DocUtils { } }, icon: "calculator" }); - const documentList:ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ + const documentList: ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title), event: undoBatch((args: { x: number, y: number }) => { const newDoc = Doc.copyDragFactory(dragDoc); @@ -1215,7 +1216,7 @@ export namespace DocUtils { } }), icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; + })) as ContextMenuProps[]; documentList.push(math) ContextMenu.Instance.addItem({ description: "Create document", diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index cb0ee411c..b72a3189d 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -25,24 +25,35 @@ export class DocumentManager { @action public AddView = (view: DocumentView) => { - DocListCast(view.rootDoc.links).forEach(link => { - const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2"; - const otherDoc = link && (link[whichOtherAnchor] as Doc); - const otherDocAnno = DocumentType.MARKER === otherDoc?.type ? otherDoc.annotationOn as Doc : undefined; - otherDoc && DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)). - forEach(otherView => { - if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) { - this.LinkedDocumentViews.push({ a: whichOtherAnchor === "anchor1" ? otherView : view, b: whichOtherAnchor === "anchor1" ? view : otherView, l: link }); - } - }); - }); + //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString); + if (view.props.LayoutTemplateString?.includes("LinkAnchorBox")) { + const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1"; + view.props.LayoutTemplateString && DocListCast(view.rootDoc.links).forEach(link => { + DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)). + forEach(otherView => { + this.LinkedDocumentViews.push( + { + a: viewAnchorIndex === "anchor2" ? otherView : view, + b: viewAnchorIndex === "anchor2" ? view : otherView, + l: link + }); + }); + }); + // this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " + + // view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString)); + } this.DocumentViews.push(view); } public RemoveView = action((view: DocumentView) => { + this.LinkedDocumentViews.slice().forEach(action(pair => { + if (pair.a === view || pair.b === view) { + const li = this.LinkedDocumentViews.indexOf(pair); + li !== -1 && this.LinkedDocumentViews.splice(li, 1); + } + })); + const index = this.DocumentViews.indexOf(view); index !== -1 && this.DocumentViews.splice(index, 1); - - this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null)); }); //gets all views diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 16258404d..6fe46bbb2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -43,8 +43,8 @@ export class CollectionFreeFormLinkView extends React.Component this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. - const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; - const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; + const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); + const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); const adiv = acont.length ? acont[0] : A.ContentDiv; const bdiv = bcont.length ? bcont[0] : B.ContentDiv; const a = adiv.getBoundingClientRect(); @@ -140,8 +140,8 @@ export class CollectionFreeFormLinkView extends React.Component + Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) || + Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) || + ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) || + ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc)) + ); + } @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); } @computed get allLinkEndpoints() { // the small blue dots that mark the endpoints of links TraceMobx(); - if (this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null; + if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null; if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null); - // need to use allLinks for RTF since embedded linked text anchors are not rendered with DocumentViews. All other documents render their anchors with nested DocumentViews so we just need to render the directLinks here - const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden); + const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden); return filtered.map((link, i) => -
+
{ } componentWillUnmount() { Object.values(this._disposers).forEach(disposer => disposer?.()); - !this.props.dontRegisterView && DocumentManager.Instance.RemoveView(this); + !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this); } render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3a399bfdb..2bafb3b7f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -79,9 +79,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent
- +
; } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 1f85219d6..eaedd48c7 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -187,8 +187,8 @@ export class WebBox extends ViewBoxAnnotatableComponent
- +
); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 59d43f8d7..bc578f95d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -713,7 +713,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { - const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) }); + const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true }); const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { -- cgit v1.2.3-70-g09d2 From 64ff849d21273f7440917ef15a4b1c7f1909687c Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 3 Sep 2021 13:16:48 -0400 Subject: fixed warnings. cleaned up link lines a bit more - simplified, and allowed multiple aliases to share a link. fixed link lines to text anchors from flapping around wildly when camera pans --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 12 +++---- src/client/util/DocumentManager.ts | 38 +++++++++++++--------- src/client/views/PropertiesButtons.tsx | 2 +- .../CollectionFreeFormLinkView.tsx | 13 +++++--- .../CollectionFreeFormLinksView.tsx | 19 ++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 1 - src/client/views/nodes/PDFBox.tsx | 10 +++--- src/client/views/nodes/WebBox.tsx | 4 +-- .../views/nodes/button/textButton/TextButton.tsx | 2 +- src/client/views/nodes/button/textButton/index.ts | 2 +- .../views/nodes/button/toggleButton/index.ts | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 13 +++----- src/client/views/pdf/PDFViewer.tsx | 5 ++- 15 files changed, 60 insertions(+), 67 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 671e27408..9a45da440 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1217,7 +1217,7 @@ export namespace DocUtils { }), icon: Doc.toIcon(dragDoc), })) as ContextMenuProps[]; - documentList.push(math) + documentList.push(math); ContextMenu.Instance.addItem({ description: "Create document", subitems: documentList, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 56807c63b..f9c55da29 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -832,7 +832,7 @@ export class CurrentUserUtils { await doc.myTrails; if (doc.myTrails === undefined) { const newTrail = ScriptField.MakeScript(`createNewPresentation()`); - const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true }); + const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true }); doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Trails", _showTitle: "title", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", @@ -852,16 +852,16 @@ export class CurrentUserUtils { doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); const newFolder = ScriptField.MakeFunction(`doc.makeFolder()`, { doc: doc.myFilesystem })!; - const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true }); + const newFolderButton: Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true }); doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], { - title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, + title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." })); - (doc.myTrails as any as Doc).contextMenuScripts = new List([newFolder!]); + (doc.myTrails as any as Doc).contextMenuScripts = new List([newFolder]); (doc.myTrails as any as Doc).contextMenuLabels = new List(["Create new folder"]); } return doc.myFilesystem as any as Doc; @@ -870,7 +870,7 @@ export class CurrentUserUtils { static setupRecentlyClosedDocs(doc: Doc) { if (doc.myRecentlyClosedDocs === undefined) { const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); - const clearDocsButton:Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true }); + const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true }); doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", @@ -1215,7 +1215,7 @@ export class CurrentUserUtils { // Import sidebar is where shared documents are contained static setupUploadSidebar(doc: Doc) { if (doc.myUploadDocs === undefined) { - const newUploadButton:Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Upload from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.ClickButton, buttonText: "Upload", icon: "upload", system: true }); + const newUploadButton: Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Upload from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.ClickButton, buttonText: "Upload", icon: "upload", system: true }); doc.myUploadDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My Uploads", _forceActive: true, buttonMenu: true, buttonMenuDoc: newUploadButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index b72a3189d..9e190ad02 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,19 +1,21 @@ import { action, observable, runInAction } from 'mobx'; import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; -import { Cast, NumCast, StrCast } from '../../fields/Types'; +import { Cast } from '../../fields/Types'; import { returnFalse } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; +import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { Scripting } from './Scripting'; export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) @observable public DocumentViews: DocumentView[] = []; + @observable public LinkAnchorBoxViews: DocumentView[] = []; @observable public RecordingEvent = 0; @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = []; @@ -26,23 +28,24 @@ export class DocumentManager { @action public AddView = (view: DocumentView) => { //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString); - if (view.props.LayoutTemplateString?.includes("LinkAnchorBox")) { + if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1"; - view.props.LayoutTemplateString && DocListCast(view.rootDoc.links).forEach(link => { - DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)). - forEach(otherView => { - this.LinkedDocumentViews.push( - { - a: viewAnchorIndex === "anchor2" ? otherView : view, - b: viewAnchorIndex === "anchor2" ? view : otherView, - l: link - }); - }); + DocListCast(view.rootDoc.links).forEach(link => { + this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)). + forEach(otherView => this.LinkedDocumentViews.push( + { + a: viewAnchorIndex === "anchor2" ? otherView : view, + b: viewAnchorIndex === "anchor2" ? view : otherView, + l: link + }) + ); }); + this.LinkAnchorBoxViews.push(view); // this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " + // view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString)); + } else { + this.DocumentViews.push(view); } - this.DocumentViews.push(view); } public RemoveView = action((view: DocumentView) => { this.LinkedDocumentViews.slice().forEach(action(pair => { @@ -52,8 +55,13 @@ export class DocumentManager { } })); - const index = this.DocumentViews.indexOf(view); - index !== -1 && this.DocumentViews.splice(index, 1); + if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { + const index = this.LinkAnchorBoxViews.indexOf(view); + this.LinkAnchorBoxViews.splice(index, 1); + } else { + const index = this.DocumentViews.indexOf(view); + index !== -1 && this.DocumentViews.splice(index, 1); + } }); //gets all views diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 9fe2122e6..dd737dc9d 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -156,7 +156,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { ["toggleDetail", "Toggle Detail"], ["linkInPlace", "Follow Link"], ["linkOnRight", "Open Link on Right"] - ] + ]; const currentSelection = this.selectedDoc.onClickBehavior; // Get items to place into the list diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 6fe46bbb2..3b3e069d8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -5,7 +5,6 @@ import { Id } from "../../../../fields/FieldSymbols"; import { List } from "../../../../fields/List"; import { NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils'; -import { DocumentType } from "../../../documents/DocumentTypes"; import { LinkManager } from "../../../util/LinkManager"; import { SnappingManager } from "../../../util/SnappingManager"; import { DocumentView } from "../../nodes/DocumentView"; @@ -69,8 +68,10 @@ export class CollectionFreeFormLinkView extends React.Component= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100; + if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100; } if (!targetBhyperlink) { if (linkDoc.linkAutoMove) { @@ -80,8 +81,10 @@ export class CollectionFreeFormLinkView extends React.Component= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100; + if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index e812064b7..dacbb3508 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,30 +1,17 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; -import { Utils } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; -import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { DocumentType } from "../../../documents/DocumentTypes"; -import { LinkManager } from "../../../util/LinkManager"; @observer export class CollectionFreeFormLinksView extends React.Component { @computed get uniqueConnections() { - const connections = DocumentManager.Instance.LinkedDocumentViews - .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK) - .reduce((drawnPairs, connection) => { - const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b); - matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l)); - if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set([connection.l]) }); - return drawnPairs; - }, [] as { a: DocumentView, b: DocumentView, l: Set }[]); - const set = new Map(); - connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) })); - return Array.from(set.values()).map(c => ); + return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c => + + ); } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2deaea823..018c2a895 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1457,7 +1457,7 @@ export class CollectionFreeFormView extends CollectionSubView
- {this.layoutDoc["_backgroundGridShow"] ? this.backgroundGrid : (null)} + {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)} ; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index eaedd48c7..7e46d8433 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as WebRequest from 'web-request'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; @@ -24,6 +24,7 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; +import { Colors } from "../global/globalEnums"; import { LightboxView } from "../LightboxView"; import { MarqueeAnnotator } from "../MarqueeAnnotator"; import { AnchorMenu } from "../pdf/AnchorMenu"; @@ -33,7 +34,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from "./LinkDocPreview"; import "./WebBox.scss"; import React = require("react"); -import { Colors } from "../global/globalEnums"; const _global = (window /* browser */ || global /* node */) as any; const htmlToText = require("html-to-text"); diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx index 414b50dcb..e18590a95 100644 --- a/src/client/views/nodes/button/textButton/TextButton.tsx +++ b/src/client/views/nodes/button/textButton/TextButton.tsx @@ -12,6 +12,6 @@ export class TextButton extends Component { return (
{this.props.label} -
) +
); } } \ No newline at end of file diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts index 01fcde54f..01d62eb7e 100644 --- a/src/client/views/nodes/button/textButton/index.ts +++ b/src/client/views/nodes/button/textButton/index.ts @@ -1 +1 @@ -export * from './TextButton' \ No newline at end of file +export * from './TextButton'; \ No newline at end of file diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts index 48b28fb4c..cdb9c527c 100644 --- a/src/client/views/nodes/button/toggleButton/index.ts +++ b/src/client/views/nodes/button/toggleButton/index.ts @@ -1 +1 @@ -export * from './ToggleButton' \ No newline at end of file +export * from './ToggleButton'; \ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index bc578f95d..cad9af2b0 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1448,18 +1448,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; if (children) { - var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); - // setTimeout(() => { - // proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - // scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - // scrollHeight && setScrollHeight(); - // }, 10); - } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } else { + setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } } } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 02010e123..d953c6b6c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,14 +2,13 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; -import { createSchema } from "../../../fields/Schema"; import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse, returnEmptyString, returnEmptyFilter } from "../../../Utils"; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -- cgit v1.2.3-70-g09d2 From a11cbed94999a437d3b51dbf8632441acdb8b5be Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 8 Sep 2021 09:33:16 -0400 Subject: tried adjusting text annotation button so text would be more readable in corner. --- .../views/nodes/formattedText/FormattedTextBox.scss | 17 +++++++---------- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 ++++-- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 56f5c5631..4134e3c67 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -66,11 +66,12 @@ audiotag:hover { .formattedTextBox-sidebar-handle { position: absolute; - top: 5px; + top: 0; + left: 17px; //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views - width: 25px; - height: 100%; - max-height: 25px; + width: 17px; + height: 17px; + border-radius: 3px; color: white; background: $medium-gray; border-radius: 5px; @@ -80,8 +81,9 @@ audiotag:hover { cursor:grabbing; box-shadow: $standard-box-shadow; // transition: 0.2s; - + opacity: 0.3; &:hover{ + opacity: 1 !important; filter: brightness(0.85); } @@ -425,12 +427,7 @@ footnote::after { .formattedTextBox-sidebar-handle { position: absolute; - top: 0; - //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views - width: 10px; - height: 35px; background: lightgray; - border-radius: 20px; cursor: grabbing; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index cad9af2b0..bc19320e9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1360,6 +1360,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._undoTyping = undefined; return wasUndoing; } + @action onBlur = (e: any) => { FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined); @@ -1485,9 +1486,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : "")); return (!annotated && !this.props.isContentActive()) ? (null) :
; -- cgit v1.2.3-70-g09d2 From 058cf10e63fe784ba97f288e24fc58d41e0d46f3 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 8 Sep 2021 10:56:05 -0400 Subject: made setting background undoable. added setting header color/visibility from main toolbar. made changing header contents possible using #(;
; + const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc); const titleView = !showTitle ? (null) :
field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")} + contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")} display={"block"} fontSize={10} - GetValue={() => Field.toString((this.dataDoc || this.props.Document)[showTitle.split(";")[0]] as any as Field)} - SetValue={undoBatch((value) => showTitle.includes("Date") ? true : (Doc.GetProto(this.dataDoc || this.props.Document)[showTitle] = value) ? true : true)} + GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle} + SetValue={undoBatch(value => { + if (value?.startsWith(showTitle + "=")) { + value = value.substring((showTitle + "=").length); + if (showTitle !== "title" && Number(value).toString() === value) value = Number(value); + if (showTitle.includes("Date") || showTitle === "author") return true; + return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true; + } else if (value?.startsWith("#")) { + Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate"; + return true; + } + return true; + })} />
; return this.props.hideTitle || (!showTitle && !showCaption) ? diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index b72f31ef8..4475cf347 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -15,7 +15,7 @@ import { DocumentType } from '../../../documents/DocumentTypes'; import { Scripting } from "../../../util/Scripting"; import { SelectionManager } from '../../../util/SelectionManager'; import { ColorScheme } from '../../../util/SettingsManager'; -import { UndoManager } from '../../../util/UndoManager'; +import { UndoManager, undoBatch } from '../../../util/UndoManager'; import { CollectionViewType } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; @@ -31,6 +31,7 @@ import { ToggleButton } from './toggleButton'; import { IButtonProps } from './ButtonInterface'; import { FontIconBadge } from './FontIconBadge'; import './FontIconBox.scss'; +import { undo } from 'prosemirror-history'; const FontIconSchema = createSchema({ icon: "string", }); @@ -303,7 +304,7 @@ export class FontIconBox extends DocComponent(Fon
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> - {dropdown || noneSelected ? (null) : } + {dropdown || noneSelected ? (null) : }
{text && text[0].toUpperCase() + text.slice(1)}
@@ -348,7 +349,7 @@ export class FontIconBox extends DocComponent(Fon const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', - '#FFFFFF', '#f1efeb']; + '#FFFFFF', '#f1efeb', "transparent"]; const colorBox = (func: (color: ColorState) => void) => (Fon const hex: string = value.hex; const s = ScriptField.MakeScript(script + '("' + hex + '", false)'); if (s) { - s.script.run().result; + undoBatch(() => s.script.run().result)(); } }; return ( @@ -493,7 +494,7 @@ export class FontIconBox extends DocComponent(Fon {this.label}
; - const buttonProps:IButtonProps = { + const buttonProps: IButtonProps = { type: this.type, rootDoc: this.rootDoc, label: label, @@ -512,9 +513,9 @@ export class FontIconBox extends DocComponent(Fon button = (
- {buttonText ? + {buttonText ?
- {buttonText} + {buttonText}
: null} {label} @@ -598,11 +599,18 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo return "#FFFFFF"; } } - if (selected && selected.type === DocumentType.INK) selected.fillColor = color; + if (selected?.type === DocumentType.INK) selected.fillColor = color; if (selected) selected._backgroundColor = color; Doc.UserDoc()._fontColor = color; }); +// toggle: Set overlay status of selected document +Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) { + Doc.SharingDoc().userColor = undefined; + Doc.GetProto(Doc.SharingDoc()).userColor = color; + Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate"); +}); + // toggle: Set overlay status of selected document Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index bc19320e9..fbb291858 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1484,15 +1484,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; const color = !annotated ? Colors.WHITE : Colors.BLACK; const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : "")); - return (!annotated && !this.props.isContentActive()) ? (null) :
- -
; + return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) : +
+ +
; } @computed get sidebarCollection() { const renderComponent = (tag: string) => { -- cgit v1.2.3-70-g09d2 From afbbb76afc5a9b1370374b337af1a03a2e94b5d7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Sep 2021 14:46:06 -0400 Subject: fixed anchor menu highlighter button's dropdown location. fixed metadata view for formattedText. added menu item for metadata view. fixed formattedTextbox initialization. --- src/client/util/CurrentUserUtils.ts | 15 ++++++----- src/client/util/Scripting.ts | 8 +++--- src/client/views/DocComponent.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 20 +++++++------- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 31 +++++++++++++++------- .../views/nodes/formattedText/RichTextMenu.scss | 1 + src/client/views/pdf/AnchorMenu.scss | 29 ++++++++++++++++++++ src/client/views/pdf/AnchorMenu.tsx | 13 +++++---- 10 files changed, 86 insertions(+), 38 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index eb9184e88..816503b37 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -22,6 +22,7 @@ import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMu import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; +import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; import { OverlayView } from "../views/OverlayView"; @@ -37,8 +38,6 @@ import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; -import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; interface Button { title?: string; @@ -447,16 +446,16 @@ export class CurrentUserUtils { storedMarks: [] }; const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { - title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", + title: "text", version: headerViewVersion, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, cloneFieldFilter: new List(["system"]) }, "header"); const headerBtnHgt = 10; headerTemplate[DataSym].layout = "" + - ` ` + - " " + - ` Metadata` + + ` ` + + " " + + ` Metadata` + ""; // "
" + @@ -562,7 +561,7 @@ export class CurrentUserUtils { if (dragCreatorSet === undefined) { doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true })); } else { @@ -1554,6 +1553,8 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { view && SelectionManager.SelectView(view, false); } }); +Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); }, + "document containing all shared Docs"); Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode"); Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index efee37419..40b94024e 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -171,7 +171,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an if (!options.editable) { batch = Doc.MakeReadOnly(); } - + const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); if (batch) { batch.end(); @@ -316,9 +316,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`); } const paramString = paramList.join(", "); - const funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} { - ${addReturn ? `return ${script};` : `return ${script};`} - })`; + const body = addReturn ? `return ${script};` : `return ${script};`; + const reqTypes = requiredType ? `: ${requiredType}` : ''; + const funcScript = `(function(${paramString})${reqTypes} { ${body} })`; host.writeFile("file.ts", funcScript); if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 33dff9da5..cb36f4270 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -115,7 +115,7 @@ export function ViewBoxAnnotatableComponent

{ // bcz: this executes a script to convert a property expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || ""; + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? ""; }; divKeys.map((prop: string) => { const p = (this.props as any)[prop]; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 544125ede..fad905d6d 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -201,7 +201,8 @@ export class DocumentContentsView extends React.Component 1) { const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] }); layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1); - return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" }); + const script = code[1].value.replace(/^‘/, "").replace(/’$/, ""); // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts + return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: "number", value: "string" }); } return undefined; // add input function to props diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6eeb51ffe..6f97cdbd8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -712,7 +712,7 @@ export class DocumentViewInternal extends DocComponent this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" }); if (!this.Document.annotationOn) { @@ -970,19 +970,19 @@ export class DocumentViewInternal extends DocComponent showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle} - SetValue={undoBatch(value => { - if (value?.startsWith(showTitle + "=")) { - value = value.substring((showTitle + "=").length); - if (showTitle !== "title" && Number(value).toString() === value) value = Number(value); - if (showTitle.includes("Date") || showTitle === "author") return true; - return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true; - } else if (value?.startsWith("#")) { + SetValue={undoBatch(input => { + if (input?.startsWith("#")) { if (this.props.showTitle) { - this.rootDoc._showTitle = value?.substring(1) ? value.substring(1) : undefined; + this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined; } else { - Doc.UserDoc().showTitle = value?.substring(1) ? value.substring(1) : "creationDate"; + Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate"; } return true; + } else { + var value = input.replace(new RegExp(showTitle + "="), ""); + if (showTitle !== "title" && Number(value).toString() === value) value = Number(value); + if (showTitle.includes("Date") || showTitle === "author") return true; + return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true; } return true; })} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 62f65cdae..34908e54b 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component { this.ProseRef = ele; + this.setupEditor(this.config, this.props.fieldKey); this._dropDisposer?.(); ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc)); // if (this.autoHeight) this.tryUpdateScrollHeight(); @@ -573,12 +574,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const cm = ContextMenu.Instance; const changeItems: ContextMenuProps[] = []; - changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" }); + changeItems.push({ + description: "plain", event: undoBatch(() => { + Doc.setNativeView(this.rootDoc); + this.layoutDoc.autoHeightMargins = undefined; + }), icon: "eye" + }); + changeItems.push({ + description: "metadata", event: undoBatch(() => { + this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout; + this.rootDoc.layoutKey = "layout_meta"; + setTimeout(() => this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50, 50); + }), icon: "eye" + }); const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null); DocListCast(noteTypesDoc?.data).forEach(note => { const icon: IconProp = StrCast(note.icon) as IconProp; changeItems.push({ description: StrCast(note.title), event: undoBatch(() => { + this.layoutDoc.autoHeightMargins = undefined; Doc.setNativeView(this.rootDoc); DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); }), icon: icon @@ -864,8 +878,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } ); - this.setupEditor(this.config, this.props.fieldKey); - this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc), search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(), { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false }); @@ -1144,8 +1156,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } selectOnLoad && 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. - if (!this._editorView!.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) { - 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 && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) { + this._editorView.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; } } @@ -1487,7 +1499,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) :

= 10 ? "-selected" : ""; - return ( + const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props. e.g., + return (styleFromString?.height === "0px" ? (null) :
this.props.isContentActive() && e.stopPropagation()} style={{ @@ -1564,7 +1577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp width: this.props.dontScale ? undefined : `${100 / scale}%`, height: this.props.dontScale ? undefined : `${100 / scale}%`, // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, - ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., + ...styleFromString }}>
{ @observable private highlightColor: string = "rgba(245, 230, 95, 0.616)"; @observable private _showLinkPopup: boolean = false; - @observable public _colorBtn = false; @observable public Highlighting: boolean = false; @observable public Status: "marquee" | "annotation" | "" = ""; @@ -97,9 +96,11 @@ export class AnchorMenu extends AntimodeMenu { @computed get highlighter() { const button = - ; const dropdownContent = @@ -117,7 +118,9 @@ export class AnchorMenu extends AntimodeMenu {
; return ( {"Click to Highlight"}
}> - +
+ +
); } -- cgit v1.2.3-70-g09d2 From c9c9d00b581bc7f80edbf7848a8ffb3a8e64f005 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 10 Sep 2021 10:14:04 -0400 Subject: event handling fixes for ink. double click on deselected ink takes shows handles. lightbox view works. transparent fills are not selectable. --- src/client/util/InteractionUtils.tsx | 2 +- src/client/views/InkingStroke.tsx | 48 ++++++++++++++++------ src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 8 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- 5 files changed, 41 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index e009fb3a9..8429a806a 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -209,7 +209,7 @@ export namespace InteractionUtils { points={strpts} style={{ // filter: drawHalo ? "url(#inkSelectionHalo)" : undefined, - fill: fill ? fill : "none", + fill: fill && fill !== "transparent" ? fill : "none", opacity: 1.0, // opacity: strokeWidth !== width ? 0.5 : undefined, pointerEvents: pevents as any, diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index db09849fd..dc2a5b39b 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,26 +1,26 @@ import React = require("react"); -import { action, observable } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../fields/Doc"; import { documentSchema } from "../../fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../fields/InkField"; import { makeInterface } from "../../fields/Schema"; -import { Cast, StrCast, NumCast } from "../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; -import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils"; +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { Scripting } from "../util/Scripting"; import { ContextMenu } from "./ContextMenu"; import { ViewBoxBaseComponent } from "./DocComponent"; -import "./InkStroke.scss"; -import { FieldView, FieldViewProps } from "./nodes/FieldView"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; +import { GestureOverlay } from "./GestureOverlay"; +import { Colors } from "./global/globalEnums"; import { InkControls } from "./InkControls"; import { InkHandles } from "./InkHandles"; -import { Colors } from "./global/globalEnums"; -import { GestureOverlay } from "./GestureOverlay"; +import "./InkStroke.scss"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { FieldView, FieldViewProps } from "./nodes/FieldView"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -29,6 +29,8 @@ const InkDocument = makeInterface(documentSchema); export class InkingStroke extends ViewBoxBaseComponent(InkDocument) { static readonly MaskDim = 50000; @observable private _properties?: InkStrokeProperties; + _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated + _selDisposer: IReactionDisposer | undefined; constructor(props: FieldViewProps & InkDocument) { super(props); @@ -36,6 +38,14 @@ export class InkingStroke extends ViewBoxBaseComponent this.props.isSelected(), // react to stroke being deselected by turning off ink handles + selected => !selected && this.toggleControlButton()); + } + componentWillUnmount() { + this._selDisposer?.(); + } + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } @@ -53,14 +63,26 @@ export class InkingStroke extends ViewBoxBaseComponent { + if (this._handledClick) { + e.stopPropagation(); //stop the event so that docView won't open the lightbox + } + } + /** * Handles the movement of the entire ink object when the user clicks and drags. */ onPointerDown = (e: React.PointerEvent) => { + this._handledClick = false; if (this.props.isSelected(true)) { setupMoveUpEvents(this, e, returnFalse, emptyFunction, - action((e: PointerEvent, doubleTap: boolean | undefined) => - doubleTap && this._properties && (this._properties._controlButton = true)) + action((e: PointerEvent, doubleTap: boolean | undefined) => { + doubleTap = doubleTap || this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick; + if (doubleTap && this._properties) { + this._properties._controlButton = true; + this._handledClick = true; // mark the double-click pseudo pointerevent so we can block the real mouse event from propagating to DocumentView + } + }), this._properties?._controlButton, this._properties?._controlButton ); } } @@ -77,7 +99,6 @@ export class InkingStroke extends ViewBoxBaseComponent { const cm = ContextMenu.Instance; if (cm) { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index fad905d6d..dbab5e762 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -113,7 +113,6 @@ export class DocumentContentsView extends React.Component number, setHeight: (height: number) => void, layoutKey: string, - hideOnLeave?: boolean, }> { @computed get layout(): string { TraceMobx(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8b19fb204..a2d2f17b6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -808,7 +808,7 @@ export class DocumentViewInternal extends DocComponent; return
+
field.trim()).map(field => targetDoc[field]?.toString()).join("\\")} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 78de1fd89..e7a44f113 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -69,7 +69,6 @@ const translateGoogleApi = require("translate-google-api"); export interface FormattedTextBoxProps { makeLink?: () => Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text - hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar) xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yPadding?: number; noSidebar?: boolean; @@ -1558,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp TraceMobx(); const selected = this.props.isSelected(); const active = this.props.isContentActive(); - const scale = this.props.hideOnLeave ? 1 : (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); + const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false); if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide); -- cgit v1.2.3-70-g09d2