diff options
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 2 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 2 | ||||
-rw-r--r-- | src/client/util/UndoManager.ts | 11 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 2 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 13 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/collections/TreeView.tsx | 52 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 3 | ||||
-rw-r--r-- | src/server/websocket.ts | 2 |
9 files changed, 46 insertions, 50 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 288f75592..beced8c3c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -558,7 +558,7 @@ export class CurrentUserUtils { _width: 60, _height: 60, watchedDocuments, - onClick: ScriptField.MakeScript(click, { scriptContext: "any" }), system: true + onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) })); const userDoc = menuBtns[menuBtns.length - 1]; userDoc.userDoc = doc; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7b4d43793..19f1f8d15 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -13,7 +13,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { UndoManager } from "./UndoManager"; import { SnappingManager } from "./SnappingManager"; -export type dropActionType = "alias" | "copy" | "move" | "same" | "none" | undefined; // undefined = move +export type dropActionType = "alias" | "copy" | "move" | "same" | "none" | undefined; // undefined = move, "same" = move but don't call removeDropProperties export function SetupDrag( _reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc> | undefined, diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 569ad8ab4..05fb9f378 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -148,12 +148,15 @@ export namespace UndoManager { } }); - export function ClearTempBatch() { - tempEvents = undefined; - } export function RunInTempBatch<T>(fn: () => T) { tempEvents = []; - return runInAction(fn); + try { + const success = runInAction(fn); + if (!success) UndoManager.UndoTempBatch(); + return success; + } finally { + tempEvents = undefined; + } } //TODO Make this return the return value export function RunInBatch<T>(fn: () => T, batchName: string) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7f1023a4a..2391a21e6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -566,7 +566,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b (collectionAcl === AclAdmin || collectionAcl === AclEdit || docAcl === AclAdmin); }); const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection); - const closeIcon = !canDelete ? (null) : ( + const closeIcon = !canDelete ? <div></div> : ( <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" /> diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 3ebcda219..14c0a18a3 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -142,13 +142,14 @@ export class KeyManager { break; case "delete": case "backspace": - if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") { - return { stopPropagation: false, preventDefault: false }; + if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") { + const selected = SelectionManager.Views().slice(); + UndoManager.RunInBatch(() => { + SelectionManager.DeselectAll(); + selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)); + }, "delete"); + return { stopPropagation: true, preventDefault: true }; } - - const selected = SelectionManager.Views().slice(); - UndoManager.RunInBatch(() => selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)), "delete"); - SelectionManager.DeselectAll(); break; case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), "nudge left"); break; case "arrowright": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), "nudge right"); break; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 9ae469930..f305174f1 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -211,14 +211,7 @@ export class CollectionView extends Touchable<CollectionViewProps> { } const first = doc instanceof Doc ? doc : doc[0]; if (!first?._stayInCollection && addDocument !== returnFalse) { - if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) { - const added = addDocument(doc); - if (!added) UndoManager.UndoTempBatch(); - else UndoManager.ClearTempBatch(); - - return added; - } - UndoManager.ClearTempBatch(); + return UndoManager.RunInTempBatch(() => this.removeDocument(doc) && addDocument(doc)); } return false; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index aed6302ad..00e9456a1 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -39,14 +39,14 @@ export interface TreeViewProps { containingCollection: Doc; prevSibling?: Doc; renderDepth: number; - removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; - moveDocument: DragManager.MoveFunction; dropAction: dropActionType; addDocTab: (doc: Doc, where: string) => boolean; pinToPres: (document: Doc) => void; panelWidth: () => number; panelHeight: () => number; addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; + removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; + moveDocument: DragManager.MoveFunction; indentDocument?: (editTitle: boolean) => void; outdentDocument?: (editTitle: boolean) => void; ScreenToLocalTransform: () => Transform; @@ -263,20 +263,15 @@ export class TreeView extends React.Component<TreeViewProps> { 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 canAdd = !StrCast((inside ? this.props.document : this.props.containingCollection)?.freezeChildren).includes("add") || docDragData.treeViewDoc === this.props.treeView.props.Document; + const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false; + const addDoc = !inside ? parentAddDoc : + (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean); 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); + if (canAdd) { + UndoManager.RunInTempBatch(() => docDragData.droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) : addDoc(d)) || added, false)); + } } - return false; } refTransform = (ref: HTMLDivElement | undefined | null) => { @@ -401,16 +396,19 @@ export class TreeView extends React.Component<TreeViewProps> { 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}-sortCriteria`; - return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => { - if (this.props.active()) { - !this.outlineMode && (this.doc[sortKey] = - (this.doc[sortKey] === "ascending" ? "descending" : - (this.doc[sortKey] === "descending" ? "zorder" : - (this.doc[sortKey] === "zorder" ? undefined : - "ascending")))); - e.stopPropagation(); - } - }}> + let downX = 0, downY = 0; + return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} + onPointerDown={e => { downX = e.clientX; downY = e.clientY; }} + onClick={(e) => { + if (this.props.active() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { + !this.outlineMode && (this.doc[sortKey] = + (this.doc[sortKey] === "ascending" ? "descending" : + (this.doc[sortKey] === "descending" ? "zorder" : + (this.doc[sortKey] === "zorder" ? undefined : + "ascending")))); + 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, @@ -571,7 +569,7 @@ export class TreeView extends React.Component<TreeViewProps> { : <DocumentView key="title" ref={action((r: any) => { this._docRef = r ? r : undefined; - if (this._docRef && TreeView._editTitleOnLoad?.id === this.props.document[Id] && TreeView._editTitleOnLoad.parent == this.props.parentTreeView) { + if (this._docRef && TreeView._editTitleOnLoad?.id === this.props.document[Id] && TreeView._editTitleOnLoad.parent === this.props.parentTreeView) { this._docRef.select(false); this.setEditTitle(this._docRef); TreeView._editTitleOnLoad = undefined; @@ -772,7 +770,7 @@ export class TreeView extends React.Component<TreeViewProps> { parentCollectionDoc: Doc | undefined, parentPrevSibling: Doc | undefined, add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, - remove: ((doc: Doc | Doc[]) => boolean), + remove: undefined | ((doc: Doc | Doc[]) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, @@ -818,7 +816,7 @@ export class TreeView extends React.Component<TreeViewProps> { newParent.treeViewOpen = true; child.context = treeView.Document; } - } + }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, parentPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined)); const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 36d14056b..4008a20b3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -28,6 +28,7 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu"; import "./MarqueeView.scss"; import React = require("react"); import { StyleLayers } from "../../StyleProvider"; +import { TreeView } from "../TreeView"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -161,8 +162,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque slide.x = x; slide.y = y; FormattedTextBox.SelectOnLoad = slide[Id]; + TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined }; this.props.addDocument?.(slide); - //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false)); e.stopPropagation(); } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) { FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.childLayoutString ? e.key : ""; diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 6850f0601..7e3c4da82 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -299,7 +299,7 @@ export namespace WebSocket { diff.diff.$set = diff.diff.$remFromSet; delete diff.diff.$remFromSet; const updatefield = Array.from(Object.keys(diff.diff.$set))[0]; const remListItems = diff.diff.$set[updatefield].fields; - const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || []; + const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((f: any) => f !== null) || []; diff.diff.$set[updatefield].fields = curList?.filter((curItem: any) => !remListItems.some((remItem: any) => remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem)); const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length; delete diff.diff.length; |