diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/DocumentDecorations.tsx | 32 | ||||
| -rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 2 | ||||
| -rw-r--r-- | src/client/views/MainView.scss | 27 | ||||
| -rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/collections/TabDocView.scss | 5 | ||||
| -rw-r--r-- | src/client/views/collections/TabDocView.tsx | 107 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 30 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/FilterBox.tsx | 6 | ||||
| -rw-r--r-- | src/client/views/nodes/FontIconBox.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 2 |
16 files changed, 144 insertions, 119 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 41be364fd..e4e27bec7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -13,7 +13,7 @@ import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick, returnVal } from "../../Utils"; import { DocUtils, Docs } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; -import { DragManager } from "../util/DragManager"; +import { DragManager, dropActionType } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from "../util/UndoManager"; @@ -137,8 +137,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0); dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top); - dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument; + dragData.moveDocument = dragDocView.props.moveDocument; dragData.isSelectionMove = true; + dragData.dropAction = dragDocView.props.Document.dropAction as dropActionType; this.Interacting = true; this._hidden = true; DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(dv => dv.ContentDiv!), dragData, e.x, e.y, { @@ -578,20 +579,25 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return (null); } const canDelete = SelectionManager.SelectedDocuments().some(docView => { - const collectionAcl = GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]); - return collectionAcl === AclAdmin || collectionAcl === AclEdit; + const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit; + return !docView.props.Document._stayInCollection && (collectionAcl === AclAdmin || collectionAcl === AclEdit); }); - const minimal = bounds.r - bounds.x < 100 ? true : false; - const closeIcon = canDelete ? ( + const canOpen = SelectionManager.SelectedDocuments().some(docView => !docView.props.Document._stayInCollection); + const closeIcon = !canDelete ? (null) : ( <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top"> <div className="documentDecorations-closeButton" onClick={this.onCloseClick}> <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" /> - </div></Tooltip>) : (null); + </div></Tooltip>); + + const openIcon = !canOpen ? (null) : <Tooltip title={<div className="dash-tooltip">Open In a New Pane</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> + {SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."} + </div> + </Tooltip>; const titleArea = this._edtingTitle ? <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle} onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : - <div className="documentDecorations-title" style={{ gridColumnEnd: minimal ? 4 : 5 }} key="title" onPointerDown={this.onTitleDown} > + <div className="documentDecorations-title" style={{ gridColumnEnd: 5 }} key="title" onPointerDown={this.onTitleDown} > <span style={{ width: "100%", display: "inline-block", cursor: "move" }}>{`${this.selectionTitle}`}</span> </div>; @@ -627,14 +633,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> }}> {closeIcon} {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) : + {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || Object.keys(SelectionManager.SelectedDocuments()[0].props).includes("treeViewDoc") ? (null) : <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top"> <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}> <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" /> </div></Tooltip>} - <Tooltip title={<div className="dash-tooltip">Open In a New Pane</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> - {SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."} - </div></Tooltip> + {openIcon} <div className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} /> <div className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} /> <div className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} /> @@ -654,9 +658,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div> </div > - <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}> + {seldoc?.Document.type === DocumentType.FONTICON ? (null) : <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}> <DocumentButtonBar views={SelectionManager.SelectedDocuments} /> - </div> + </div>} </>} </div > ); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 83c02b09b..b231b2171 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -113,7 +113,7 @@ export class KeyManager { } const selected = SelectionManager.SelectedDocuments().slice(); - UndoManager.RunInBatch(() => selected.map(dv => dv.props.removeDocument?.(dv.props.Document)), "delete"); + UndoManager.RunInBatch(() => selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)), "delete"); SelectionManager.DeselectAll(); break; case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left"); break; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index aebb28859..f64449616 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -382,4 +382,31 @@ display: block; width: 500px; height: 1000px; +} + +.lm_drag_tab { + padding: 0; + width: 15px !important; + height: 15px !important; + position: relative !important; + display: inline-flex !important; + align-items: center; + top: 0 !important; + right: unset !important; + left: 0 !important; +} +.lm_close_tab { + padding: 0; + width: 15px !important; + height: 15px !important; + position: relative !important; + display: inline-flex !important; + align-items: center; + top: 0 !important; + right: unset !important; + left: 0 !important; +} +.lm_tab, .lm_tab_active { + display: flex !important; + padding-right: 0 !important; }
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 82261fcf0..c2290dd09 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -445,11 +445,7 @@ export class MainView extends React.Component { remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc); - addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => { - const ret = flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc); - ret && (doc._stayInCollection = undefined); - return ret; - }, true) + addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 306b9187e..4685d2ffc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -152,7 +152,11 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns instance._goldenLayout.root.addChild(newContentItem); - } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row + } else if (instance._goldenLayout.root.contentItems.length === 1 && instance._goldenLayout.root.contentItems[0].contentItems.length === 1 && + instance._goldenLayout.root.contentItems[0].contentItems[0].contentItems.length === 0) { + instance._goldenLayout.root.contentItems[0].contentItems[0].addChild(docContentConfig); + } + else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row switch (pullSide) { default: case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 9e67faed5..0f8a5df0d 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -417,7 +417,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp if (this._dragRef.current && this.selectedDoc) { const dragData = new DragManager.DocumentDragData([this.selectedDoc]); const [left, top] = [e.clientX, e.clientY]; - dragData.dropAction = "alias"; + dragData.defaultDropAction = "alias"; DragManager.StartDocumentDrag([this._dragRef.current], dragData, left, top, { offsetX: dragData.offset[0], offsetY: dragData.offset[1], diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index ed5414954..dc1d5a8dd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,5 +1,4 @@ import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx"; -import { basename } from 'path'; import CursorField from "../../../fields/CursorField"; import { Doc, Opt, Field, DocListCast } from "../../../fields/Doc"; import { Id, ToString } from "../../../fields/FieldSymbols"; @@ -220,15 +219,20 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: if (docDragData) { let added = false; const dropAction = docDragData.dropAction || docDragData.userDropAction; - if ((!dropAction || dropAction === "move") && docDragData.moveDocument) { + const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]); + const someMoved = !docDragData.userDropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag)); + if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop); + if ((!dropAction || dropAction === "move" || someMoved) && docDragData.moveDocument) { const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); - const res = addedDocs.length ? this.addDocument(addedDocs) : true; if (movedDocs.length) { const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || !this.props.isAnnotationOverlay || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document); added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse); - } else added = res; + } else { + ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); + added = addedDocs.length ? this.addDocument(addedDocs) : true; + } added && e.stopPropagation(); return added; } else { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1aa3a9364..61c8f580d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -52,7 +52,7 @@ export interface TreeViewProps { indentDocument?: () => void; outdentDocument?: () => void; ScreenToLocalTransform: () => Transform; - backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined; + backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined; outerXf: () => { translateX: number, translateY: number }; treeView: CollectionTreeView; parentKey: string; @@ -655,7 +655,7 @@ class TreeView extends React.Component<TreeViewProps> { dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, pinToPres: (document: Doc) => void, - backgroundColor: undefined | ((document: Doc, renderDepth: number) => string | undefined), + backgroundColor: undefined | ((document: Opt<Doc>, renderDepth: number) => string | undefined), screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: (outsideReaction?: boolean) => boolean, diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss index 5dfb9366a..edf556c9f 100644 --- a/src/client/views/collections/TabDocView.scss +++ b/src/client/views/collections/TabDocView.scss @@ -4,10 +4,15 @@ input.lm_title max-width: unset !important; transition-delay: unset; width: 100%; + cursor: text; } input.lm_title { transition-delay: 0.35s; width: 100px; + cursor: pointer; +} +.tabDocView-drag { + margin: auto; } .miniMap-hidden, .miniMap { diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 84dc6c9fb..f89285923 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -1,3 +1,5 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import { clamp } from 'lodash'; @@ -10,7 +12,7 @@ import { FieldId } from "../../../fields/RefField"; import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; @@ -22,13 +24,11 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocumentView } from "../nodes/DocumentView"; import { PresBox } from '../nodes/PresBox'; import { CollectionDockingView } from './CollectionDockingView'; -import "./TabDocView.scss"; import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionViewType } from './CollectionView'; +import "./TabDocView.scss"; import React = require("react"); -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; const _global = (window /* browser */ || global /* node */) as any; interface TabDocViewProps { @@ -72,11 +72,8 @@ export class TabDocView extends React.Component<TabDocViewProps> { tab.setActive(true); } }; - const onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, (e) => { - !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY); - return !e.defaultPrevented; - }, returnFalse, emptyFunction); + const dragBtnDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction); }; // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected @@ -94,20 +91,13 @@ export class TabDocView extends React.Component<TabDocViewProps> { //attach the selection doc buttons menu to the drag handle const stack = tab.contentItem.parent; - const dragHdl = document.createElement("span"); - dragHdl.className = "collectionDockingView-gear"; - dragHdl.style.position = "relative"; - dragHdl.style.paddingLeft = "0px"; - dragHdl.style.paddingRight = "12px"; - tab._disposers.buttonDisposer = reaction(() => this.view, (view) => view && - [ReactDOM.render( - <span title="Drag as document" className="collectionDockingView-drag" onPointerDown={onPointerDown} > - <CollectionDockingViewMenu views={() => [view]} Stack={stack} /> - </span>, dragHdl), - tab._disposers.buttonDisposer?.()], + const dragHdl = document.createElement("div"); + dragHdl.className = "lm_drag_tab"; + tab._disposers.buttonDisposer = reaction(() => this.view, view => + view && [ReactDOM.render(<span className="tabDocView-drag" onPointerDown={dragBtnDown}><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, dragHdl), tab._disposers.buttonDisposer?.()], { fireImmediately: true }); tab.reactComponents = [dragHdl]; - tab.element.append(dragHdl); + tab.closeElement.before(dragHdl); // highlight the tab when the tab document is brushed in any part of the UI tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { @@ -126,6 +116,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { }); } } + /** * Adds a document to the presentation view **/ @@ -154,13 +145,13 @@ export class TabDocView extends React.Component<TabDocViewProps> { } } } + /** * Adds a document to the presentation view **/ @undoBatch @action public static UnpinDoc(doc: Doc) { - //add this new doc to props.Document const curPres = CurrentUserUtils.ActivePresentation; if (curPres) { const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)); @@ -169,20 +160,18 @@ export class TabDocView extends React.Component<TabDocViewProps> { } componentDidMount() { - const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white"); const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document); - const updateTabColor = () => this.tab?.titleElement[0] && (this.tab.titleElement[0].style.backgroundColor = selected() ? color() : ""); - const observer = new _global.ResizeObserver(action((entries: any) => { + new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; this._panelHeight = entry.contentRect.height; } - updateTabColor(); - })); - observer.observe(this.props.glContainer._element[0]); + })).observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(); - this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), updateTabColor, { fireImmediately: true }); + this._tabReaction = reaction(() => ({ selected: selected(), color: this.tabColor, title: this.tab.titleElement[0] }), + ({ selected, color, title }) => title && (title.style.backgroundColor = selected ? color : ""), + { fireImmediately: true }); } componentWillUnmount() { @@ -196,30 +185,23 @@ export class TabDocView extends React.Component<TabDocViewProps> { this._isActive = this.props.glContainer.tab.isActive; (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged(); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. - // this._isActive && this.view && SelectionManager.SelectDoc(this.view, false); } } - get layoutDoc() { return this._document && Doc.Layout(this._document); } nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0; panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth) panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight; nativeWidth = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeHeight) || this._panelHeight : 0; - contentScaling = () => { - const nativeH = this.nativeHeight(); - const nativeW = this.nativeWidth(); + const nativeH = NumCast(this.layoutDoc?._nativeHeight); + const nativeW = NumCast(this.layoutDoc?._nativeWidth); let scaling = 1; - if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) { - scaling = 1; - } else if (NumCast(this.layoutDoc?._nativeWidth) && ((this.layoutDoc?._fitWidth) || - this._panelHeight / NumCast(this.layoutDoc?._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc?._nativeWidth))) { - scaling = this._panelWidth / NumCast(this.layoutDoc?._nativeWidth); + if (nativeW && (this.layoutDoc?._fitWidth || this._panelHeight / nativeH > this._panelWidth / nativeW)) { + scaling = this._panelWidth / nativeW; // width-limited or fitWidth } else if (nativeW && nativeH) { - const wscale = this.panelWidth() / nativeW; - scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; + scaling = this._panelHeight / nativeH; // height-limited } return scaling; } @@ -234,6 +216,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; } + @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } // adds a tab to the layout based on the locaiton parameter which can be: // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, @@ -244,10 +227,10 @@ export class TabDocView extends React.Component<TabDocViewProps> { // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); - if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - const locationFields = location.split(":"); + const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); const locationParams = locationFields.length > 1 ? locationFields[1] : ""; switch (locationFields[0]) { + case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); @@ -257,42 +240,44 @@ export class TabDocView extends React.Component<TabDocViewProps> { } } - @computed get renderContentBounds() { + @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document, 0))); } + @computed get renderBounds() { const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; const xbounds = bounds[2] - bounds[0]; const ybounds = bounds[3] - bounds[1]; const dim = Math.max(xbounds, ybounds); return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; } - @computed get miniLeft() { return 50 + (NumCast(this._document?._panX) - this.renderContentBounds.cx) / this.renderContentBounds.dim * 100 - this.miniWidth / 2; } - @computed get miniTop() { return 50 + (NumCast(this._document?._panY) - this.renderContentBounds.cy) / this.renderContentBounds.dim * 100 - this.miniHeight / 2; } - @computed get miniWidth() { return this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; } - @computed get miniHeight() { return this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; } childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null); returnMiniSize = () => NumCast(this._document?._miniMapSize, 150); miniDown = (e: React.PointerEvent) => { - this._document && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim); - this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim); + const doc = this._document; + const miniSize = this.returnMiniSize(); + doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { + doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * this.renderBounds.dim, this.renderBounds.l, this.renderBounds.l + this.renderBounds.dim); + doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * this.renderBounds.dim, this.renderBounds.t, this.renderBounds.t + this.renderBounds.dim); return false; }), emptyFunction, emptyFunction); } - getCurrentFrame = (): number => { - const presTargetDoc = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null); - return Cast(presTargetDoc._currentFrame, "number", null); + getCurrentFrame = () => { + return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); } - renderMiniMap() { + const miniWidth = this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100; + const miniHeight = this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100; + const miniLeft = 50 + (NumCast(this._document?._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; + const miniTop = 50 + (NumCast(this._document?._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; + const miniSize = this.returnMiniSize(); return <> {this._document?.hideMinimap ? (null) : - <div className="miniMap" style={{ width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor, StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))), }}> + <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.tabColor }}> <CollectionFreeFormView Document={this._document!} LibraryPath={emptyPath} CollectionView={undefined} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid havin to set stuff like this. + ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale active={returnTrue} select={emptyFunction} @@ -321,13 +306,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { fitToBox={true} /> <div className="miniOverlay" onPointerDown={this.miniDown} > - <div className="miniThumb" style={{ - width: `${this.miniWidth}% `, - height: `${this.miniHeight}% `, - left: `${this.miniLeft}% `, - top: `${this.miniTop}% `, - }} - /> + <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} /> </div> </div>} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1cae12a9d..85641531d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -217,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)]; layoutDoc._width = NumCast(layoutDoc._width, 300); layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300); - d._isBackground === undefined && !d["_isBackground-canClick"] && (d.zIndex = zsorted.length + 1 + i); // bringToFront + !d._isBackground && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront } (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments); @@ -372,12 +372,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } } - getClusterColor = (doc: Doc) => { + getClusterColor = (doc: Opt<Doc>) => { let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1); - const cluster = NumCast(doc.cluster); + const cluster = NumCast(doc?.cluster); if (this.Document._useClusters) { if (this._clusterSets.length <= cluster) { - setTimeout(() => this.updateCluster(doc), 0); + setTimeout(() => doc && this.updateCluster(doc), 0); } else { // choose a cluster color from a palette const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"]; @@ -839,7 +839,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } bringToFront = action((doc: Doc, sendToBack?: boolean) => { - if (sendToBack || doc._isBackground || doc["_isBackground-canClick"]) { + if (sendToBack || doc._isBackground) { doc.zIndex = 0; } else if (doc.isInkMask) { doc.zIndex = 5000; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 966355e8f..5ca57a193 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -87,7 +87,7 @@ export interface DocumentViewProps { addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean; pinToPres: (document: Doc) => void; backgroundHalo?: () => boolean; - backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined; + backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined; forcedBackgroundColor?: (doc: Doc) => string | undefined; opacity?: () => number | undefined; ChromeHeight?: () => number; @@ -303,7 +303,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { let stopPropagate = true; let preventDefault = true; - !this.props.Document._isBackground && !this.props.Document["_isBackground-canClick"] && this.props.bringToFront(this.props.Document); + !this.props.Document._isBackground && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (this._timeout) { clearTimeout(this._timeout); @@ -602,9 +602,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } @undoBatch @action - toggleLockInBack = () => { - this.rootDoc["_isBackground-canClick"] = !this.rootDoc["_isBackground-canClick"]; - if (this.rootDoc["_isBackground-canClick"]) this.rootDoc.zIndex = 0; + toggleRaiseWhenDragged = () => { + this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined; } @undoBatch @action @@ -753,6 +752,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu @action onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => { + if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) { + e.preventDefault(); + e.stopPropagation(); + !this.isSelected(true) && SelectionManager.SelectDoc(this, false); + } // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 if (e) { if (e.button === 0 && !e.ctrlKey || e.isDefaultPrevented()) { @@ -790,19 +794,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; + + const zorders = cm.findByDescription("ZOrder..."); + const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : []; + zorderItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" }); + zorderItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" }); + zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: this.toggleRaiseWhenDragged, icon: "expand-arrows-alt" }); + !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" }); + if (!this.Document.annotationOn) { const options = cm.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); - const zorders = cm.findByDescription("ZOrder..."); - const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : []; - zorderItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" }); - zorderItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" }); - zorderItems.push({ description: this.rootDoc["_isBackground-canClick"] ? "Unlock from Back" : "Lock in Back", event: this.toggleLockInBack, icon: "expand-arrows-alt" }); - !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" }); - onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); @@ -1049,7 +1054,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } @computed get ignorePointerEvents() { return this.props.pointerEvents === "none" || - (SnappingManager.GetIsDragging() && this.Document["_isBackground-canClick"]) || (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None); } diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c7a1dfcb0..0d29e43b8 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -39,7 +39,7 @@ export interface FieldViewProps { pinToPres: (document: Doc) => void; removeDocument?: (document: Doc | Doc[]) => boolean; moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; - backgroundColor?: (document: Doc, renderDepth: number) => string | undefined; + backgroundColor?: (document: Opt<Doc>, renderDepth: number) => string | undefined; ScreenToLocalTransform: () => Transform; bringToFront: (doc: Doc, sendToBack?: boolean) => void; active: (outsideReaction?: boolean) => boolean; diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index eab365445..7a010532f 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -94,13 +94,13 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc }); let newFacet: Opt<Doc>; if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { - newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); + newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox newFacet._textBoxPadding = 4; const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`; newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); } else if (facetHeader !== "tags" && nonNumbers / facetValues.length < .1) { - newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true }); + newFacet = Docs.Create.SliderDocument({ title: facetHeader, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", treeViewOpen: true }); const newFacetField = Doc.LayoutFieldKey(newFacet); const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox @@ -118,7 +118,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc newFacet.title = facetHeader; newFacet.treeViewOpen = true; newFacet.type = DocumentType.COL; - const capturedVariables = { layoutDoc: targetDoc, dataDoc: (targetDoc.data as any)[0][DataSym] }; + const capturedVariables = { layoutDoc: targetDoc, _stayInCollection: true, _hideContextMenu: true, dataDoc: (targetDoc.data as any)[0][DataSym] }; newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables); } newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet); diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index f9b62ceb5..6e96513c7 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -45,10 +45,12 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); }; specificContextMenu = (): void => { - const cm = ContextMenu.Instance; - cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" }); - !Doc.UserDoc().noviceMode && cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" }); - !Doc.UserDoc().noviceMode && cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" }); + if (!Doc.UserDoc().noviceMode) { + const cm = ContextMenu.Instance; + cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" }); + cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" }); + cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" }); + } } componentWillUnmount() { diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 2d26c5c6d..89ea57f65 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -16,7 +16,7 @@ interface Props { linkDoc?: Doc; linkSrc?: Doc; href?: string; - backgroundColor: (doc: Doc, renderDepth: number) => string; + backgroundColor: (doc: Opt<Doc>, renderDepth: number) => string; addDocTab: (document: Doc, where: string) => boolean; location: number[]; } |
