From ec89542cbd31aaff9d4f58e9e9bf045e934a8235 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 25 Aug 2020 18:13:39 -0400 Subject: changed system documents to act like dividers in tree views. removed some menu items in cases they don't apply. fixed ink selection to not use boudning box. --- src/client/views/collections/CollectionTreeView.scss | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index c9bf82406..f96a5c4f0 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -85,6 +85,15 @@ white-space: pre-wrap; min-width: 10px; } +.docContainer-system { + font-variant: all-small-caps; + border-radius: 5px; + background: grey; + color: white; + padding-left: 3px; + padding-right: 3px; + padding-bottom: 2px; +} .treeViewItem-openRight { display: none; -- cgit v1.2.3-70-g09d2 From 63296223883c1aa9018140abb951e16f614b4365 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 8 Sep 2020 23:32:41 -0400 Subject: fixed treeview to show pdf icons at proper size. fixed link anchors dots to not block pointer events from the rest of their document. --- src/client/views/collections/CollectionTreeView.scss | 2 +- src/client/views/nodes/ContentFittingDocumentView.tsx | 4 ++-- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.scss | 1 + src/client/views/nodes/ScreenshotBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index f96a5c4f0..b0a791a21 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -107,7 +107,7 @@ } .treeViewItem-border { - display: inherit; + display: flex; border-left: dashed 1px #00000042; } diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 55149a44b..c0bcc0a6d 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -28,10 +28,10 @@ export class ContentFittingDocumentView extends React.Component returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc?._nativeWidth || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeWidth"], - (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()))); + (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()))) nativeHeight = () => returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc?._nativeHeight || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeHeight"], - (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()))); + (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()))) @computed get scaling() { const wscale = this.props.PanelWidth() / this.nativeWidth(); if (wscale * this.nativeHeight() > this.props.PanelHeight()) { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 90fc5dc64..67e7c1986 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -148,7 +148,7 @@ export class DocumentContentsView extends React.Component, onInput: Opt): JsxBindings { const list = { - ...OmitKeys(this.props, ['parentActive'], "", (obj: any) => obj.active = this.props.parentActive).omit, + ...OmitKeys(this.props, ['parentActive', 'NativeWidth', 'NativeHeight'], "", (obj: any) => obj.active = this.props.parentActive).omit, RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc, Document: this.layoutDoc, DataDoc: this.dataDoc, diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 2dd3bba91..430a1aa45 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -61,6 +61,7 @@ width: 100%; height: 100%; display: inline-block; + pointer-events: none; } .documentView-lock { diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index f13f373d0..16ce749bc 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -9,7 +9,7 @@ import { InkTool } from "../../../fields/InkField"; import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast } from "../../../fields/Types"; import { VideoField } from "../../../fields/URLField"; -import { emptyFunction, returnFalse, returnOne, returnZero, Utils, OmitKeys, OmitKeys } from "../../../Utils"; +import { emptyFunction, returnFalse, returnOne, returnZero, Utils, OmitKeys } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 925bc9dd6..51506970d 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -8,7 +8,7 @@ import { InkTool } from "../../../fields/InkField"; import { createSchema, makeInterface } from "../../../fields/Schema"; import { Cast, StrCast } from "../../../fields/Types"; import { VideoField } from "../../../fields/URLField"; -import { Utils, emptyFunction, returnOne, returnZero, OmitKeys, OmitKeys } from "../../../Utils"; +import { Utils, emptyFunction, returnOne, returnZero, OmitKeys } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ContextMenu } from "../ContextMenu"; -- cgit v1.2.3-70-g09d2 From d5e086e4c161e7d445f6d69faca8ffc7391ed3dd Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 9 Sep 2020 04:04:58 -0400 Subject: fixed doc decorations on selected treeview items to not overlap as much. fixed contentFitting's screentToLocalXf which wasn't wroking for nested tempaltes with native dimensions --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 23 +++++++++++++++++++++- src/client/util/HypothesisUtils.ts | 2 +- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/collections/CollectionSubView.tsx | 17 ++++++++++------ .../views/collections/CollectionTreeView.scss | 1 + .../views/collections/CollectionTreeView.tsx | 4 ++-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../views/nodes/ContentFittingDocumentView.scss | 2 ++ .../views/nodes/ContentFittingDocumentView.tsx | 21 ++++++++++---------- src/client/views/nodes/ImageBox.tsx | 4 ++-- src/client/views/nodes/WebBox.tsx | 8 +++++++- 12 files changed, 63 insertions(+), 27 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7e276747c..2f7b5a449 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1050,7 +1050,7 @@ export namespace DocUtils { }); } ctor = Docs.Create.WebDocument; - options = { ...options, _nativeWidth: 850, _nativeHeight: 962, _width: 500, _height: 566, title: path, }; + options = { ...options, _nativeWidth: 850, _width: 400, _height: 512, title: path, }; } return ctor ? ctor(path, options) : undefined; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7a1c193c1..82e80a394 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -245,6 +245,26 @@ export class CurrentUserUtils { removeDropProperties: new List(["dropAction"]), title: "detail view", icon: "window-maximize", system: true }); } + if (doc["template-button-simple"] === undefined) { + const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; + + const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)"); + const carousel = CarouselDocument([], { + title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10, + onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true + }); + + const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 }; + const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" }; + const detailView = Docs.Create.StackingDocument([carousel], { ...shared, ...detailViewOpts, system: true }); + detailView.isTemplateDoc = makeTemplate(detailView); + + doc["template-button-simple"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(detailView) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "simple view", icon: "window-maximize", system: true + }); + } const requiredTypes = [ doc["template-button-slides"] as Doc, @@ -252,6 +272,7 @@ export class CurrentUserUtils { doc["template-button-query"] as Doc, doc["template-mobile-button"] as Doc, doc["template-button-detail"] as Doc, + doc["template-button-simple"] as Doc, doc["template-button-link"] as Doc, doc["template-button-switch"] as Doc]; if (doc["template-buttons"] === undefined) { @@ -439,7 +460,7 @@ export class CurrentUserUtils { { _width: 250, _height: 250, title: "container", system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.emptyWebpage === undefined) { - doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.activeMobileMenu === undefined) { this.setupActiveMobileMenu(doc); diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index 4a5b52e1e..8cb523093 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -21,7 +21,7 @@ export namespace Hypothesis { export const getSourceWebDoc = async (uri: string) => { const result = await findWebDoc(uri); console.log(result ? "existing doc found" : "existing doc NOT found"); - return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found + return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found }; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e5c6f0aa9..abad53b6d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -647,8 +647,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> {closeIcon} - {titleArea} - {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal ? (null) : + {Object.keys(SelectionManager.SelectedDocuments()[0].props).includes("treeViewDoc") ? (null) : titleArea} + {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal || Object.keys(SelectionManager.SelectedDocuments()[0].props).includes("treeViewDoc") ? (null) : {`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}} placement="top">
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 81f3ddad2..aff7e8000 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -270,6 +270,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: return; } + console.log("Html = ", html); if (html) { if (FormattedTextBox.IsFragment(html)) { const href = FormattedTextBox.GetHref(html); @@ -353,14 +354,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: if (uriList || text) { if ((uriList || text).includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) { const url = (uriList || text).replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0]; - this.addDocument(Docs.Create.VideoDocument(url, { + console.log("Video URI = ", uriList); + console.log("Add:" + this.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5 - })); + }))); return; } // let matches: RegExpExecArray | null; @@ -381,27 +383,30 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: // } } if (uriList) { + ; + console.log("Web URI = ", uriList); const existingWebDoc = await Hypothesis.findWebDoc(uriList); if (existingWebDoc) { const alias = Doc.MakeAlias(existingWebDoc); alias.x = options.x; alias.y = options.y; alias._nativeWidth = 850; - alias._nativeHeight = 962; + alias._height = 512; alias._width = 400; this.addDocument(alias); } else { + console.log("Adding ..."); const newDoc = Docs.Create.WebDocument(uriList, { ...options, title: uriList.split("#annotations:")[0], _width: 400, - _height: 315, + _height: 512, _nativeWidth: 850, - _nativeHeight: 962, useCors: true }); + console.log(" ... " + newDoc.title); newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig) - this.addDocument(newDoc); + console.log(" ... " + this.addDocument(newDoc) + " " + newDoc.title); } return; } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index b0a791a21..e192f1760 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -109,6 +109,7 @@ .treeViewItem-border { display: flex; border-left: dashed 1px #00000042; + overflow: hidden; } .treeViewItem-header-editing, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7b3e95404..6c3fb56e2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -348,8 +348,8 @@ class TreeView extends React.Component { const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; return
this.props.addDocTab( - Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right")); + Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right")); cm.displayMenu(this._downX, this._downY); e.stopPropagation(); diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss index 50562951a..679073d44 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.scss +++ b/src/client/views/nodes/ContentFittingDocumentView.scss @@ -3,6 +3,8 @@ .contentFittingDocumentView { position: relative; display: flex; + width: 100%; + height: 100%; .contentFittingDocumentView-previewDoc { position: relative; diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index c0bcc0a6d..7cb27451f 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -6,7 +6,7 @@ import { Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnVal } from "../../../Utils"; +import { emptyFunction, returnVal, OmitKeys } from "../../../Utils"; import { dropActionType } from "../../util/DragManager"; import { CollectionView } from "../collections/CollectionView"; import { DocumentView, DocumentViewProps } from "../nodes/DocumentView"; @@ -34,8 +34,9 @@ export class ContentFittingDocumentView extends React.Component this.props.PanelHeight()) { - return (this.props.PanelHeight() / this.nativeHeight()) || 1; + return hscale || 1; } return wscale || 1; } @@ -47,7 +48,10 @@ export class ContentFittingDocumentView extends React.Component this.props.dontCenter ? this.props.ScreenToLocalTransform().scale(1 / this.contentScaling()) : this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling()); + @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); } + private getTransform = () => this.props.dontCenter ? + this.props.ScreenToLocalTransform().scale(this.childXf) : + this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(this.childXf); private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; } private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; } @@ -55,10 +59,7 @@ export class ContentFittingDocumentView extends React.Component 0.001 ? "auto" : this.props.PanelWidth(), - height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(), - }}> + return (
{!this.props.Document || !this.props.PanelWidth ? (null) : (
0.001 ? `${100 * this.nativeHeight() / this.nativeWidth() * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(), width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth() }}> - - (); private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + constructor(props: any) { + super(props); + this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 850)); + this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, this.Document[HeightSym]() / this.Document[WidthSym]() * 850)); + } + iframeLoaded = action((e: any) => { const iframe = this._iframeRef.current; if (iframe && iframe.contentDocument) { -- cgit v1.2.3-70-g09d2 From 51eea45ec6ee39cb83ee3b0780ad262b8b8b5dd8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 10 Sep 2020 20:10:58 -0400 Subject: made basic progressivized slides using TreeView --- src/client/documents/Documents.ts | 2 + src/client/util/CurrentUserUtils.ts | 4 ++ src/client/views/EditableView.tsx | 23 ++++++---- src/client/views/PropertiesView.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 13 +++++- .../views/collections/CollectionTreeView.tsx | 49 ++++++++++++++++++---- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 4 ++ .../collections/collectionFreeForm/MarqueeView.tsx | 24 +++++++---- src/client/views/nodes/DocumentView.tsx | 3 -- src/client/views/nodes/PresBox.tsx | 16 ++++--- 11 files changed, 105 insertions(+), 37 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2f7b5a449..f67887c09 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -130,6 +130,7 @@ export interface DocumentOptions { opacity?: number; defaultBackgroundColor?: string; _isBackground?: boolean; + "_isBackground-canClick"?: boolean; // a background document that you can still click on to edit its contents isLinkButton?: boolean; _columnWidth?: number; _fontSize?: string; @@ -195,6 +196,7 @@ export interface DocumentOptions { treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked treeViewTruncateTitleWidth?: number; + treeViewOutlineMode?: boolean; // whether slide should function as a text outline treeViewLockExpandedView?: boolean; // whether the expanded view can be changed treeViewDefaultExpandedView?: string; // default expanded view limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2f08aa928..b3567a4e8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -418,6 +418,9 @@ export class CurrentUserUtils { doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0; } + if (doc.emptySlide === undefined) { + doc.emptySlide = Docs.Create.TreeDocument([], { title: "slide", treeViewOutlineMode: true, "_isBackground-canClick": true, _backgroundColor: "transparent", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); + } if (doc.emptyComparison === undefined) { doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); } @@ -453,6 +456,7 @@ export class CurrentUserUtils { return [ { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, }, { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true }, + { 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, noviceMode: true }, { 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 }, diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 1b4b9a2be..cbbd78a20 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -18,11 +18,12 @@ export interface EditableProps { * @param value - The string entered by the user to set the value to * @returns `true` if setting the value was successful, `false` otherwise * */ - SetValue(value: string, shiftDown?: boolean): boolean; + SetValue(value: string, shiftDown?: boolean, enterKey?: boolean): boolean; OnFillDown?(value: string): void; OnTab?(shift?: boolean): void; + OnEmpty?(): void; /** * The contents to render when not editing @@ -93,13 +94,17 @@ export class EditableView extends React.Component { switch (e.key) { case "Tab": e.stopPropagation(); - this.finalizeEdit(e.currentTarget.value, e.shiftKey, false); - this.props.OnTab && this.props.OnTab(e.shiftKey); + this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, false); + this.props.OnTab?.(e.shiftKey); + break; + case "Backspace": + e.stopPropagation(); + if (!e.currentTarget.value) this.props.OnEmpty?.(); break; case "Enter": e.stopPropagation(); if (!e.ctrlKey) { - this.finalizeEdit(e.currentTarget.value, e.shiftKey, false); + this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true); } else if (this.props.OnFillDown) { this.props.OnFillDown(e.currentTarget.value); this._editing = false; @@ -130,10 +135,10 @@ export class EditableView extends React.Component { } @action - private finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean) { - if (this.props.SetValue(value, shiftDown)) { + private finalizeEdit(value: string, shiftDown: boolean, lostFocus: boolean, enterKey: boolean) { + if (this.props.SetValue(value, shiftDown, enterKey)) { this._editing = false; - this.props.isEditingCallback?.(false); + this.props.isEditingCallback?.(false,); } else { this._editing = false; this.props.isEditingCallback?.(false); @@ -164,7 +169,7 @@ export class EditableView extends React.Component { className: "editableView-input", onKeyDown: this.onKeyDown, autoFocus: true, - onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true), + onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false), onPointerDown: this.stopPropagation, onClick: this.stopPropagation, onPointerUp: this.stopPropagation, @@ -178,7 +183,7 @@ export class EditableView extends React.Component { onKeyDown={this.onKeyDown} autoFocus={true} onKeyPress={e => e.stopPropagation()} - onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)} + onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} style={{ display: this.props.display, fontSize: this.props.fontSize, minWidth: 20 }} placeholder={this.props.placeholder} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index d70f4b332..5a9402d70 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1040,7 +1040,7 @@ export class PropertiesView extends React.Component {
} {!selectedItem ? (null) :
{ this.openPresProgressivize = !this.openPresProgressivize; })} + onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)} style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>     Progressivize
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index e192f1760..3f437b799 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -30,6 +30,14 @@ padding-left: 0; } + .outline-bullet { + position: relative; + width: 15px; + color: $intermediate-color; + margin-top: 3px; + transform: scale(0.5); + } + .bullet { position: relative; width: 15px; @@ -106,11 +114,14 @@ cursor: pointer; } +.treeViewItem-border-outline, .treeViewItem-border { display: flex; - border-left: dashed 1px #00000042; overflow: hidden; } +.treeViewItem-border{ + border-left: dashed 1px #00000042; +} .treeViewItem-header-editing, .treeViewItem-header { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d2b86d3c6..a6975f517 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -181,22 +181,38 @@ class TreeView extends React.Component { fontStyle={style} fontSize={12} GetValue={() => StrCast(this.doc[key])} - SetValue={undoBatch((value: string) => { - Doc.SetInPlace(this.doc, key, value, false) || true; - Doc.SetInPlace(this.doc, "editTitle", undefined, false); + SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => { + if (this.props.treeView.doc.treeViewOutlineMode && enterKey) { + Doc.SetInPlace(this.doc, key, value, false); + const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + Doc.SetInPlace(doc, "editTitle", "*", false); + this.props.addDocument(doc); + doc.context = this.props.treeView.Document; + } else { + Doc.SetInPlace(this.doc, key, value, false) || true; + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + } })} OnFillDown={undoBatch((value: string) => { Doc.SetInPlace(this.doc, key, value, false); - const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); + const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); Doc.SetInPlace(this.doc, "editTitle", undefined, false); Doc.SetInPlace(doc, "editTitle", "*", false); - return this.props.addDocument(doc); + const added = this.props.addDocument(doc); + doc.context = this.props.treeView.Document; + return added; })} onClick={() => { SelectionManager.DeselectAll(); Doc.UserDoc().activeSelection = new List([this.doc]); return false; }} + OnEmpty={undoBatch(() => { + if (this.props.treeView.doc.treeViewOutlineMode) { + this.props.removeDoc?.(this.doc); + } + })} OnTab={undoBatch((shift?: boolean) => { shift ? this.props.outdentDocument?.() : this.props.indentDocument?.(); setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0); @@ -398,13 +414,25 @@ class TreeView extends React.Component { e.stopPropagation(); } + @computed get renderOutlineBullet() { + TraceMobx(); + return
+ {} +
; + } @computed get renderBullet() { TraceMobx(); const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; return
+ style={{ + color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), + opacity: checked === "unchecked" ? undefined : 0.4 + }}> {}
; } @@ -521,10 +549,10 @@ class TreeView extends React.Component { } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - {this.renderBullet} + {this.props.treeView.props.Document.treeViewOutlineMode ? this.renderOutlineBullet : this.renderBullet} {this.renderTitle}
-
+
{!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
@@ -617,6 +645,7 @@ class TreeView extends React.Component { Doc.AddDocToList(docs[i - 1], fieldKey, child); docs[i - 1].treeViewOpen = true; remove(child); + child.context = treeView.Document; } } }; @@ -627,6 +656,7 @@ class TreeView extends React.Component { Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false); parentCollectionDoc.treeViewOpen = true; remove(child); + child.context = treeView.Document; } }; const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => { @@ -729,8 +759,11 @@ export class CollectionTreeView extends CollectionSubView doAddDoc(doc)); + } else if (relativeTo === undefined) { + this.props.addDocument(doc); } else { doAddDoc(doc); + (doc instanceof Doc ? [doc] : doc).forEach(d => d.context = this.props.Document); } return true; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6939399e6..7084aba40 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -391,7 +391,7 @@ export class CollectionView extends Touchable diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 5ace82b3c..aa1852250 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -146,6 +146,10 @@ export class TabDocView extends React.Component { CollectionDockingView.AddSplit(curPres, "right"); } DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null)); + setTimeout(() => { + curPres._itemIndex = DocListCast(curPres.data).length - 1; + doc.treeViewOutlineMode && PresBox.Instance.progressivizeChild(null as any); + }, 100); } } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index deb7e68e8..ebad3bf45 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -116,15 +116,23 @@ export class MarqueeView extends React.Component { + // const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); + // if (ns.length === 1 && text.startsWith("http")) { + // this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer + // } else { + // this.pasteTable(ns, x, y); + // } + // }); + // e.stopPropagation(); + e.preventDefault(); - navigator.clipboard.readText().then(text => { - const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); - if (ns.length === 1 && text.startsWith("http")) { - this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer - } else { - this.pasteTable(ns, x, y); - } - }); + const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!; + slide.x = x; + slide.y = y; + this.props.addDocument(slide); + setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false)); e.stopPropagation(); } else if (!e.ctrlKey && !e.metaKey) { FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : ""; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7ef5eaf54..7b7c3578b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -593,9 +593,6 @@ export class DocumentView extends DocComponent(Docu if (CurrentUserUtils.ActiveDashboard === this.props.Document) { alert("Can't delete the active dashboard"); } else { - const selected = SelectionManager.SelectedDocuments().slice(); - SelectionManager.DeselectAll(); - selected.map(dv => dv.props.removeDocument?.(dv.props.Document)); this.props.removeDocument?.(this.props.Document); } } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 99fb5d2ce..79f1b7ddc 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1258,7 +1258,7 @@ export class PresBox extends ViewBoxBaseComponent
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
{this.stringType} selected -
+
Contents
Edit
@@ -1435,18 +1435,22 @@ export class PresBox extends ViewBoxBaseComponent } @action - progressivizeChild = (e: React.MouseEvent) => { - e.stopPropagation(); + progressivizeChild = (e?: React.MouseEvent) => { + e?.stopPropagation(); const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); if (!activeItem.presProgressivize) { targetDoc.editing = false; activeItem.presProgressivize = true; targetDoc.presProgressivize = true; targetDoc._currentFrame = 0; - docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true)); - targetDoc.lastFrame = docs.length - 1; + let count = 0; + const setupProgressivize = (doc: Doc) => { + CollectionFreeFormDocumentView.setupKeyframes([doc], count++, true); + targetDoc.treeViewOutlineMode && DocListCast(doc[Doc.LayoutFieldKey(doc)]).forEach(d => setupProgressivize(d)); + } + setupProgressivize(targetDoc); + targetDoc.lastFrame = count; } else { targetDoc.editProgressivize = false; activeItem.presProgressivize = false; -- cgit v1.2.3-70-g09d2 From 702242a07451fc89d176ffcd7cc881928b2bc23c Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 11 Sep 2020 17:28:08 -0400 Subject: changes to switch from 'pt' units to 'px' for fonts. fixxes to presentations for treeview outlines. --- solr-8.3.1/bin/solr-8983.pid | 2 +- src/client/util/CurrentUserUtils.ts | 17 +- src/client/util/DragManager.ts | 2 +- src/client/util/SettingsManager.tsx | 4 +- src/client/util/UndoManager.ts | 2 + src/client/views/MainView.tsx | 3 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/animationtimeline/Timeline.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 10 +- .../views/collections/CollectionTreeView.scss | 3 +- .../views/collections/CollectionTreeView.tsx | 295 ++++++++++++++------- src/client/views/nodes/DocumentView.tsx | 13 +- src/client/views/nodes/PresBox.tsx | 28 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 15 +- .../formattedText/ProsemirrorExampleTransfer.ts | 76 +++--- .../views/nodes/formattedText/RichTextMenu.tsx | 36 +-- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/fields/Doc.ts | 9 +- 18 files changed, 314 insertions(+), 209 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/solr-8.3.1/bin/solr-8983.pid b/solr-8.3.1/bin/solr-8983.pid index 1f77d281d..cfdfb83dc 100644 --- a/solr-8.3.1/bin/solr-8983.pid +++ b/solr-8.3.1/bin/solr-8983.pid @@ -1 +1 @@ -1731 +1750 diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20799fa26..c2c9892be 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -208,7 +208,7 @@ export class CurrentUserUtils { details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" ")); const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 }; - const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" }; + const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" }; const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true }; const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); @@ -240,7 +240,7 @@ export class CurrentUserUtils { }); const shared = { _chromeStatus: "disabled", _autoHeight: true, _xMargin: 0 }; - const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12pt" }; + const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" }; const detailView = Docs.Create.StackingDocument([carousel], { ...shared, ...detailViewOpts, system: true }); detailView.isTemplateDoc = makeTemplate(detailView); @@ -419,7 +419,12 @@ export class CurrentUserUtils { ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptySlide === undefined) { - doc.emptySlide = Docs.Create.TreeDocument([], { title: "slide", treeViewOutlineMode: true, _backgroundColor: "transparent", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); + const textDoc = Docs.Create.TextDocument("Slide", { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List(["system"]) }); + Doc.GetProto(textDoc).layout = CollectionView.LayoutString("data"); + Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); + Doc.GetProto(textDoc).data = new List([]); + FormattedTextBox.SelectOnLoad = textDoc[Id]; + doc.emptySlide = textDoc; } if (doc.emptyComparison === undefined) { doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); @@ -641,13 +646,13 @@ export class CurrentUserUtils { // Sets up the title of the button static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { ...opts, - dropAction: undefined, title: buttonTitle, _fontSize: "37pt", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", system: true + dropAction: undefined, title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", system: true }) as any as Doc // Sets up the description of the button static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { ...opts, - dropAction: undefined, title: "info", _fontSize: "25pt", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true + dropAction: undefined, title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, _chromeStatus: "disabled", backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true }) as any as Doc @@ -953,7 +958,7 @@ export class CurrentUserUtils { doc.activeArrowStart = StrCast(doc.activeArrowStart, ""); doc.activeArrowEnd = StrCast(doc.activeArrowEnd, ""); doc.activeDash = StrCast(doc.activeDash, "0"); - doc.fontSize = StrCast(doc.fontSize, "12pt"); + doc.fontSize = StrCast(doc.fontSize, "12px"); doc.fontFamily = StrCast(doc.fontFamily, "Arial"); doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9797f5488..367155c90 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -329,7 +329,7 @@ export namespace DragManager { dragLabel = document.createElement("div"); dragLabel.className = "dragManager-dragLabel"; dragLabel.style.zIndex = "100001"; - dragLabel.style.fontSize = "10pt"; + dragLabel.style.fontSize = "10px"; dragLabel.style.position = "absolute"; // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar dragDiv.appendChild(dragLabel); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 0a1890ca4..c9605c620 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -81,7 +81,7 @@ export class SettingsManager extends React.Component<{}> {
; const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; - const fontSizes = ["7pt", "8pt", "9pt", "10pt", "12pt", "14pt", "16pt", "18pt", "20pt", "24pt", "32pt", "48pt", "72pt"]; + const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"]; return
@@ -93,7 +93,7 @@ export class SettingsManager extends React.Component<{}> { - {fontSizes.map(size => )}
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index c7b7bb215..0f7ad6d0a 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -128,6 +128,7 @@ export namespace UndoManager { } export function StartBatch(batchName: string): Batch { + // console.log("Start " + batchCounter + " " + batchName); batchCounter++; if (batchCounter > 0 && currentBatch === undefined) { currentBatch = []; @@ -137,6 +138,7 @@ export namespace UndoManager { const EndBatch = action((cancel: boolean = false) => { batchCounter--; + // console.log("End " + batchCounter); if (batchCounter === 0 && currentBatch?.length) { if (!cancel) { undoStack.push(currentBatch); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 379577a25..371ee3960 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,6 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; +import * as far from '@fortawesome/free-regular-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; @@ -141,7 +142,7 @@ export class MainView extends React.Component { fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper, - fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, + fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle, fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5a9402d70..53261d8de 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -45,8 +45,8 @@ export class PropertiesView extends React.Component { @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; } @computed get selectedDocumentView() { + if (PresBox.Instance?._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0]; - if (PresBox.Instance && PresBox.Instance._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); return undefined; } @computed get isPres(): boolean { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 1131ec874..9269e6f33 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -382,10 +382,10 @@ export class Timeline extends React.Component { const ttime = `Total: ${this.toReadTime(this._time)}`; return (
-
+
{ctime}
-
+
{ttime}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index cb989d348..f3ed89c78 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -251,7 +251,7 @@ export class CollectionViewBaseChrome extends React.Component); @@ -528,16 +528,14 @@ export class CollectionFreeFormViewChrome extends React.Component { get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } - get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); } get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; } + @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; } @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @@ -122,12 +123,10 @@ class TreeView extends React.Component { return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc); } @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { + this.props.treeView.props.select(false); return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); } - @undoBatch @action removeDoc = (doc: Doc | Doc[]) => { - return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => - flg && Doc.RemoveDocFromList(this.doc, Doc.LayoutFieldKey(this.doc), doc), true); - } + @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc)); constructor(props: any) { super(props); @@ -144,10 +143,15 @@ class TreeView extends React.Component { ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc); } + componentWillUnmount() { + document.removeEventListener("pointermove", this.onDragMove, true); + } + onPointerEnter = (e: React.PointerEvent): void => { this.props.active(true) && Doc.BrushDoc(this.dataDoc); if (e.buttons === 1 && SnappingManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; + document.removeEventListener("pointermove", this.onDragMove, true); document.addEventListener("pointermove", this.onDragMove, true); } } @@ -171,6 +175,24 @@ class TreeView extends React.Component { e.stopPropagation(); } + public static makeTextBullet() { + const bullet = Docs.Create.TextDocument("", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List([Templates.Title.Layout]) }); + Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); + Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); + Doc.GetProto(bullet).data = new List([]); + Doc.SetInPlace(bullet, "editTitle", "*", false); + FormattedTextBox.SelectOnLoad = bullet[Id]; + return bullet; + } + + makeTextCollection = () => { + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + const bullet = TreeView.makeTextBullet(); + const added = this.props.addDocument(bullet); + bullet.context = this.props.treeView.Document; + return added; + } + editableView = (key: string, style?: string) => ( { fontSize={12} GetValue={() => StrCast(this.doc[key])} SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => { - if (this.props.treeView.doc.treeViewOutlineMode && enterKey) { + if (this.outlineMode && enterKey) { Doc.SetInPlace(this.doc, key, value, false); - const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); - Doc.SetInPlace(this.doc, "editTitle", undefined, false); - Doc.SetInPlace(doc, "editTitle", "*", false); - this.props.addDocument(doc); - doc.context = this.props.treeView.Document; + this.makeTextCollection(); } else { Doc.SetInPlace(this.doc, key, value, false) || true; Doc.SetInPlace(this.doc, "editTitle", undefined, false); } })} - OnFillDown={undoBatch((value: string) => { - Doc.SetInPlace(this.doc, key, value, false); - const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); - Doc.SetInPlace(this.doc, "editTitle", undefined, false); - Doc.SetInPlace(doc, "editTitle", "*", false); - const added = this.props.addDocument(doc); - doc.context = this.props.treeView.Document; - return added; - })} onClick={() => { SelectionManager.DeselectAll(); Doc.UserDoc().activeSelection = new List([this.doc]); @@ -240,8 +249,13 @@ class TreeView extends React.Component { const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); let addDoc = parentAddDoc; if (inside) { + const localAdd = (doc: Doc) => { + const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); + added && (doc.context = this.doc.context); + return added; + } addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce( - (flg: boolean, doc) => flg && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc), true) || parentAddDoc(doc); + (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc); } const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument; return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false); @@ -290,8 +304,12 @@ class TreeView extends React.Component { if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) { const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); - const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce( - (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true); + const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { + const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); + added && (doc.context = this.doc.context); + return added; + } + const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, @@ -328,18 +346,23 @@ class TreeView extends React.Component { rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20); @computed get renderContent() { TraceMobx(); const expandKey = this.treeViewExpandedView; if (["links", "annotations", this.fieldKey].includes(expandKey)) { const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey); - const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => - (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true); + const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { + const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true); + added && (doc.context = this.doc.context); + return added; + } + const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; const sortKey = `${this.fieldKey}-sortAscending`; return
    { - !this.props.treeView.Document.treeViewOutlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true))); + !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true))); e.stopPropagation(); }}> {!docs ? (null) : @@ -416,7 +439,7 @@ class TreeView extends React.Component { title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} onClick={this.bulletClick} style={{ opacity: NumCast(this.doc.opacity, 1) }}> - {} + {(this.doc.text as RichTextField)?.Text ? : (null)}
; } @computed get renderBullet() { @@ -442,7 +465,7 @@ class TreeView extends React.Component { return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); - onChildDoubleClick = () => (!this.props.treeView.props.Document.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); /** * Renders the EditableView title element for placement into the tree. */ @@ -456,11 +479,11 @@ class TreeView extends React.Component { onPointerDown={action(() => { if (this.treeViewOpen) { this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : - this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : + this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") : this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); + this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> @@ -488,7 +511,7 @@ class TreeView extends React.Component { PanelWidth={this.truncateTitleWidth} PanelHeight={returnZero} contextMenuItems={this.contextMenuItems} - opacity={this.props.treeView.props.Document.treeViewOutlineMode ? undefined : returnOne} + opacity={this.outlineMode ? undefined : returnOne} renderDepth={1} focus={returnTrue} parentActive={returnTrue} @@ -529,7 +552,67 @@ class TreeView extends React.Component { } } } else this._editMaxWidth = ""; - return this.doc.treeViewHideTitle || (this.props.treeView.props.Document.treeViewOutlineMode && this.doc.type !== DocumentType.COL) ? this.renderContent : + return this.doc.treeViewHideTitle || (this.outlineMode) ? + !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent : +
this.props.active(true) && SelectionManager.DeselectAll()} + onKeyDown={e => { + e.stopPropagation(); + e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete"); + e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab"); + e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet"); + e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); + }} + > +
{ + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } + }} + onPointerDown={e => { + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } + }} + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> + {this.outlineMode ? this.renderOutlineBullet : this.renderBullet} +
+ +
+
+ +
+ {!this.treeViewOpen ? (null) : this.renderContent} +
+
:
this.props.active(true) && SelectionManager.DeselectAll()}>
  • { @@ -546,11 +629,11 @@ class TreeView extends React.Component { } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - {this.props.treeView.props.Document.treeViewOutlineMode ? this.renderOutlineBullet : this.renderBullet} + {this.outlineMode ? this.renderOutlineBullet : this.renderBullet} {this.renderTitle}
    -
    - {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent} +
    + {!this.treeViewOpen ? (null) : this.renderContent}
  • ; @@ -639,9 +722,10 @@ class TreeView extends React.Component { const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1]; const fieldKey = fieldKeysub.split("\'")[1]; if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) { + remove(child); + FormattedTextBox.SelectOnLoad = child[Id]; Doc.AddDocToList(docs[i - 1], fieldKey, child); docs[i - 1].treeViewOpen = true; - remove(child); child.context = treeView.Document; } } @@ -650,9 +734,10 @@ class TreeView extends React.Component { if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) { const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1]; const fieldKey = fieldKeysub.split("\'")[1]; + remove(child); + FormattedTextBox.SelectOnLoad = child[Id]; Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false); parentCollectionDoc.treeViewOpen = true; - remove(child); child.context = treeView.Document; } }; @@ -713,6 +798,7 @@ export class CollectionTreeView extends CollectionSubView !docs.includes(v)); + SelectionManager.DeselectAll(); if (result.length !== value.length) { targetDataDoc[this.props.fieldKey] = new List(result); return true; @@ -785,45 +872,6 @@ export class CollectionTreeView extends CollectionSubView this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); } - !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ - description: "Buxton Layout", icon: "eye", event: () => { - const { ImageDocument, PdfDocument } = Docs.Create; - const { Document } = this.props; - const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; - const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactory, Doc, null); - const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work? - heroView.proto!.layout = ImageBox.LayoutString("hero"); - heroView._showTitle = "title"; - heroView._showTitleHover = "titlehover"; - - const fallback = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target - let pdfContent: string; - this.childDocs?.map(d => { - DocListCast(d.data).map((img, i) => { - const caption = (d.captions as any)[i]; - if (caption) { - Doc.GetProto(img).caption = caption; - Doc.GetProto(img).doubleClickView = (pdfContent = StrCast(img.additionalMedia_pdfs)) ? PdfDocument(pdfContent, { title: pdfContent }) : fallback; - } - }); - Doc.GetProto(d).type = "buxton"; - Doc.GetProto(d).proto = heroView; // all devices "are" heroViews that share the same layout & defaults. Seems better than making them all be independent and copy a layout string // .layout = ImageBox.LayoutString("hero"); - }); - - const iconBuxtonView = ImageDocument(fallbackImg, { title: "hero", _width: 60, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); - iconBuxtonView.isTemplateDoc = makeTemplate(iconBuxtonView, true, "icon_buxton"); - Doc.UserDoc()["template-icon-view-buxton"] = new PrefetchProxy(iconBuxtonView); - const tempIcons = Doc.GetProto(Cast(Doc.UserDoc()["template-icons"], Doc, null)); - Doc.AddDocToList(tempIcons, "data", iconBuxtonView); - - Document.childLayoutTemplate = heroView; - Document.childClickedOpenTemplateView = new PrefetchProxy(detailView); - Document._viewType = CollectionViewType.Time; - Document.forceActive = true; - Document._pivotField = "company"; - Document.childDropAction = "alias"; - } - }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ @@ -846,13 +894,74 @@ export class CollectionTreeView extends CollectionSubView; } - onChildClick = () => { - return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); + @undoBatch + makeTextCollection = action((childDocs: Doc[]) => { + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + const bullet = TreeView.makeTextBullet(); + bullet.context = this.doc; + this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true); + setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); + }) + + editableTitle(childDocs: Doc[]) { + return StrCast(this.dataDoc.title)} + SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => { + if (this.props.Document.treeViewOutlineMode && enter) { + this.makeTextCollection(childDocs) + } + return Doc.SetInPlace(this.dataDoc, "title", value, false); + })} />; } - _isChildActive = false; - whenActiveChanged = (isActive: boolean) => { - this.props.whenActiveChanged(this._isChildActive = isActive); + + + rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.PanelWidth() - 20); + rtfOutlineHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2); + titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20)); + documentTitle = (childDocs: Doc[]) => { + return
    { + e.stopPropagation(); + e.key === "Enter" && this.makeTextCollection(childDocs) + }}> + +
    } + + onChildClick = () => { return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); } + whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); } active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; render() { TraceMobx(); @@ -882,21 +991,7 @@ export class CollectionTreeView extends CollectionSubView this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> - {hideTitle ? (null) : StrCast(this.dataDoc.title)} - SetValue={undoBatch((value: string) => { - if (this.props.Document.treeViewOutlineMode) { - const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); - Doc.SetInPlace(doc, "editTitle", "*", false); - this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); - } - return Doc.SetInPlace(this.dataDoc, "title", value, false); - })} />} + {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)} {this.doc.allowClear ? this.renderClearButton : (null)}
      {childElements}
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7b7c3578b..4b740d3de 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,6 +40,7 @@ import { RadialMenu } from './RadialMenu'; import { TaskCompletionBox } from './TaskCompletedBox'; import React = require("react"); import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { RichTextField } from '../../../fields/RichTextField'; export type DocFocusFunc = () => boolean; @@ -269,6 +270,9 @@ export class DocumentView extends DocComponent(Docu } onKeyDown = (e: React.KeyboardEvent) => { + if (this.rootDoc._singleLine && ((e.key === "Backspace" && this.dataDoc.text && !(this.dataDoc.text as RichTextField)?.Text) || ["Tab", "Enter"].includes(e.key))) { + return; + } if (e.altKey && !(e.nativeEvent as any).StopPropagationForReal) { (e.nativeEvent as any).StopPropagationForReal = true; // e.stopPropagation() doesn't seem to work... e.stopPropagation(); @@ -624,6 +628,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { + if (this.props.LayoutTemplateString) return; if (this.props.Document === CurrentUserUtils.ActiveDashboard) { if ((e.target as any)?.closest?.("*.lm_content")) { alert("You can't perform this move most likely because you don't have permission to modify the destination."); @@ -901,7 +906,7 @@ export class DocumentView extends DocComponent(Docu layoutKey={this.finalLayoutKey} /> {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} {/* {this.allAnchors} */} - {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) : + {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || (!this.isSelected() && this.layoutDoc.hideLinkButton) || this.props.dontRegisterView ? (null) : }
    ); @@ -1045,7 +1050,7 @@ export class DocumentView extends DocComponent(Docu const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor; - const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document); + const fullDegree = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString const borderRounding = this.layoutDoc.borderRounding; const localScale = fullDegree; const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ? @@ -1059,8 +1064,8 @@ export class DocumentView extends DocComponent(Docu id={this.props.Document[Id]} ref={this._mainCont} onKeyDown={this.onKeyDown} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={action(() => { Doc.BrushDoc(this.props.Document); })} - onPointerLeave={action((e: React.PointerEvent) => { + onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))} + onPointerLeave={action(e => { let entered = false; const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); for (let child: any = target; child; child = child?.parentElement) { diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 79f1b7ddc..b23dffe80 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1129,13 +1129,13 @@ export class PresBox extends ViewBoxBaseComponent y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20; } let doc = undefined; - const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", }); - const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" }); - const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" }); - const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" }); - const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" }); - const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" }); - const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" }); + const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24px", }); + const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16px" }); + const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20px" }); + const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24px" }); + const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14px" }); + const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14px" }); + const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14px" }); switch (layout) { case 'blank': doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y }); @@ -1227,8 +1227,9 @@ export class PresBox extends ViewBoxBaseComponent const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; let type: string = ''; + const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;// bcz: Argh .. .need a better way to identify a slide doc if (activeItem) { - switch (targetDoc.type) { + switch (effectiveType) { case DocumentType.PDF: type = "PDF"; break; case DocumentType.RTF: type = "Text node"; break; case DocumentType.COL: type = "Collection"; break; @@ -1248,8 +1249,9 @@ export class PresBox extends ViewBoxBaseComponent @computed get progressivizeDropdown() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; + const activeItem = this.activeItem; + const targetDoc = this.targetDoc; + const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type; if (activeItem && targetDoc) { const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black"; const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black"; @@ -1258,7 +1260,7 @@ export class PresBox extends ViewBoxBaseComponent
    e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
    {this.stringType} selected -
    +
    Contents
    Edit
    @@ -1274,11 +1276,11 @@ export class PresBox extends ViewBoxBaseComponent
    {this.viewedColorPicker} - {/*
    + {/*
    Zoom
    Edit
    */} -
    +
    Scroll
    Edit
    diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 97d023673..3be02aa92 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -988,7 +988,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (documentId) { exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc); } - UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls); + exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls); } updateState = (exportState: Opt, dataDoc: Doc) => { @@ -1171,6 +1171,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } componentWillUnmount() { + this.endUndoTypingBatch(); Object.values(this._disposers).forEach(disposer => disposer?.()); this._editorView?.destroy(); } @@ -1417,7 +1418,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } _lastTimedMark: Mark | undefined = undefined; - onKeyPress = (e: React.KeyboardEvent) => { + onKeyDown = (e: React.KeyboardEvent) => { + // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container) + if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) { + return; + } if (e.altKey) { e.preventDefault(); return; @@ -1502,8 +1507,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) { setTimeout(() => FormattedTextBoxComment.Hide(), 0); } - const selPad = this.props.isSelected() ? -10 : 0; - const selclass = this.props.isSelected() ? "-selected" : ""; + const selPad = this.props.isSelected() && !this.layoutDoc._singleLine ? -10 : 0; + const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : ""; return (
    this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)} diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index c6bacc1a8..674b63e77 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,11 +7,13 @@ 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 } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast } 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"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -45,6 +47,29 @@ export function buildKeymap>(schema: S, props: any, mapKey keys[key] = cmd; } + /// bcz; Argh!! replace with an onEnter func that conditionally handles Enter + const addTextBox = (below: boolean, force?: boolean) => { + if (props.LayoutTemplateString) return true; + const layoutDoc = props.Document; + const originalDoc = layoutDoc.rootDocument || layoutDoc; + if (force || props.Document._singleLine) { + const layoutKey = StrCast(originalDoc.layoutKey); + const newDoc = Doc.MakeCopy(originalDoc, true); + const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)]; + newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; + if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10; + else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10; + if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { + newDoc[layoutKey] = originalDoc[layoutKey]; + } + Doc.GetProto(newDoc).text = undefined; + FormattedTextBox.SelectOnLoad = newDoc[Id]; + props.addDocument(newDoc); + return true; + } + return false; + } + //History commands bind("Mod-z", undo); bind("Shift-Mod-z", redo); @@ -66,6 +91,11 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Ctrl-i", wrapInList(schema.nodes.ordered_list)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { + /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); + if (props.Document._singleLine) { + if (!props.LayoutTemplateString) return addTextBox(false, true); + 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()); @@ -89,6 +119,8 @@ 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; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { @@ -136,47 +168,11 @@ export function buildKeymap>(schema: S, props: any, mapKey return tx; }; - const addTextOnRight = (force: boolean) => { - const layoutDoc = props.Document; - const originalDoc = layoutDoc.rootDocument || layoutDoc; - if (force || props.Document._singleLine) { - const layoutKey = StrCast(originalDoc.layoutKey); - const newDoc = Doc.MakeCopy(originalDoc, true); - newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined; - newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10; - if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { - newDoc[layoutKey] = originalDoc[layoutKey]; - } - Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; - props.addDocument(newDoc); - return true; - } - return false; - }; - //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { - return addTextOnRight(true); - }); + bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => 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) => { - const layoutDoc = props.Document; - const originalDoc = layoutDoc.rootDocument || layoutDoc; - if (originalDoc instanceof Doc) { - const layoutKey = StrCast(originalDoc.layoutKey); - const newDoc = Doc.MakeCopy(originalDoc, true); - newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined; - newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10; - if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { - newDoc[layoutKey] = originalDoc[layoutKey]; - } - Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; - props.addDocument(newDoc); - } - }); + bind("Ctrl-Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => addTextBox(true, true)); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { @@ -199,7 +195,7 @@ export function buildKeymap>(schema: S, props: any, mapKey //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { - if (addTextOnRight(false)) return true; + if (addTextBox(true, false)) 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; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 307238ea1..da515aa5a 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -78,21 +78,21 @@ export class RichTextMenu extends AntimodeMenu { runInAction(() => this.Pinned = true); this.fontSizeOptions = [ - { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize }, + { 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: "13pt", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option + { 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 = [ @@ -177,7 +177,7 @@ export class RichTextMenu extends AntimodeMenu { this.activeListType = this.getActiveListStyle(); this.activeAlignment = this.getActiveAlignment(); this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various"; - this.activeFontSize = !activeSizes.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "..."; + this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "..."; this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "..."; this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "..."; @@ -255,7 +255,7 @@ export class RichTextMenu extends AntimodeMenu { marks.forEach(m => { m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); - m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt"); + m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px"); m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); }); } @@ -384,7 +384,7 @@ export class RichTextMenu extends AntimodeMenu { 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() + "pt"); break; + case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break; } } else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown"); @@ -425,7 +425,7 @@ export class RichTextMenu extends AntimodeMenu { changeFontSize = (mark: Mark, view: EditorView) => { if ((this.view?.state.selection.$from.pos || 0) < 2) { - this.TextView.layoutDoc._fontSize = mark.attrs.fontSize; + this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize; } const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }); this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 5c0505909..02f9c6268 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -90,7 +90,7 @@ export class RichTextRules { textDoc.inlineTextCount = numInlines + 1; const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" }); + const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" }); textDocInline.title = inlineFieldKey; // give the annotation its own title textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 08d949b5e..be62249c6 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -183,7 +183,7 @@ export class Doc extends RefField { this.___fields = value; for (const key in value) { const field = value[key]; - field && (this.__fieldKeys[key] = true); + (field !== undefined) && (this.__fieldKeys[key] = true); if (!(field instanceof ObjectField)) continue; field[Parent] = this[Self]; field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); @@ -875,12 +875,7 @@ export namespace Doc { } export function isBrushedHighlightedDegree(doc: Doc) { - if (Doc.IsHighlighted(doc)) { - return 6; - } - else { - return Doc.IsBrushedDegree(doc); - } + return Doc.IsHighlighted(doc) ? 6 : Doc.IsBrushedDegree(doc); } export class DocBrush { -- cgit v1.2.3-70-g09d2 From 93b72ab7818e48592e9d42d092fa91b4ebedd49e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 12 Sep 2020 16:22:09 -0400 Subject: fixed sidebar views in text boxes to work in treeview outlines. fixed context menus to select docs again. fixed linkAnchors to show up when isLinkButton is true but doc is selcted. changed nudging to be ignore in annotation overlays --- src/client/util/SelectionManager.ts | 4 +- src/client/views/PreviewCursor.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 19 ++++++++ .../views/collections/CollectionTreeView.tsx | 21 +++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 4 +- .../nodes/formattedText/FormattedTextBox.scss | 30 +++---------- .../views/nodes/formattedText/FormattedTextBox.tsx | 51 +++++++++++----------- 9 files changed, 68 insertions(+), 72 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 730db4f29..7795a4a59 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -73,8 +73,8 @@ export namespace SelectionManager { // computed functions, such as used in IsSelected generate errors if they're called outside of a // reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature // to avoid unnecessary mobx invalidations when running inside a reaction. - export function IsSelected(doc: DocumentView, outsideReaction?: boolean): boolean { - return outsideReaction ? + export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean { + return !doc ? false : outsideReaction ? manager.SelectedDocuments.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get() computedFn(function isSelected(doc: DocumentView) { // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed return manager.SelectedDocuments.get(doc) ? true : false; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 0a4d46c1f..7575aa430 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -22,7 +22,7 @@ export class PreviewCursor extends React.Component<{}> { static _getTransform: () => Transform; static _addDocument: (doc: Doc | Doc[]) => void; static _addLiveTextDoc: (doc: Doc) => void; - static _nudge: (x: number, y: number) => boolean; + static _nudge?: (x: number, y: number) => boolean; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; constructor(props: any) { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 09a46c30f..feeb04134 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -103,6 +103,15 @@ padding-bottom: 2px; } +.treeViewItem-container-active { + z-index: 100; + position: relative;; + .formattedTextbox-sidebar { + background-color: #ffff001f !important; + height: 500px !important; + } +} + .treeViewItem-openRight { display: none; height: 17px; @@ -128,6 +137,16 @@ border: transparent 1px solid; display: flex; align-items: center; + ::-webkit-scrollbar { + display: none; + } + .formattedTextBox-cont { + .formattedTextbox-sidebar { + overflow: visible !important; + border-left: unset; + } + overflow: visible !important; + } .editableView-container-editing-oneLine { min-width: 15px; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 52e3c86b8..276e0b873 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,6 +32,7 @@ import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextField } from '../../../fields/RichTextField'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; +import { DocumentManager } from '../../util/DocumentManager'; export interface TreeViewProps { document: Doc; @@ -176,7 +177,7 @@ class TreeView extends React.Component { } public static makeTextBullet() { - const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List([Templates.Title.Layout]) }); + const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 500, _height: 10, templates: new List([Templates.Title.Layout]) }); Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); Doc.GetProto(bullet).data = new List([]); @@ -552,9 +553,10 @@ class TreeView extends React.Component { } } } else this._editMaxWidth = ""; + const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc)); return this.doc.treeViewHideTitle || (this.outlineMode) ? !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent : -
    this.props.active(true) && SelectionManager.DeselectAll()} +
    this.props.active(true) && SelectionManager.DeselectAll()} onKeyDown={e => { e.stopPropagation(); e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete"); @@ -563,18 +565,9 @@ class TreeView extends React.Component { e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); }} > -
    { - if (this.props.active(true)) { - e.stopPropagation(); - e.preventDefault(); - } - }} - onPointerDown={e => { - if (this.props.active(true)) { - e.stopPropagation(); - e.preventDefault(); - } - }} +
    { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} + onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 543798b7e..843314c03 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -792,7 +792,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay) return; + if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay || this.props.Document.treeViewOutlineMode) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -1369,7 +1369,8 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) { // bcz: this isn't ideal, but want to try it out... + if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || + this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out... this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(), NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "transform 500ms", true); this._nudgeTime = Date.now(); @@ -1381,7 +1382,7 @@ export class CollectionFreeFormView extends CollectionSubView void; addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; - nudge: (x: number, y: number) => boolean; + nudge?: (x: number, y: number) => boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 147745d93..b372f3691 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -835,7 +835,7 @@ export class DocumentView extends DocComponent(Docu e?.stopPropagation(); // DocumentViews should stop propagation of this event } cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); - this.isSelected(true) && SelectionManager.SelectDoc(this, false); + !this.isSelected(true) && SelectionManager.SelectDoc(this, false); }); } @@ -912,7 +912,7 @@ export class DocumentView extends DocComponent(Docu layoutKey={this.finalLayoutKey} /> {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} {/* {this.allAnchors} */} - {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || (!this.isSelected() && this.layoutDoc.hideLinkButton) || this.props.dontRegisterView ? (null) : + {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) : }
    ); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 160f4ba72..847b1bb30 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -45,10 +45,11 @@ width: 100%; height: 100%; } - + .formattedTextBox-sidebar-handle { position: absolute; - top: calc(50% - 17.5px); + 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; @@ -56,11 +57,6 @@ cursor:grabbing; } -.formattedTextBox-cont>.formattedTextBox-sidebar-handle { - right: 0; - left: unset; -} - .formattedTextBox-sidebar, .formattedTextBox-sidebar-inking { border-left: dashed 1px black; @@ -73,11 +69,6 @@ .collectionfreeformview-container { position: relative; } - - >.formattedTextBox-sidebar-handle { - right: unset; - left: -5; - } } .formattedTextBox-sidebar-inking { @@ -396,10 +387,11 @@ footnote::after { width: 100%; height: 100%; } - + .formattedTextBox-sidebar-handle { position: absolute; - top: calc(50% - 17.5px); + 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; @@ -407,11 +399,6 @@ footnote::after { cursor:grabbing; } - .formattedTextBox-cont>.formattedTextBox-sidebar-handle { - right: 0; - left: unset; - } - .formattedTextBox-sidebar, .formattedTextBox-sidebar-inking { border-left: dashed 1px black; @@ -423,11 +410,6 @@ footnote::after { .collectionfreeformview-container { position: relative; } - - >.formattedTextBox-sidebar-handle { - right: unset; - left: -5; - } } .formattedTextBox-sidebar-inking { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 29d9283e7..a79e70017 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1500,7 +1500,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } - @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); } + @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "20%"); } sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0); @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } @@ -1569,32 +1569,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }} />
    - {!this.layoutDoc._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ? -
    : + {!this.layoutDoc._showSidebar ? (null) : <>
    - - -
    -
    } + {this.sidebarWidthPercent === "0%" ? (null) : + 1000 : this.props.PanelHeight} + PanelWidth={this.sidebarWidth} + scaleField={this.annotationKey + "-scale"} + annotationsKey={this.annotationKey} + isAnnotationOverlay={true} + focus={this.props.focus} + isSelected={this.props.isSelected} + select={emptyFunction} + active={this.annotationsActive} + ContentScaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + CollectionView={undefined} + ScreenToLocalTransform={this.sidebarScreenToLocal} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} /> + } +
    + {this.props.isSelected() ?
    : (null)} + } {!this.layoutDoc._showAudio ? (null) :
    this._recording = !this._recording)} > Date: Mon, 14 Sep 2020 11:22:22 -0400 Subject: fixed userdoc tree view layout issues caused by outline mode. added treeViewHideHeader to avoid hackiness of overlaoding treeVewHideTitle --- src/client/documents/Documents.ts | 3 ++- src/client/views/collections/CollectionTreeView.scss | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 6 +++--- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/client/views/nodes/formattedText/FormattedTextBox.tsx | 7 ++++--- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8fe55b9a6..abe50e011 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -190,7 +190,8 @@ export interface DocumentOptions { _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view - treeViewHideTitle?: boolean; // whether to hide the top document of a tree view + 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. treeViewOpen?: boolean; // whether this document is expanded in a tree view treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index feeb04134..aaf17153a 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -136,8 +136,8 @@ .treeViewItem-header { border: transparent 1px solid; display: flex; - align-items: center; - ::-webkit-scrollbar { + //align-items: center; + ::-webkit-scrollbar { display: none; } .formattedTextBox-cont { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 47d4aad94..ff3c38a43 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -177,7 +177,7 @@ class TreeView extends React.Component { } public static makeTextBullet() { - const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List([Templates.Title.Layout]) }); + const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, treeViewHideHeader: true, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10, templates: new List([Templates.Title.Layout]) }); Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); Doc.GetProto(bullet).data = new List([]); @@ -554,7 +554,7 @@ class TreeView extends React.Component { } } else this._editMaxWidth = ""; const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc)); - return this.doc.treeViewHideTitle || (this.outlineMode) ? + return this.doc.treeViewHideHeader ? !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
    this.props.active(true) && SelectionManager.DeselectAll()} onKeyDown={e => { @@ -565,7 +565,7 @@ class TreeView extends React.Component { e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); }} > -
    { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 843314c03..60e347786 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1233,6 +1233,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); + Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 668b84903..31d471571 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -616,15 +616,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const appearance = cm.findByDescription("Appearance..."); const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" }); - this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); + // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); appearanceItems.push({ - description: "Convert to be a template style", event: () => { + description: "Make Default Layout", event: () => { if (!this.layoutDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); - } else { + } else if (!this.rootDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; this.rootDoc.layout = (this.layoutDoc as Doc).layout as string; @@ -640,6 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null); }, 10); } + Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc); Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc); }, icon: "eye" }); -- cgit v1.2.3-70-g09d2 From f62fb0f5f90eee7c946deaa3a6e856b76e88f9f1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 18 Sep 2020 13:17:07 -0400 Subject: cleaned up warnings and split collection tree view into Tree View a --- src/Utils.ts | 3 +- src/client/views/DocumentButtonBar.tsx | 4 +- src/client/views/EditableView.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 112 --- .../views/collections/CollectionTreeView.tsx | 810 +-------------------- src/client/views/collections/TreeView.scss | 115 +++ src/client/views/collections/TreeView.tsx | 760 +++++++++++++++++++ src/client/views/nodes/FilterBox.tsx | 10 + src/client/views/nodes/PresBox.tsx | 27 +- .../views/presentationview/PresElementBox.tsx | 2 +- 10 files changed, 921 insertions(+), 924 deletions(-) create mode 100644 src/client/views/collections/TreeView.scss create mode 100644 src/client/views/collections/TreeView.tsx (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/Utils.ts b/src/Utils.ts index 7dff1ac55..cc7ee9537 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -518,7 +518,8 @@ export function clearStyleSheetRules(sheet: any) { return false; } -export function simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number, rightClick = true) { +export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) { + if (!element) return; ["pointerdown", "pointerup"].map(event => element.dispatchEvent( new PointerEvent(event, { view: window, diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 575ebaf3b..c5fd3c777 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -184,7 +184,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV @computed get pinButton() { const targetDoc = this.view0?.props.Document; - let isPinned = targetDoc && Doc.isDocPinned(targetDoc); + const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) :
    {"Pin to presentation"}
    }>
    (DocumentV this.props.views()[0]?.select(false); this._tooltipOpen = false; setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); - }) + }); onAliasButtonMoved = () => { if (this._dragRef.current) { const dragDocView = this.view0!; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index cbbd78a20..d35271ffd 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -204,7 +204,7 @@ export class EditableView extends React.Component { setTimeout(() => this.props.autosuggestProps?.resetValue(), 0); return this.props.contents instanceof ObjectField ? (null) :
    { this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()} diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index aaf17153a..20bfc0e9d 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -30,24 +30,6 @@ padding-left: 0; } - .outline-bullet { - position: relative; - width: 15px; - color: $intermediate-color; - transform: scale(0.5); - display: inline-block; - } - - .bullet { - position: relative; - width: 15px; - color: $intermediate-color; - margin-top: 3px; - transform: scale(1.3, 1.3); - border: #80808030 1px solid; - border-radius: 4px; - } - .editableView-container { font-weight: bold; } @@ -101,98 +83,4 @@ padding-left: 3px; padding-right: 3px; padding-bottom: 2px; -} - -.treeViewItem-container-active { - z-index: 100; - position: relative;; - .formattedTextbox-sidebar { - background-color: #ffff001f !important; - height: 500px !important; - } -} - -.treeViewItem-openRight { - display: none; - height: 17px; - width: 15px; -} - -.treeViewItem-openRight:hover { - background: #797777; - cursor: pointer; -} - -.treeViewItem-border-outline, -.treeViewItem-border { - display: flex; - overflow: hidden; -} -.treeViewItem-border{ - border-left: dashed 1px #00000042; -} - -.treeViewItem-header-editing, -.treeViewItem-header { - border: transparent 1px solid; - display: flex; - //align-items: center; - ::-webkit-scrollbar { - display: none; - } - .formattedTextBox-cont { - .formattedTextbox-sidebar { - overflow: visible !important; - border-left: unset; - } - overflow: visible !important; - } - - .editableView-container-editing-oneLine { - min-width: 15px; - } - - .documentView-node-topmost { - width: unset; - } - - >svg { - display: none; - } - -} - -.treeViewItem-header:hover { - .collectionTreeView-keyHeader { - display: inherit; - } - - >svg { - display: inherit; - } - - .treeViewItem-openRight { - display: inline-block; - height: 17px; - width: 15px; - - // display: inline; - svg { - display: block; - padding: 0px; - margin-left: 3px; - } - } -} - -.treeViewItem-header-above { - border-top: black 1px solid; -} - -.treeViewItem-header-below { - border-bottom: black 1px solid; -} - -.treeViewItem-header-inside { - border: black 1px solid; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 19b8400c8..46f18099a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,791 +1,29 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from "mobx"; +import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; -import { Document, listSpec } from '../../../fields/Schema'; -import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; -import { DocumentType } from "../../documents/DocumentTypes"; -import { SnappingManager } from '../../util/SnappingManager'; +import { Document } from '../../../fields/Schema'; +import { ScriptField } from '../../../fields/ScriptField'; +import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils'; +import { DocUtils } from '../../documents/Documents'; import { DragManager, dropActionType } from "../../util/DragManager"; -import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; -import { Transform } from '../../util/Transform'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; -import { DocumentView } from '../nodes/DocumentView'; -import { KeyValueBox } from '../nodes/KeyValueBox'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; -import { CollectionViewType, CollectionView } from './CollectionView'; +import { TreeView } from "./TreeView"; import React = require("react"); -import { TraceMobx } from '../../../fields/util'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { RichTextField } from '../../../fields/RichTextField'; -import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { DocumentManager } from '../../util/DocumentManager'; - -export interface TreeViewProps { - document: Doc; - dataDoc?: Doc; - containingCollection: Doc; - prevSibling?: Doc; - renderDepth: number; - removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; - moveDocument: DragManager.MoveFunction; - dropAction: dropActionType; - addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean; - pinToPres: (document: Doc) => void; - panelWidth: () => number; - panelHeight: () => number; - ChromeHeight: undefined | (() => number); - addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; - indentDocument?: () => void; - outdentDocument?: () => void; - ScreenToLocalTransform: () => Transform; - backgroundColor?: (doc: Opt, renderDepth: number) => string | undefined; - outerXf: () => { translateX: number, translateY: number }; - treeView: CollectionTreeView; - parentKey: string; - active: (outsideReaction?: boolean) => boolean; - treeViewHideHeaderFields: () => boolean; - treeViewPreventOpen: boolean; - renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle - onCheckedClick?: () => ScriptField; - onChildClick?: () => ScriptField; - ignoreFields?: string[]; - firstLevel: boolean; - whenActiveChanged: (isActive: boolean) => void; -} - -@observer -/** - * Renders a treeView of a collection of documents - * - * special fields: - * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden - * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) - * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree - */ -class TreeView extends React.Component { - private _editTitleScript: (() => ScriptField) | undefined; - private _openScript: (() => ScriptField) | undefined; - private _header?: React.RefObject = React.createRef(); - private _treedropDisposer?: DragManager.DragDropDisposer; - private _dref = React.createRef(); - private _tref = React.createRef(); - private _docRef = React.createRef(); - private _uniqueId = Utils.GenerateGuid(); - private _editMaxWidth: number | string = 0; - - get doc() { return this.props.document; } - get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } - get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive - get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } - get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); } - get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } - @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state - set treeViewOpen(c: boolean) { - if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; - else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; - } - @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; } - @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } - @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } - @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } - @computed get dataDoc() { return this.doc[DataSym]; } - @computed get layoutDoc() { return Doc.Layout(this.doc); } - @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } - childDocList(field: string) { - const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; - return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field - (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields - DocListCastOrNull(this.doc[field])); // otherwise use the document's data field - } - @computed get childDocs() { return this.childDocList(this.fieldKey); } - @computed get childLinks() { return this.childDocList("links"); } - @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } - @computed get boundsOfCollectionDocument() { - return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined : - Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey])); - } - - @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right"); - @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { - return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc); - } - @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { - this.props.treeView.props.select(false); - return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); - } - @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc)); - - constructor(props: any) { - super(props); - const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); - const openScript = ScriptField.MakeScript(`openOnRight(self)`); - const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`); - this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript); - this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined; - if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); - } - - protected createTreeDropTarget = (ele: HTMLDivElement) => { - this._treedropDisposer?.(); - ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc); - } - - componentWillUnmount() { - document.removeEventListener("pointermove", this.onDragMove, true); - document.removeEventListener("pointermove", this.onDragUp, true); - } - - onDragUp = (e: PointerEvent) => { - document.removeEventListener("pointerup", this.onDragUp, true); - document.removeEventListener("pointermove", this.onDragMove, true); - } - onPointerEnter = (e: React.PointerEvent): void => { - this.props.active(true) && Doc.BrushDoc(this.dataDoc); - if (e.buttons === 1 && SnappingManager.GetIsDragging()) { - this._header!.current!.className = "treeViewItem-header"; - document.removeEventListener("pointermove", this.onDragMove, true); - document.addEventListener("pointermove", this.onDragMove, true); - document.removeEventListener("pointerup", this.onDragUp, true); - document.addEventListener("pointerup", this.onDragUp, true); - } - } - onPointerLeave = (e: React.PointerEvent): void => { - Doc.UnBrushDoc(this.dataDoc); - if (this._header?.current?.className !== "treeViewItem-header-editing") { - this._header!.current!.className = "treeViewItem-header"; - } - document.removeEventListener("pointerup", this.onDragUp, true); - document.removeEventListener("pointermove", this.onDragMove, true); - } - onDragMove = (e: PointerEvent): void => { - Doc.UnBrushDoc(this.dataDoc); - const pt = [e.clientX, e.clientY]; - const rect = this._header!.current!.getBoundingClientRect(); - const before = pt[1] < rect.top + rect.height / 2; - const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); - this._header!.current!.className = "treeViewItem-header"; - if (inside) this._header!.current!.className += " treeViewItem-header-inside"; - else if (before) this._header!.current!.className += " treeViewItem-header-above"; - else if (!before) this._header!.current!.className += " treeViewItem-header-below"; - e.stopPropagation(); - } - - public static makeTextBullet() { - const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); - Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); - Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); - Doc.GetProto(bullet).data = new List([]); - Doc.SetInPlace(bullet, "editTitle", "*", false); - FormattedTextBox.SelectOnLoad = bullet[Id]; - return bullet; - } - - makeTextCollection = () => { - Doc.SetInPlace(this.doc, "editTitle", undefined, false); - const bullet = TreeView.makeTextBullet(); - const added = this.props.addDocument(bullet); - bullet.context = this.props.treeView.Document; - return added; - } - - editableView = (key: string, style?: string) => ( StrCast(this.doc[key])} - SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => { - if (this.outlineMode && enterKey) { - Doc.SetInPlace(this.doc, key, value, false); - this.makeTextCollection(); - } else { - Doc.SetInPlace(this.doc, key, value, false) || true; - Doc.SetInPlace(this.doc, "editTitle", undefined, false); - } - })} - onClick={() => { - SelectionManager.DeselectAll(); - Doc.UserDoc().activeSelection = new List([this.doc]); - return false; - }} - OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))} - OnTab={undoBatch((shift?: boolean) => { - shift ? this.props.outdentDocument?.() : this.props.indentDocument?.(); - setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0); - })} - />) - - preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { - const dragData = de.complete.docDragData; - dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction); - } - - @undoBatch - treeDrop = (e: Event, de: DragManager.DropEvent) => { - const pt = [de.x, de.y]; - const rect = this._header!.current!.getBoundingClientRect(); - const before = pt[1] < rect.top + rect.height / 2; - const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); - const complete = de.complete; - if (complete.linkDragData) { - const sourceDoc = complete.linkDragData.linkSourceDocument; - const destDoc = this.doc; - DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", ""); - e.stopPropagation(); - } - const docDragData = complete.docDragData; - if (docDragData) { - e.stopPropagation(); - if (docDragData.draggedDocuments[0] === this.doc) return true; - const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); - let addDoc = parentAddDoc; - if (inside) { - const localAdd = (doc: Doc) => { - const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); - added && (doc.context = this.doc.context); - return added; - }; - addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce( - (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc); - } - const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument; - return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false); - } - return false; - } - - refTransform = (ref: HTMLDivElement) => { - const { scale, translateX, translateY } = Utils.GetScreenTransform(ref); - const outerXf = this.props.outerXf(); - const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); - } - docTransform = () => this.refTransform(this._dref.current!); - getTransform = () => this.refTransform(this._tref.current!); - docWidth = () => { - const layoutDoc = this.layoutDoc; - const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); - return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; - } - docHeight = () => { - const layoutDoc = this.layoutDoc; - const bounds = this.boundsOfCollectionDocument; - return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { - const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); - if (aspect) return this.docWidth() * aspect; - if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, - NumCast(this.props.containingCollection._height)))) : - NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; - })())); - } - - @computed get expandedField() { - const ids: { [key: string]: string } = {}; - const doc = this.doc; - doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); - - const rows: JSX.Element[] = []; - for (const key of Object.keys(ids).slice().sort()) { - if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; - const contents = doc[key]; - let contentElement: (JSX.Element | null)[] | JSX.Element = []; - - if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) { - const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); - const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { - const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - added && (doc.context = this.doc.context); - return added; - }; - const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); - contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), - this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, - this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged); - } else { - contentElement = Field.toKeyValueString(doc, key)} - SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />; - } - rows.push(
    - {key + ":"} -   - {contentElement} -
    ); - } - rows.push(
    - ""} - SetValue={(value: string) => { - value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true); - return true; - }} /> -
    ); - return rows; - } - - rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); - rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; - rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20); - - @computed get renderContent() { - TraceMobx(); - const expandKey = this.treeViewExpandedView; - if (["links", "annotations", this.fieldKey].includes(expandKey)) { - const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey); - const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { - const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true); - added && (doc.context = this.doc.context); - return added; - }; - const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); - const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; - const sortKey = `${this.fieldKey}-sortAscending`; - return
      { - !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true))); - e.stopPropagation(); - }}> - {!docs ? (null) : - TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc, - this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, - this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)} -
    ; - } else if (this.treeViewExpandedView === "fields") { - return
      - {this.expandedField} -
    ; - } else { - const layoutDoc = this.layoutDoc; - const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight; - const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; - return
    - -
    ; - } - } - - get onCheckedClick() { return this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); } - - @action - bulletClick = (e: React.MouseEvent) => { - if (this.onCheckedClick && this.doc.type !== DocumentType.COL) { - // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check"; - this.onCheckedClick?.script.run({ - this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, - heading: this.props.containingCollection.title, - checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check", - containingTreeView: this.props.treeView.props.Document, - }, console.log); - } else { - this.treeViewOpen = !this.treeViewOpen; - } - e.stopPropagation(); - } - - @computed get renderOutlineBullet() { - TraceMobx(); - return
    - {(this.doc.text as RichTextField)?.Text ? : (null)} -
    ; - } - @computed get renderBullet() { - TraceMobx(); - const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; - return
    - {} -
    ; - } - - showContextMenu = (e: React.MouseEvent) => { - this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - } - contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }]; - truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0); - @computed get showTitleEdit() { - return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); - } - onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); - onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); - /** - * Renders the EditableView title element for placement into the tree. - */ - @computed - get renderTitle() { - TraceMobx(); - const headerElements = this.props.treeViewHideHeaderFields() ? (null) : - <> - { this.showContextMenu(e); e.stopPropagation(); }} /> - { - if (this.treeViewOpen) { - this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : - this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") : - this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : - this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : - (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields"); - } - this.treeViewOpen = true; - })}> - {this.treeViewExpandedView} - - ; - const view = this.showTitleEdit ? this.editableView("title") : - ; - return <> -
    - {view} -
    - {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements} - ; - } - - refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); - - render() { - TraceMobx(); - if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null; - const sorting = this.doc[`${this.fieldKey}-sortAscending`]; - if (this.showTitleEdit) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll - let par: any = this._header?.current; - if (par) { - while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode; - if (par) { - const par_rect = (par as HTMLElement).getBoundingClientRect(); - const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect(); - this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0)); - } - } - } else this._editMaxWidth = ""; - const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc)); - return this.doc.treeViewHideHeader || this.outlineMode ? - !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent : -
    this.props.active(true) && SelectionManager.DeselectAll()} - onKeyDown={e => { - e.stopPropagation(); - e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete"); - e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab"); - e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet"); - e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); - }} - > -
    { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} - onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} - onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - {this.outlineMode ? this.renderOutlineBullet : this.renderBullet} -
    - -
    -
    - -
    - {!this.treeViewOpen ? (null) : this.renderContent} -
    -
    : -
    this.props.active(true) && SelectionManager.DeselectAll()}> -
  • -
    { - if (this.props.active(true)) { - e.stopPropagation(); - e.preventDefault(); - SelectionManager.DeselectAll(); - } - }} - onPointerDown={e => { - if (this.props.active(true)) { - e.stopPropagation(); - e.preventDefault(); - } - }} - onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - {this.outlineMode ? this.renderOutlineBullet : this.renderBullet} - {this.renderTitle} -
    -
    - {!this.treeViewOpen ? (null) : this.renderContent} -
    -
  • -
    ; - } - public static GetChildElements( - childDocs: Doc[], - treeView: CollectionTreeView, - containingCollection: Doc, - dataDoc: Doc | undefined, - key: string, - parentCollectionDoc: Doc | undefined, - parentPrevSibling: Doc | undefined, - add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, - remove: ((doc: Doc | Doc[]) => boolean), - move: DragManager.MoveFunction, - dropAction: dropActionType, - addDocTab: (doc: Doc, where: string) => boolean, - pinToPres: (document: Doc) => void, - backgroundColor: undefined | ((document: Opt, renderDepth: number) => string | undefined), - screenToLocalXf: () => Transform, - outerXf: () => { translateX: number, translateY: number }, - active: (outsideReaction?: boolean) => boolean, - panelWidth: () => number, - ChromeHeight: undefined | (() => number), - renderDepth: number, - treeViewHideHeaderFields: () => boolean, - treeViewPreventOpen: boolean, - renderedIds: string[], - onCheckedClick: undefined | (() => ScriptField), - onChildClick: undefined | (() => ScriptField), - ignoreFields: string[] | undefined, - firstLevel: boolean, - whenActiveChanged: (isActive: boolean) => void - ) { - const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); - if (viewSpecScript) { - childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); - } - - const docs = childDocs.slice(); - const ascending = containingCollection?.[key + "-sortAscending"]; - if (ascending !== undefined) { - const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => { - const reN = /[0-9]*$/; - const aA = a.replace(reN, ""); // get rid of trailing numbers - const bA = b.replace(reN, ""); - if (aA === bA) { // if header string matches, then compare numbers numerically - const aN = parseInt(a.match(reN)![0], 10); - const bN = parseInt(b.match(reN)![0], 10); - return aN === bN ? 0 : aN > bN ? 1 : -1; - } else { - return aA > bA ? 1 : -1; - } - }; - docs.sort(function (a, b): 0 | 1 | -1 { - const descA = ascending ? b : a; - const descB = ascending ? a : b; - const first = descA.title; - const second = descB.title; - // TODO find better way to sort how to sort.................. - if (typeof first === 'number' && typeof second === 'number') { - return (first - second) > 0 ? 1 : -1; - } - if (typeof first === 'string' && typeof second === 'string') { - return sortAlphaNum(first, second); - } - if (typeof first === 'boolean' && typeof second === 'boolean') { - // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load - // return Number(descA.x) > Number(descB.x) ? 1 : -1; - // } - return first > second ? 1 : -1; - } - return ascending ? 1 : -1; - }); - } - - const rowWidth = () => panelWidth() - 20; - return docs.map((child, i) => { - const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); - if (!pair.layout || pair.data instanceof Promise) { - return (null); - } - - const indent = i === 0 ? undefined : () => { - if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) { - const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1]; - const fieldKey = fieldKeysub.split("\'")[1]; - if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) { - remove(child); - FormattedTextBox.SelectOnLoad = child[Id]; - Doc.AddDocToList(docs[i - 1], fieldKey, child); - docs[i - 1].treeViewOpen = true; - child.context = treeView.Document; - } - } - }; - const outdent = !parentCollectionDoc ? undefined : () => { - if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) { - const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1]; - const fieldKey = fieldKeysub.split("\'")[1]; - remove(child); - FormattedTextBox.SelectOnLoad = child[Id]; - Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false); - parentCollectionDoc.treeViewOpen = true; - child.context = treeView.Document; - } - }; - const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => { - return add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); - }; - const childLayout = Doc.Layout(pair.layout); - const rowHeight = () => { - const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0); - return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); - }; - return !(child instanceof Doc) ? (null) : ; - }); - } -} export type collectionTreeViewProps = { treeViewHideTitle?: boolean; @@ -797,18 +35,16 @@ export type collectionTreeViewProps = { @observer export class CollectionTreeView extends CollectionSubView>(Document) { private treedropDisposer?: DragManager.DragDropDisposer; + private _isChildActive = false; private _mainEle?: HTMLDivElement; - public _uniqueId = Utils.GenerateGuid(); - _isChildActive = false; + @computed get doc() { return this.props.Document; } @computed get dataDoc() { return this.props.DataDoc || this.doc; } protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer?.(); - if (this._mainEle = ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this)); - } + if (this._mainEle = ele) this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this)); } protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { @@ -873,8 +109,7 @@ export class CollectionTreeView extends CollectionSubView -
    ; @@ -984,15 +219,4 @@ export class CollectionTreeView extends CollectionSubView ); } -} - -Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { - const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []); - for (let i = 0; i < docFilters.length; i += 3) { - const [header, value, state] = docFilters.slice(i, i + 3); - if (header === facetHeader && value === facetValue) { - return state; - } - } - return undefined; -}); \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss new file mode 100644 index 000000000..17c6b0750 --- /dev/null +++ b/src/client/views/collections/TreeView.scss @@ -0,0 +1,115 @@ +@import "../globalCssVariables"; + +.treeView-container, +.treeView-container-active { + .bullet-outline { + position: relative; + width: 15px; + color: $intermediate-color; + transform: scale(0.5); + display: inline-block; + } + + .bullet { + position: relative; + width: 15px; + color: $intermediate-color; + margin-top: 3px; + transform: scale(1.3, 1.3); + border: #80808030 1px solid; + border-radius: 4px; + } +} +.treeView-container-active { + z-index: 100; + position: relative;; + .formattedTextbox-sidebar { + background-color: #ffff001f !important; + height: 500px !important; + } +} + +.treeView-openRight { + display: none; + height: 17px; + width: 15px; +} + +.treeView-openRight:hover { + background: #797777; + cursor: pointer; +} + +.treeView-border-outline, +.treeView-border { + display: flex; + overflow: hidden; +} +.treeView-border{ + border-left: dashed 1px #00000042; +} + +.treeView-header-editing, +.treeView-header { + border: transparent 1px solid; + display: flex; + //align-items: center; + ::-webkit-scrollbar { + display: none; + } + .formattedTextBox-cont { + .formattedTextbox-sidebar { + overflow: visible !important; + border-left: unset; + } + overflow: visible !important; + } + + .editableView-container-editing-oneLine { + min-width: 15px; + } + + .documentView-node-topmost { + width: unset; + } + + >svg { + display: none; + } + +} + +.treeView-header:hover { + .collectionTreeView-keyHeader { + display: inherit; + } + + >svg { + display: inherit; + } + + .treeView-openRight { + display: inline-block; + height: 17px; + width: 15px; + + // display: inline; + svg { + display: block; + padding: 0px; + margin-left: 3px; + } + } +} + +.treeView-header-above { + border-top: black 1px solid; +} + +.treeView-header-below { + border-bottom: black 1px solid; +} + +.treeView-header-inside { + border: black 1px solid; +} \ No newline at end of file diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx new file mode 100644 index 000000000..e509eb78d --- /dev/null +++ b/src/client/views/collections/TreeView.tsx @@ -0,0 +1,760 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; +import { RichTextField } from '../../../fields/RichTextField'; +import { listSpec } from '../../../fields/Schema'; +import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { Docs, DocUtils } from '../../documents/Documents'; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { DocumentManager } from '../../util/DocumentManager'; +import { DragManager, dropActionType } from "../../util/DragManager"; +import { SelectionManager } from '../../util/SelectionManager'; +import { SnappingManager } from '../../util/SnappingManager'; +import { Transform } from '../../util/Transform'; +import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { EditableView } from "../EditableView"; +import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; +import { KeyValueBox } from '../nodes/KeyValueBox'; +import { CollectionTreeView } from './CollectionTreeView'; +import { CollectionView, CollectionViewType } from './CollectionView'; +import "./TreeView.scss"; +import React = require("react"); + +export interface TreeViewProps { + document: Doc; + dataDoc?: Doc; + containingCollection: Doc; + prevSibling?: Doc; + renderDepth: number; + removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; + moveDocument: DragManager.MoveFunction; + dropAction: dropActionType; + addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean; + pinToPres: (document: Doc) => void; + panelWidth: () => number; + panelHeight: () => number; + ChromeHeight: undefined | (() => number); + addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; + indentDocument?: () => void; + outdentDocument?: () => void; + ScreenToLocalTransform: () => Transform; + backgroundColor?: (doc: Opt, renderDepth: number) => string | undefined; + outerXf: () => { translateX: number, translateY: number }; + treeView: CollectionTreeView; + parentKey: string; + active: (outsideReaction?: boolean) => boolean; + treeViewHideHeaderFields: () => boolean; + treeViewPreventOpen: boolean; + renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle + onCheckedClick?: () => ScriptField; + onChildClick?: () => ScriptField; + ignoreFields?: string[]; + firstLevel: boolean; + whenActiveChanged: (isActive: boolean) => void; +} + +@observer +/** + * Renders a treeView of a collection of documents + * + * special fields: + * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden + * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) + * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree + */ +export class TreeView extends React.Component { + private _editTitleScript: (() => ScriptField) | undefined; + private _openScript: (() => ScriptField) | undefined; + private _header?: React.RefObject = React.createRef(); + private _treedropDisposer?: DragManager.DragDropDisposer; + private _dref = React.createRef(); + private _tref = React.createRef(); + private _docRef = React.createRef(); + private _uniqueId = Utils.GenerateGuid(); + private _editMaxWidth: number | string = 0; + + get doc() { return this.props.document; } + get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } + get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive + get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } + get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); } + get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } + @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state + set treeViewOpen(c: boolean) { + if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; + else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; + } + @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; } + @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } + @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } + @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } + @computed get dataDoc() { return this.doc[DataSym]; } + @computed get layoutDoc() { return Doc.Layout(this.doc); } + @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } + childDocList(field: string) { + const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; + return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field + (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields + DocListCastOrNull(this.doc[field])); // otherwise use the document's data field + } + @computed get childDocs() { return this.childDocList(this.fieldKey); } + @computed get childLinks() { return this.childDocList("links"); } + @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } + @computed get boundsOfCollectionDocument() { + return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined : + Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey])); + } + + @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right"); + @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { + return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc); + } + @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { + this.props.treeView.props.select(false); + return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); + } + @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc)); + + constructor(props: any) { + super(props); + const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); + const openScript = ScriptField.MakeScript(`openOnRight(self)`); + const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`); + this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript); + this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined; + if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); + } + + protected createTreeDropTarget = (ele: HTMLDivElement) => { + this._treedropDisposer?.(); + ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc); + } + + componentWillUnmount() { + document.removeEventListener("pointermove", this.onDragMove, true); + document.removeEventListener("pointermove", this.onDragUp, true); + } + + onDragUp = (e: PointerEvent) => { + document.removeEventListener("pointerup", this.onDragUp, true); + document.removeEventListener("pointermove", this.onDragMove, true); + } + onPointerEnter = (e: React.PointerEvent): void => { + this.props.active(true) && Doc.BrushDoc(this.dataDoc); + if (e.buttons === 1 && SnappingManager.GetIsDragging()) { + this._header!.current!.className = "treeView-header"; + document.removeEventListener("pointermove", this.onDragMove, true); + document.addEventListener("pointermove", this.onDragMove, true); + document.removeEventListener("pointerup", this.onDragUp, true); + document.addEventListener("pointerup", this.onDragUp, true); + } + } + onPointerLeave = (e: React.PointerEvent): void => { + Doc.UnBrushDoc(this.dataDoc); + if (this._header?.current?.className !== "treeView-header-editing") { + this._header!.current!.className = "treeView-header"; + } + document.removeEventListener("pointerup", this.onDragUp, true); + document.removeEventListener("pointermove", this.onDragMove, true); + } + onDragMove = (e: PointerEvent): void => { + Doc.UnBrushDoc(this.dataDoc); + const pt = [e.clientX, e.clientY]; + const rect = this._header!.current!.getBoundingClientRect(); + const before = pt[1] < rect.top + rect.height / 2; + const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); + this._header!.current!.className = "treeView-header"; + if (inside) this._header!.current!.className += " treeView-header-inside"; + else if (before) this._header!.current!.className += " treeView-header-above"; + else if (!before) this._header!.current!.className += " treeView-header-below"; + e.stopPropagation(); + } + + public static makeTextBullet() { + const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); + Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); + Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); + Doc.GetProto(bullet).data = new List([]); + Doc.SetInPlace(bullet, "editTitle", "*", false); + FormattedTextBox.SelectOnLoad = bullet[Id]; + return bullet; + } + + makeTextCollection = () => { + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + const bullet = TreeView.makeTextBullet(); + const added = this.props.addDocument(bullet); + bullet.context = this.props.treeView.Document; + return added; + } + + editableView = (key: string, style?: string) => ( StrCast(this.doc[key])} + SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => { + Doc.SetInPlace(this.doc, key, value, false); + if (this.outlineMode && enterKey) { + this.makeTextCollection(); + } else { + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + } + })} + onClick={() => { + SelectionManager.DeselectAll(); + Doc.UserDoc().activeSelection = new List([this.doc]); + return false; + }} + OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))} + OnTab={undoBatch((shift?: boolean) => { + shift ? this.props.outdentDocument?.() : this.props.indentDocument?.(); + setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0); + })} + />) + + preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { + const dragData = de.complete.docDragData; + dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction); + } + + @undoBatch + treeDrop = (e: Event, de: DragManager.DropEvent) => { + const pt = [de.x, de.y]; + const rect = this._header!.current!.getBoundingClientRect(); + const before = pt[1] < rect.top + rect.height / 2; + const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); + if (de.complete.linkDragData) { + const sourceDoc = de.complete.linkDragData.linkSourceDocument; + const destDoc = this.doc; + DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", ""); + e.stopPropagation(); + } + const docDragData = de.complete.docDragData; + if (docDragData) { + e.stopPropagation(); + if (docDragData.draggedDocuments[0] === this.doc) return true; + const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); + let addDoc = parentAddDoc; + if (inside) { + const localAdd = (doc: Doc) => { + const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); + added && (doc.context = this.doc.context); + return added; + }; + addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce( + (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc); + } + const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument; + return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false); + } + return false; + } + + refTransform = (ref: HTMLDivElement) => { + const { scale, translateX, translateY } = Utils.GetScreenTransform(ref); + const outerXf = this.props.outerXf(); + const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); + return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); + } + docTransform = () => this.refTransform(this._dref.current!); + getTransform = () => this.refTransform(this._tref.current!); + docWidth = () => { + const layoutDoc = this.layoutDoc; + const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); + if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); + return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; + } + docHeight = () => { + const layoutDoc = this.layoutDoc; + const bounds = this.boundsOfCollectionDocument; + return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { + const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); + if (aspect) return this.docWidth() * aspect; + if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); + return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) : + Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, + NumCast(this.props.containingCollection._height)))) : + NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; + })())); + } + + @computed get expandedField() { + const ids: { [key: string]: string } = {}; + const rows: JSX.Element[] = []; + const doc = this.doc; + doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); + + for (const key of Object.keys(ids).slice().sort()) { + if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; + const contents = doc[key]; + let contentElement: (JSX.Element | null)[] | JSX.Element = []; + + if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) { + const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); + const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { + const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); + added && (doc.context = this.doc.context); + return added; + }; + const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); + contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), + this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, + this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged); + } else { + contentElement = Field.toKeyValueString(doc, key)} + SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />; + } + rows.push(
    + {key + ":"} +   + {contentElement} +
    ); + } + rows.push(
    + value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} /> +
    ); + return rows; + } + + rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); + rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20); + + @computed get renderContent() { + TraceMobx(); + const expandKey = this.treeViewExpandedView; + if (["links", "annotations", this.fieldKey].includes(expandKey)) { + const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey); + const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { + const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true); + added && (doc.context = this.doc.context); + return added; + }; + const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); + const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; + const sortKey = `${this.fieldKey}-sortAscending`; + return
      { + !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true))); + e.stopPropagation(); + }}> + {!docs ? (null) : + TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc, + this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, + this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)} +
    ; + } else if (this.treeViewExpandedView === "fields") { + return
      + {this.expandedField} +
    ; + } else { + const layoutDoc = this.layoutDoc; + const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight; + const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; + return
    + +
    ; + } + } + + get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); } + + @action + bulletClick = (e: React.MouseEvent) => { + if (this.onCheckedClick) { + this.onCheckedClick?.script.run({ + this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, + heading: this.props.containingCollection.title, + checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check", + containingTreeView: this.props.treeView.props.Document, + }, console.log); + } else { + this.treeViewOpen = !this.treeViewOpen; + } + e.stopPropagation(); + } + + @computed get renderBullet() { + TraceMobx(); + const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; + return
    + {this.outlineMode && !(this.doc.text as RichTextField)?.Text ? (null) : + } +
    ; + } + @computed get showTitleEditorControl() { return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || ""); } + @computed get headerElements() { + return (Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode) || this.props.treeViewHideHeaderFields() ? (null) : + <> + { this.showContextMenu(e); e.stopPropagation(); }} /> + { + if (this.treeViewOpen) { + this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : + this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") : + this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : + this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : + (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : + this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields"); + } + this.treeViewOpen = true; + })}> + {this.treeViewExpandedView} + + ; + } + + showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef.current?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }]; + truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0); + onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); + onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + /** + * Renders the EditableView title element for placement into the tree. + */ + @computed + get renderTitle() { + TraceMobx(); + const view = this.showTitleEditorControl ? this.editableView("title") : + ; + return <> +
    + {view} +
    + {this.headerElements} + ; + } + + refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); + + render() { + TraceMobx(); + if (this.props.renderedIds.indexOf(this.doc[Id]) !== -1) return null; + const sorting = this.doc[`${this.fieldKey}-sortAscending`]; + if (this.showTitleEditorControl) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll + let par: any = this._header?.current; + while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode; + if (par) { + const par_rect = (par as HTMLElement).getBoundingClientRect(); + const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect(); + this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0)); + } + } + else this._editMaxWidth = ""; + const selected = SelectionManager.IsSelected(DocumentManager.Instance.getFirstDocumentView(this.doc)); + return this.doc.treeViewHideHeader || this.outlineMode ? + !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? + this.renderContent + :
    this.props.active(true) && SelectionManager.DeselectAll()} + onKeyDown={e => { + e.stopPropagation(); + switch (e.key) { + case "Backspace": return this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete"); + case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet"); + case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); + return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab"); + } + }} > +
    { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} + onPointerDown={e => { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); } }} + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> + {this.renderBullet} +
    + +
    +
    + +
    + {!this.treeViewOpen ? (null) : this.renderContent} +
    +
    : +
    this.props.active(true) && SelectionManager.DeselectAll()}> +
  • +
    { + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + SelectionManager.DeselectAll(); + } + }} + onPointerDown={e => { + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } + }} + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> + {this.renderBullet} + {this.renderTitle} +
    +
    + {!this.treeViewOpen ? (null) : this.renderContent} +
    +
  • +
    ; + } + + + public static GetChildElements( + childDocs: Doc[], + treeView: CollectionTreeView, + containingCollection: Doc, + dataDoc: Doc | undefined, + key: string, + parentCollectionDoc: Doc | undefined, + parentPrevSibling: Doc | undefined, + add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, + remove: ((doc: Doc | Doc[]) => boolean), + move: DragManager.MoveFunction, + dropAction: dropActionType, + addDocTab: (doc: Doc, where: string) => boolean, + pinToPres: (document: Doc) => void, + backgroundColor: undefined | ((document: Opt, renderDepth: number) => string | undefined), + screenToLocalXf: () => Transform, + outerXf: () => { translateX: number, translateY: number }, + active: (outsideReaction?: boolean) => boolean, + panelWidth: () => number, + ChromeHeight: undefined | (() => number), + renderDepth: number, + treeViewHideHeaderFields: () => boolean, + treeViewPreventOpen: boolean, + renderedIds: string[], + onCheckedClick: undefined | (() => ScriptField), + onChildClick: undefined | (() => ScriptField), + ignoreFields: string[] | undefined, + firstLevel: boolean, + whenActiveChanged: (isActive: boolean) => void + ) { + const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); + if (viewSpecScript) { + childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); + } + + const docs = childDocs.slice(); + const ascending = containingCollection?.[key + "-sortAscending"]; + if (ascending !== undefined) { + const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => { + const reN = /[0-9]*$/; + const aA = a.replace(reN, ""); // get rid of trailing numbers + const bA = b.replace(reN, ""); + if (aA === bA) { // if header string matches, then compare numbers numerically + const aN = parseInt(a.match(reN)![0], 10); + const bN = parseInt(b.match(reN)![0], 10); + return aN === bN ? 0 : aN > bN ? 1 : -1; + } else { + return aA > bA ? 1 : -1; + } + }; + docs.sort(function (a, b): 0 | 1 | -1 { + const first = (ascending ? b : a).title; + const second = (ascending ? a : b).title; + if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1; + if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second); + return ascending ? 1 : -1; + }); + } + + const rowWidth = () => panelWidth() - 20; + return docs.map((child, i) => { + const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); + if (!pair.layout || pair.data instanceof Promise) { + return (null); + } + + const indent = i === 0 ? undefined : () => { + if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) { + const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1]; + const fieldKey = fieldKeysub.split("\'")[1]; + if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) { + remove(child); + FormattedTextBox.SelectOnLoad = child[Id]; + Doc.AddDocToList(docs[i - 1], fieldKey, child); + docs[i - 1].treeViewOpen = true; + child.context = treeView.Document; + } + } + }; + const outdent = !parentCollectionDoc ? undefined : () => { + if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) { + const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1]; + const fieldKey = fieldKeysub.split("\'")[1]; + remove(child); + FormattedTextBox.SelectOnLoad = child[Id]; + Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false); + parentCollectionDoc.treeViewOpen = true; + child.context = treeView.Document; + } + }; + const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); + const childLayout = Doc.Layout(pair.layout); + const rowHeight = () => { + const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0); + return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); + }; + return !(child instanceof Doc) ? (null) : ; + }); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 9fd81bd90..748af89ef 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -193,6 +193,16 @@ export class FilterBox extends ViewBoxBaseComponent(); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 2df550b47..c5a64af3e 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -222,10 +222,10 @@ export class PresBox extends ViewBoxBaseComponent const bestTarget = DocumentManager.Instance.getFirstDocumentView(presTargetDoc)?.props.Document; bestTarget && runInAction(() => { if (activeItem.presMovement === PresMovement.Jump) { - bestTarget!._viewTransition = '0s'; + bestTarget._viewTransition = '0s'; } else { - bestTarget!._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s'; - setTimeout(() => bestTarget!._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010); + bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s'; + setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010); } }); } else { @@ -301,10 +301,10 @@ export class PresBox extends ViewBoxBaseComponent // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; bestTarget && runInAction(() => { - bestTarget!._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; - bestTarget!._panX = activeItem.presPinViewX; - bestTarget!._panY = activeItem.presPinViewY; - bestTarget!._viewScale = activeItem.presPinViewScale; + bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; + bestTarget._panX = activeItem.presPinViewX; + bestTarget._panY = activeItem.presPinViewY; + bestTarget._viewScale = activeItem.presPinViewScale; }); //setTimeout(() => targetDoc._viewTransition = undefined, 1010); } @@ -571,7 +571,7 @@ export class PresBox extends ViewBoxBaseComponent if (this.childDocs.includes(doc)) { if (docs.length === i + 1) return false; } else if (doc.type === DocumentType.LABEL) { - const audio = Cast(doc.annotationOn, Doc, null) as Doc; + const audio = Cast(doc.annotationOn, Doc, null); if (audio) { audio.aliasOf instanceof Doc; audio.presStartTime = NumCast(doc.audioStart); @@ -617,9 +617,8 @@ export class PresBox extends ViewBoxBaseComponent const list = this._selectedArray.map((doc: Doc, index: any) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null); - if (tagDoc) return ( -
    {index + 1}. {curDoc.title}
    - ); else if (curDoc) return
    {index + 1}. {curDoc.title}
    + if (tagDoc) return
    {index + 1}. {curDoc.title}
    ; + else if (curDoc) return
    {index + 1}. {curDoc.title}
    ; }); return list; } @@ -996,7 +995,7 @@ export class PresBox extends ViewBoxBaseComponent @computed get optionsDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - const presPinWithViewIcon = ; + const presPinWithViewIcon = ; if (activeItem && targetDoc) { return (
    @@ -1047,8 +1046,8 @@ export class PresBox extends ViewBoxBaseComponent const scale = targetDoc._viewScale; activeItem.presPinViewX = x; activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale - }}>Update
    : (null)} + activeItem.presPinViewScale = scale; + }}>Update
    : (null)};
    diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 0c8b20ae3..e9ab5911d 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -115,7 +115,7 @@ export class PresElementBox extends ViewBoxBaseComponent Date: Mon, 21 Sep 2020 14:36:24 -0400 Subject: fixed text box llnk preview to go away when docs are closed. fixed tree view to be more efficient by not re-rendering when items are selected. made slide view text items forceActive --- .../views/collections/CollectionTreeView.scss | 1 + .../views/collections/CollectionTreeView.tsx | 44 +++++++----- src/client/views/collections/TreeView.tsx | 21 +++--- src/client/views/nodes/DocumentContentsView.tsx | 19 ++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 82 +++++++++------------- .../formattedText/FormattedTextBoxComment.tsx | 13 ++-- 6 files changed, 84 insertions(+), 96 deletions(-) (limited to 'src/client/views/collections/CollectionTreeView.scss') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 20bfc0e9d..c5add7cfb 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -28,6 +28,7 @@ .no-indent { padding-left: 0; + width: max-content; } .editableView-container { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index efbe5581b..e2247aec3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -41,7 +41,7 @@ export class CollectionTreeView extends CollectionSubView { @@ -117,7 +117,7 @@ export class CollectionTreeView extends CollectionSubView this.onExternalDrop(e, {}); @computed get renderClearButton() { - return
    + return !this.doc.allowClear ? (null) :
    @@ -192,37 +192,45 @@ export class CollectionTreeView extends CollectionSubView this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; - render() { + @computed get treeChildren() { + TraceMobx(); + return this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; + } + @computed get treeViewElements() { TraceMobx(); - if (!(this.doc instanceof Doc)) return (null); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc); - const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; - const childElements = childDocs && TreeView.GetChildElements(childDocs, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, + return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick, this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged); + } + @computed get titleBar() { const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle; - const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth); + return hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren) + } + render() { + TraceMobx(); + if (!(this.doc instanceof Doc)) return (null); + const background = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth); + const paddingX = `${NumCast(this.doc._xPadding, 10)}px`; + const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`; + const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; - return !childDocs ? (null) : ( + return !this.treeChildren ? (null) : (
    this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> - {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)} - {this.doc.allowClear ? this.renderClearButton : (null)} -
      {childElements}
    + {this.titleBar} + {this.renderClearButton} +
      + {this.treeViewElements} +
    ); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index edb703135..486e44f6b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -83,9 +83,9 @@ export class TreeView extends React.Component { private _uniqueId = Utils.GenerateGuid(); private _editMaxWidth: number | string = 0; - get doc() { return this.props.document; } + @computed get doc() { TraceMobx(); return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } - get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive + get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); } get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } @@ -100,14 +100,14 @@ export class TreeView extends React.Component { @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } - @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } + @computed get fieldKey() { TraceMobx(); const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } childDocList(field: string) { const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields DocListCastOrNull(this.doc[field])); // otherwise use the document's data field } - @computed get childDocs() { return this.childDocList(this.fieldKey); } + @computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } @computed get boundsOfCollectionDocument() { @@ -130,13 +130,12 @@ export class TreeView extends React.Component { constructor(props: any) { super(props); - console.log("Ttile = " + this.props.document.title); const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); const openScript = ScriptField.MakeScript(`openOnRight(self)`); const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`); this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript); this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined; - if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); + if (Doc.GetT(this.props.document, "editTitle", "string", true) === "*") Doc.SetInPlace(this.props.document, "editTitle", this._uniqueId, false); } protected createTreeDropTarget = (ele: HTMLDivElement) => { @@ -185,7 +184,7 @@ export class TreeView extends React.Component { } public static makeTextBullet() { - const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); + const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", forceActive: true, _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); Doc.GetProto(bullet).data = new List([]); @@ -659,8 +658,7 @@ export class TreeView extends React.Component { onChildClick: undefined | (() => ScriptField), ignoreFields: string[] | undefined, firstLevel: boolean, - whenActiveChanged: (isActive: boolean) => void - ) { + whenActiveChanged: (isActive: boolean) => void) { const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); if (viewSpecScript) { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); @@ -691,7 +689,7 @@ export class TreeView extends React.Component { } const rowWidth = () => panelWidth() - 20; - return docs.map((child, i) => { + return docs.filter(child => child instanceof Doc).map((child, i) => { const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return (null); @@ -727,13 +725,12 @@ export class TreeView extends React.Component { const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0); return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); }; - return !(child instanceof Doc) ? (null) : { @computed get layout(): string { TraceMobx(); - if (!this.layoutDoc) return "

    awaiting layout

    "; - // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); // bcz: replaced this with below... is it right? if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString; + if (!this.layoutDoc) return "

    awaiting layout

    "; + if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string"); - if (this.props.layoutKey === "layout_keyValue") { - return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); - } else - if (layout === undefined) { - return this.props.Document.data ? - "" : - KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); - } else if (typeof layout === "string") { - return layout; - } else { - return "

    Loading layout

    "; - } + if (layout === undefined) return this.props.Document.data ? "" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); + if (typeof layout === "string") return layout; + return "

    Loading layout

    "; } get dataDoc() { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ff2ce90b7..e92efd708 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1189,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.endUndoTypingBatch(); Object.values(this._disposers).forEach(disposer => disposer?.()); this._editorView?.destroy(); - FormattedTextBoxComment.Hide(); + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); } _downEvent: any; @@ -1522,16 +1522,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } render() { TraceMobx(); + const selected = this.props.isSelected(); + const active = this.active(); const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground; - this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected - if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) { - setTimeout(() => FormattedTextBoxComment.Hide(), 0); - } + selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected + if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } const minimal = this.props.ignoreAutoHeight; - const selPad = (this.props.isSelected() && !this.layoutDoc._singleLine) || minimal ? -10 : 0; - const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : ""; + const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? -10 : 0; + const selclass = selected && !this.layoutDoc._singleLine ? "-selected" : ""; return (
    this._entered = true)} - onPointerLeave={action((e: React.PointerEvent) => { + onPointerLeave={action(e => { this._entered = false; const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); for (let child: any = target; child; child = child?.parentElement) { - if (child === this._ref.current!) { - this._entered = true; - } + child === this._ref.current! && (this._entered = true); } })} onDoubleClick={this.onDoubleClick} >
    - {!this.layoutDoc._showSidebar ? (null) : <> -
    - {this.sidebarWidthPercent === "0%" ? (null) : - 1000 : this.props.PanelHeight} - PanelWidth={this.sidebarWidth} - scaleField={this.annotationKey + "-scale"} - annotationsKey={this.annotationKey} - isAnnotationOverlay={true} - focus={this.props.focus} - isSelected={this.props.isSelected} - select={emptyFunction} - active={this.annotationsActive} - ContentScaling={returnOne} - whenActiveChanged={this.whenActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocument} - CollectionView={undefined} - ScreenToLocalTransform={this.sidebarScreenToLocal} - renderDepth={this.props.renderDepth + 1} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} /> - } -
    - {this.props.isSelected() ?
    : (null)} - } + {!this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : +
    + 1000 : this.props.PanelHeight} + PanelWidth={this.sidebarWidth} + scaleField={this.annotationKey + "-scale"} + annotationsKey={this.annotationKey} + isAnnotationOverlay={true} + focus={this.props.focus} + isSelected={this.props.isSelected} + select={emptyFunction} + active={this.annotationsActive} + ContentScaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + CollectionView={undefined} + ScreenToLocalTransform={this.sidebarScreenToLocal} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} /> +
    } + {selected ?
    : (null)} {!this.layoutDoc._showAudio ? (null) :
    this._recording = !this._recording)} > - +
    }
    diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index a37210de6..14bbee1ac 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -143,9 +143,12 @@ export class FormattedTextBoxComment { public static Hide() { FormattedTextBoxComment.textBox = undefined; - FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); - ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.linkDoc = undefined; + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); + try { + ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); + } catch (e) { } } public static SetState(textBox: any, start: number, end: number, mark: Mark) { FormattedTextBoxComment.textBox = textBox; @@ -228,17 +231,17 @@ export class FormattedTextBoxComment { if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) { try { ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); } catch (e) { } - FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltipText = document.createElement("div"); FormattedTextBoxComment.tooltipText.style.width = "100%"; FormattedTextBoxComment.tooltipText.style.height = "100%"; FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis"; FormattedTextBoxComment.tooltipText.style.cursor = "pointer"; - FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); - FormattedTextBoxComment.tooltipText.textContent = "URL: " + href; (FormattedTextBoxComment.tooltipText as any).href = href; + FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); + if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500))); } else { -- cgit v1.2.3-70-g09d2