From 62666034718378f4d970331665019b23659c1f3d Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 22 Mar 2023 17:01:17 -0400 Subject: visual changes to collaboration on doc and in PropertiesView --- src/client/views/PropertiesView.tsx | 96 +++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 20 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index e750dc43a..c34760d13 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -7,7 +7,7 @@ import { intersection } from 'lodash'; import { action, autorun, computed, Lambda, observable } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; +import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym, DocCastAsync } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -364,8 +364,13 @@ export class PropertiesView extends React.Component { */ @undoBatch changePermissions = (e: any, user: string) => { - const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym])); - SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); + if (user=="Public"){ + this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions; + } + else{ + const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym])); + SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); + } }; /** @@ -376,11 +381,11 @@ export class PropertiesView extends React.Component { if (permission === '-multiple-') dropdownValues.unshift(permission); if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1); return ( - this.changePermissions(e, user)}> {dropdownValues .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)) .map(permission => ( - @@ -433,8 +438,7 @@ export class PropertiesView extends React.Component { // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)} >
- {' '} - {name}{' '} + {' '}{name}{' '}
{/* {name !== "Me" ? this.notifyIcon : null} */}
@@ -445,13 +449,52 @@ export class PropertiesView extends React.Component { ); } + publicACLDropDown(admin: boolean, permission: string, showExpansionIcon?: boolean){ + var dropDownText = ""; + switch(StrCast(permission)){ + case ("Edit"): + dropDownText = "⬢ "; + break; + case ("Self-Edit"): + dropDownText = "⬢ "; + break; + case ("Augment"): + dropDownText = "⬟ "; + break; + case ("View"): + dropDownText = "♦ "; + break; + case ("Not-Shared"): + dropDownText = "▲ "; + break; + } + return ( +
+
+
+
+ {' '}{dropDownText}{' '} + {admin && permission !== 'Owner' ? this.getPermissionsSelect("Public", permission) : permission} + {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null} +
+
+
+
+ ); + } + /** * @returns the sharing and permissions panel. */ @computed get sharingTable() { + // const docToUse = this.selectedDocumentView?.rootDoc; + const docToUse = this.selectedDoc; + if (!docToUse){ + return null; + } // all selected docs const docs = - SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym])); + SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? docToUse : docToUse?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym])); const target = docs[0]; @@ -479,17 +522,27 @@ export class PropertiesView extends React.Component { // shifts the current user, owner, public to the top of the doc. // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-")); if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner')); - tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')); - tableEntries.unshift( - this.sharingItem( - 'Me', - showAdmin, - docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-', - !ownerSame - ) - ); - return
{tableEntries}
; + // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')); + // tableEntries.unshift( + // this.sharingItem( + // 'Me', + // showAdmin, + // docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-', + // !ownerSame + // ) + // ); + docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, doc['acl-Public'], true))); + + return ( +
Sharing Mode +
{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}
+


Who has access to the Dashboard?
+
{ +
{tableEntries}
+ }
+
+ ); } @computed get fieldsCheckbox() { @@ -1116,12 +1169,15 @@ export class PropertiesView extends React.Component { {!this.openSharing ? null : (
- {!Doc.noviceMode ? ( + + + {/* {!Doc.noviceMode ? ( // what is the layout checkbox for?
(this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
Layout
- ) : null} + ) : null} */} + {/*
{"Re-distribute sharing settings"}
}>
} placement="top"> -
-
- -
e.stopPropagation()}> - -
-
-
-
Perspective
-
- - ); - } + // @computed + // get perspectiveButton() { //WHERE ARE YOU + // return !this.selectedDoc ? null : ( + // Choose view perspective
} placement="top"> + //
+ //
+ // + //
e.stopPropagation()}> + // + //
+ //
+ //
+ //
Perspective
+ //
+ // + // ); + // } @undoBatch handlePerspectiveChange = (e: any) => { @@ -447,13 +447,13 @@ export class PropertiesButtons extends React.Component<{}, {}> { {/* {toggle(this.onClickButton)} */} {toggle(this.layout_fitWidthButton)} {/* {toggle(this.freezeThumb)} */} - {/* {toggle(this.forceActiveButton)} */} + {toggle(this.forceActiveButton)} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */} - {/* {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} */} + {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} - {/* {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} */} + {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })} {/* {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })} */} {/* {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })} */} diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 49f2fc6fe..a9577b232 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -14,6 +14,10 @@ overflow-x: hidden; overflow-y: auto; + .propertiesView-propAndInfoGrouping{ + display: flex; + } + .propertiesView-title { text-align: left; padding-top: 12px; @@ -40,6 +44,13 @@ } } + .propertiesView-info{ + margin-top: 20; + margin-right: 10; + float: right; + font-size: 20; + } + .propertiesView-name { // border-bottom: 1px solid black; padding: 8.5px; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 9c327b3f1..6b208f25e 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1,6 +1,6 @@ import React = require('react'); import { IconLookup } from '@fortawesome/fontawesome-svg-core'; -import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; +import { faAnchor, faArrowRight, faListNumeric, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Icon, Tooltip } from '@material-ui/core'; import { intersection } from 'lodash'; @@ -37,6 +37,7 @@ import { DefaultStyleProvider } from './StyleProvider'; import { RichTextField } from '../../fields/RichTextField'; import { AiFillFileText } from "react-icons/ai" //* as Icons from "react-icons/ai" // import * as Icons from "react-icons/bs" //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" +import { GrCircleInformation } from 'react-icons/gr' import { CgBrowser} from "react-icons/cg" import { ImageField, VideoField, WebField } from '../../fields/URLField'; import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; // @@ -114,6 +115,9 @@ export class PropertiesView extends React.Component { @observable inOptions: boolean = false; @observable _controlButton: boolean = false; + + @observable allClose: boolean = false; + componentDidMount() { this.selectedDocListenerDisposer?.(); // this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc()); @@ -504,7 +508,7 @@ export class PropertiesView extends React.Component { } @computed get currentType() { - console.log("current type " + this.selectedDoc?.type) + // console.log("current type " + this.selectedDoc?.type) const documentType = StrCast(this.selectedDoc?.type) var currentType: string = documentType; @@ -1201,37 +1205,23 @@ export class PropertiesView extends React.Component { ); } - @computed get doubleClickClose() { - //action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = !this.openTransform)) - //console.log(this.openContexts + " hello " + this.openOptions + this.openTransform + this.openLinks + this.openFields) - if (this.openContexts || this.openOptions || this.openTransform || this.openLinks || this.openFields){ - this.openContexts = false; - this.openOptions = false; - this.openTransform = false; - this.openLinks = false; - this.openFields = false; - - } else if (!this.openContexts && !this.openOptions && !this.openTransform && !this.openLinks && !this.openFields){ - this.openContexts = true; - this.openOptions = true; - this.openTransform = true; - this.openLinks = true; - this.openFields = true; - } - - return ( - console.log("made") - ) - } +// create observable with current property +// if everyother one is closed, set allClsoe = true +// on double click close all except current +// if everything is closed on double clikc then open everythigng + +// if everythign is closed except current property ==> double click = open all +// else ==> double click = close except current property @computed get linksSubMenu() { // onPointerDown={action(() => (this.openLinks = !this.openLinks))} - + //(this.openContexts = false, this.openOptions = false, this.openTransform = false))} onClick={action(() => (this.openLinks = !this.openLinks)) + //this.allClose == false ? (this.openContexts = false , this.allClose = true ): (this.openContexts = true, this.allClose = false) return (
-
( this.doubleClickClose, this.openLinks = true))} onClick={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}> +
Linked To
@@ -1492,9 +1482,16 @@ export class PropertiesView extends React.Component { minWidth: this.props.width, //overflowY: this.scrolling ? "scroll" : "visible" }}> -
- Properties +
+
+ Properties +
+
window.open('https://brown-dash.github.io/Dash-Documentation/')}> +
+
+ +
{this.editableTitle}
{this.currentType}
-- cgit v1.2.3-70-g09d2 From 899a6bdeb8ab0ce749328fc6093496daada1c4ad Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 21 Jun 2023 10:17:31 -0400 Subject: sort groups in PropertiesView --- src/client/views/PropertiesView.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index ab3864849..825a802cd 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -412,6 +412,15 @@ export class PropertiesView extends React.Component { return u1 > u2 ? -1 : u1 === u2 ? 0 : 1; }; + /** + * Sorting algorithm to sort groups. + */ + sortGroups = (group1: Doc, group2: Doc) => { + const g1 = StrCast(group1.title); + const g2 = StrCast(group2.title); + return g1 > g2 ? -1 : g1 === g2 ? 0 : 1; + }; + /** * @returns the sharing and permissions panel. */ @@ -459,6 +468,7 @@ export class PropertiesView extends React.Component { // adds groups const groupTableEntries: JSX.Element[] = []; const groupList = GroupManager.Instance?.allGroups || []; + groupList.sort(this.sortGroups) groupList.map(group => { if (group.title != 'Public' && this.selectedDoc) { const groupKey = 'acl-' + normalizeEmail(StrCast(group.title)); -- cgit v1.2.3-70-g09d2 From c357aefb0668780b3bb4f96270886e0570723d09 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 21 Jun 2023 12:31:14 -0400 Subject: double click working with small bug --- src/client/views/PropertiesView.tsx | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 6b208f25e..89a355ae3 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -43,7 +43,7 @@ import { ImageField, VideoField, WebField } from '../../fields/URLField'; import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; // import { IconButton } from 'browndash-components'; import { IconBase } from 'react-icons'; -import { MdOutlineMedicalServices } from 'react-icons/md'; +import { MdOutlineAddShoppingCart, MdOutlineMedicalServices } from 'react-icons/md'; import { createPromiseCapability } from 'pdfjs-dist'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; @@ -115,9 +115,6 @@ export class PropertiesView extends React.Component { @observable inOptions: boolean = false; @observable _controlButton: boolean = false; - - @observable allClose: boolean = false; - componentDidMount() { this.selectedDocListenerDisposer?.(); // this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc()); @@ -1055,7 +1052,9 @@ export class PropertiesView extends React.Component { @computed get optionsSubMenu() { return (
(this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}> -
(this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}> +
(this.openContexts = false, this.openLinks = false, this.openTransform = false, + this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))} + onClick={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}> Options
@@ -1073,7 +1072,9 @@ export class PropertiesView extends React.Component { @computed get sharingSubMenu() { return (
-
(this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}> +
(this.openContexts = false, this.openLinks = false, this.openTransform = false, + this.openOptions = false, this.openFields = false, this.openFilters = false, this.openLayout))} + onClick={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}> Sharing {'&'} Permissions
@@ -1128,7 +1129,9 @@ export class PropertiesView extends React.Component { @computed get filtersSubMenu() { return (
-
(this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}> +
(this.openContexts = false, this.openLinks = false, this.openTransform = false, + this.openOptions = false, this.openFields = false, this.openSharing = false, this.openLayout = false))} + onClick={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}> Filters
@@ -1159,7 +1162,9 @@ export class PropertiesView extends React.Component { )}
-
(this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}> +
(this.openTransform = !this.openTransform))} onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openOptions = false, + this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false, this.openTransform = true))} + style={{ backgroundColor: this.openTransform ? 'black' : '' }}> Transform
@@ -1174,7 +1179,9 @@ export class PropertiesView extends React.Component { @computed get fieldsSubMenu() { return (
-
(this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}> +
(this.openContexts = false, this.openLinks = false, this.openTransform = false, + this.openOptions = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))} + onClick={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}> Fields {'&'} Tags
@@ -1194,7 +1201,9 @@ export class PropertiesView extends React.Component { @computed get contextsSubMenu() { return (
-
(this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}> +
(this.openLinks = false, this.openTransform = false, this.openOptions = false, this.openFields = false, + this.openSharing = false, this.openFilters = false, this.openLayout = false ))} + onClick ={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}> Other Contexts
@@ -1205,30 +1214,22 @@ export class PropertiesView extends React.Component { ); } + @computed get linksSubMenu() { + var isDouble: boolean = false; -// create observable with current property -// if everyother one is closed, set allClsoe = true -// on double click close all except current -// if everything is closed on double clikc then open everythigng - -// if everythign is closed except current property ==> double click = open all -// else ==> double click = close except current property - @computed get linksSubMenu() { - // onPointerDown={action(() => (this.openLinks = !this.openLinks))} - //(this.openContexts = false, this.openOptions = false, this.openTransform = false))} onClick={action(() => (this.openLinks = !this.openLinks)) - //this.allClose == false ? (this.openContexts = false , this.allClose = true ): (this.openContexts = true, this.allClose = false) return (
-
+
(this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, + this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))} + style={{ backgroundColor: this.openLinks ? 'black' : '' }}> Linked To
{!this.openLinks ? null : this.linkCount > 0 ?
{this.links}
:
There are no current links.
} - {/* {!this.openLinks ? null :
{this.links}
} */}
); } @@ -1236,7 +1237,9 @@ export class PropertiesView extends React.Component { @computed get layoutSubMenu() { return (
-
(this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}> +
(this.openContexts = false, this.openLinks = false, this.openTransform = false, + this.openOptions = false, this.openFields = false, this.openSharing = false, this.openFilters = false ))} + onClick={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}> Layout
-- cgit v1.2.3-70-g09d2 From ddf20606684cd8755571a82eed1c4ffdc27f9274 Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Wed, 21 Jun 2023 15:15:00 -0400 Subject: fixed double click bug for links --- src/client/views/PropertiesView.tsx | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 89a355ae3..a0c45bc70 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1214,15 +1214,40 @@ export class PropertiesView extends React.Component { ); } + + @computed get linksSubMenu() { - var isDouble: boolean = false; +//onClick={action(() => (this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, +//this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))} + let isDouble = false; return (
-
(this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, - this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))} +
{ + if (!isDouble){ + this.openLinks = !this.openLinks + } } )} + + onDoubleClick = {action(() => { + isDouble = true; + + this.openContexts = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = false; + this.openSharing = false; + this.openLayout = false; + this.openFilters = false; + this.openLinks = true; + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} style={{ backgroundColor: this.openLinks ? 'black' : '' }}> Linked To
-- cgit v1.2.3-70-g09d2 From 72dad52c4fea5a2fccabacbb6a49ca1494093954 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Jun 2023 10:14:26 -0400 Subject: cleaned up dragging - abort presItem drags properly, fix naming for drag/dropAction, small fix undo of drag removeproperties, dragging in/out of headerBar, fixed isContentActive to avoid SnappingManager.GetIsDragging. fixed resizing videos. --- src/client/documents/Documents.ts | 49 +++++------ src/client/util/CurrentUserUtils.ts | 81 +++++++++--------- src/client/util/DragManager.ts | 38 ++++----- src/client/util/DropConverter.ts | 8 +- src/client/util/UndoManager.ts | 19 ++--- src/client/views/DashboardView.tsx | 6 +- src/client/views/DocComponent.tsx | 6 +- src/client/views/DocumentDecorations.tsx | 8 +- src/client/views/GestureOverlay.tsx | 95 +--------------------- src/client/views/MainView.tsx | 12 ++- src/client/views/Palette.scss | 30 ------- src/client/views/Palette.tsx | 69 ---------------- src/client/views/PropertiesView.tsx | 1 - src/client/views/StyleProvider.tsx | 1 + src/client/views/TemplateMenu.tsx | 5 +- .../views/collections/CollectionCarouselView.tsx | 4 +- .../views/collections/CollectionNoteTakingView.tsx | 6 +- .../views/collections/CollectionPileView.tsx | 11 ++- .../collections/CollectionStackedTimeline.scss | 2 +- .../collections/CollectionStackedTimeline.tsx | 10 ++- .../views/collections/CollectionStackingView.tsx | 18 ++-- src/client/views/collections/CollectionSubView.tsx | 10 +-- .../views/collections/CollectionTimeView.tsx | 2 - .../views/collections/CollectionTreeView.tsx | 14 ++-- src/client/views/collections/CollectionView.tsx | 5 +- src/client/views/collections/TabDocView.tsx | 1 - src/client/views/collections/TreeView.tsx | 29 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++- .../collections/collectionFreeForm/MarqueeView.tsx | 1 - .../collectionGrid/CollectionGridView.tsx | 2 +- .../collectionLinear/CollectionLinearView.tsx | 40 ++------- .../CollectionMulticolumnView.tsx | 18 ++-- .../CollectionMultirowView.tsx | 19 +++-- .../collectionSchema/CollectionSchemaView.tsx | 3 +- .../collections/collectionSchema/SchemaRowBox.tsx | 8 +- .../collectionSchema/SchemaTableCell.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/linking/LinkPopup.tsx | 1 - src/client/views/nodes/ComparisonBox.scss | 2 +- src/client/views/nodes/ComparisonBox.tsx | 80 ++++++++++++------ src/client/views/nodes/DocumentView.tsx | 52 +++++++----- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 4 +- src/client/views/nodes/KeyValueBox.tsx | 8 +- src/client/views/nodes/KeyValuePair.tsx | 1 - src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 5 +- src/client/views/nodes/MapBox/MapBox2.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 - src/client/views/nodes/WebBox.tsx | 14 +++- .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++--- src/client/views/nodes/trails/PresBox.tsx | 4 +- src/client/views/nodes/trails/PresElementBox.tsx | 13 +-- src/client/views/pdf/PDFViewer.tsx | 18 ++-- src/fields/Doc.ts | 2 +- src/fields/documentSchemas.ts | 10 +-- src/fields/util.ts | 4 +- 57 files changed, 356 insertions(+), 537 deletions(-) delete mode 100644 src/client/views/Palette.scss delete mode 100644 src/client/views/Palette.tsx (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ffde9fe1b..a61ef1da3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -25,7 +25,7 @@ import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox'; import { FollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; +import { undoable, UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; import { CollectionView } from '../views/collections/CollectionView'; @@ -184,7 +184,8 @@ export class DocumentOptions { opacity?: NUMt = new NumInfo('document opacity'); viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters'); dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc'); - dontUndo?: BOOLt = new BoolInfo('whether button clicks should be undoable (true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'); + _undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' + undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title'); _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes'); _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume'); @@ -355,20 +356,19 @@ export class DocumentOptions { waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait onPointerDown?: ScriptField; onPointerUp?: ScriptField; - dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); openFactoryLocation?: string; // an OpenWhere value to place the factory created document openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory'); _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)'); - enableDragWhenActive?: BOOLt = new BoolInfo('allow dragging even if document contentts are active (e.g., tree, groups)'); - _stayInCollection?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere'); + _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere'); _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.'); - allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true); - childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); - targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? '); + childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged fromt he collection'); + dropConverter?: ScriptField; // script to run when documents are dropped on this Document. + dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); - _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document + _dropPropertiesToRemove?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; - dropConverter?: ScriptField; // script to run when documents are dropped on this Document. + dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)'); + dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop @@ -536,7 +536,7 @@ export namespace Docs { link_description: '', layout_showCaption: 'link_description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area - _removeDropProperties: new List(['onClick']), + _dropPropertiesToRemove: new List(['onClick']), }, }, ], @@ -545,7 +545,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'embed', title: 'Global Link Database' }, + options: { title: 'Global Link Database' }, }, ], [ @@ -553,7 +553,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'embed', title: 'Global Script Database' }, + options: { title: 'Global Script Database' }, }, ], [ @@ -615,7 +615,7 @@ export namespace Docs { DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: 'icon' }, - options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, + options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 }, }, ], [ @@ -659,7 +659,7 @@ export namespace Docs { { data: '', layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { backgroundColor: 'gray', targetDropAction: 'embed' }, + options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always' }, }, ], [ @@ -667,7 +667,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'embed', title: 'Global Group Database' }, + options: { childDragAction: 'embed', title: 'Global Group Database' }, }, ], [ @@ -1066,12 +1066,7 @@ export namespace Docs { } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto( - Prototypes.get(DocumentType.COL), - new List(documents), - { enableDragWhenActive: true, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, - id - ); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { @@ -1523,7 +1518,7 @@ export namespace DocUtils { !simpleMenu && ContextMenu.Instance.addItem({ description: 'Quick Notes', - subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({ + subitems: DocListCast((Doc.UserDoc()['template_notes'] as Doc).data).map((note, i) => ({ description: ':' + StrCast(note.title), event: undoable((args: { x: number; y: number }) => { const textDoc = Docs.Create.TextDocument('', { @@ -1583,10 +1578,10 @@ export namespace DocUtils { } export function findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data); + const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data); const allTemplates = iconViews .concat(templBtns) .concat(noteTypes) diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 592f6d836..11a8dcaf6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -97,7 +97,7 @@ export class CurrentUserUtils { const reqdOpts:DocumentOptions = { title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, - _stayInCollection: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true, + _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; @@ -123,7 +123,7 @@ export class CurrentUserUtils { } /// Initializes templates for editing click funcs of a document - static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") { + static setupClickEditorTemplates(doc: Doc, field = "template_clickFuncs") { const tempClicks = DocCast(doc[field]); const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ @@ -144,7 +144,7 @@ export class CurrentUserUtils { } /// Initializes templates that can be applied to notes - static setupNoteTemplates(doc: Doc, field="template-notes") { + static setupNoteTemplates(doc: Doc, field="template_notes") { const tempNotes = DocCast(doc[field]); const reqdTempOpts:DocumentOptions[] = [ { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"}, @@ -174,7 +174,7 @@ export class CurrentUserUtils { } // setup templates for different document types when they are iconified from Document Decorations - static setupDefaultIconTemplates(doc: Doc, field="template-icons") { + static setupDefaultIconTemplates(doc: Doc, field="template_icons") { const reqdOpts = { title: "icon templates", _height: 75, isSystem: true }; const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); @@ -279,11 +279,11 @@ export class CurrentUserUtils { {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}}, - {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }}, + {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, - allowOverlayDrop: true, treeViewType: TreeViewType.outline, + dropAction:'move', treeViewType: TreeViewType.outline, backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true }, funcs: {title: 'self.text?.Text'}}, ]; @@ -322,17 +322,16 @@ export class CurrentUserUtils { const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, - _width: 35, _height: 35, _layout_hideContextMenu: true, _stayInCollection: true, + _width: 35, _height: 35, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true, btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true, - _removeDropProperties: new List(["_stayInCollection"]), }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs); }); const reqdOpts:DocumentOptions = { - title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true, + title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, - childDropAction: 'embed' + childDragAction: 'embed' }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts); @@ -357,7 +356,7 @@ export class CurrentUserUtils { /// the empty panel that is filled with whichever left menu button's panel has been selected static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") { - DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true}); + DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List(['proto'])}); } /// Initializes the left sidebar menu buttons and the panels they open up @@ -367,15 +366,14 @@ export class CurrentUserUtils { const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => { const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { - title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true, - _width: 60, _height: 60, _stayInCollection: true, _layout_hideContextMenu: true, - _removeDropProperties: new List(["_stayInCollection"]), + title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List(['height', 'data_columnHeaders']), dontRegisterView: true, + _width: 60, _height: 60, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs); }); const reqdStackOpts:DocumentOptions ={ - title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, + title: "menuItemPanel", childDragAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" }); @@ -458,7 +456,7 @@ export class CurrentUserUtils { /// Search option on the left side button panel static setupSearcher(doc: Doc, field:string) { return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), { - dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", + dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: "embed", _lockedPosition: true, _type_collection: CollectionViewType.Schema }); } @@ -469,7 +467,7 @@ export class CurrentUserUtils { const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined); const reqdToolOps:DocumentOptions = { title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0", - _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, + _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]); } @@ -481,7 +479,7 @@ export class CurrentUserUtils { const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; const newDashboard = `createNewDashboard()`; - const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; const reqdBtnScript = {onClick: newDashboard,} const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); @@ -495,8 +493,8 @@ export class CurrentUserUtils { const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters const reqdOpts:DocumentOptions = { title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true, - targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, - layout_headerButton: newDashboardButton, childDropAction: "embed", + dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, + layout_headerButton: newDashboardButton, childDragAction: "embed", _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, contextMenuLabels:new List(contextMenuLabels), contextMenuIcons:new List(contextMenuIcons), @@ -520,20 +518,20 @@ export class CurrentUserUtils { /// initializes the left sidebar File system pane static setupFilesystem(doc: Doc, field:string) { var myFilesystem = DocCast(doc[field]); - const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, isSystem: true, isFolder: true }); + const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", undoIgnoreFields:new List(['treeViewSortCriterion']), _dragOnlyWithinContainer: true, isSystem: true, isFolder: true }); const newFolder = `TreeView_addNewFolder()`; const newFolderOpts: DocumentOptions = { - _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _width: 30, _height: 30, + _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List(['treeViewSortCriterion']), title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true }; const newFolderScript = { onClick: newFolder}; const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript); const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, - title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true, + title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true, isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, - treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed", + treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed", childContextMenuLabels: new List(["Create new folder"]), childContextMenuIcons: new List(["plus"]), layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." @@ -549,8 +547,8 @@ export class CurrentUserUtils { /// initializes the panel displaying docs that have been recently closed static setupRecentlyClosed(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, - title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true, - treeViewTruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", + title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", isSystem: true, + treeViewTruncateTitleWidth: 350, ignoreClick: true, layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", contextMenuLabels: new List(["Empty recently closed"]), contextMenuIcons:new List(["trash"]), layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." @@ -558,7 +556,7 @@ export class CurrentUserUtils { const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const clearAll = (target:string) => `getProto(${target}).data = new List([])`; - const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, + const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", isSystem: true, toolTip: "Empty recently closed",}; DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")}); @@ -575,7 +573,7 @@ export class CurrentUserUtils { static setupUserDocView(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view", - layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true, + layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true, treeViewHideTitle: true, treeViewTruncateTitleWidth: 350 }; if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }); @@ -590,7 +588,7 @@ export class CurrentUserUtils { static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, - _removeDropProperties: new List([ "_layout_hideContextMenu", "stayInCollection"]), + _dropPropertiesToRemove: new List([ "_layout_hideContextMenu"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts, }) @@ -608,9 +606,9 @@ export class CurrentUserUtils { { scripts: { }, opts: { title: "linker", layout: "", toolTip: "link started"}}, { scripts: { }, opts: { title: "currently playing", layout: "", toolTip: "currently playing media"}}, ]; - const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); + const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { - title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDropAction: 'embed', + title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move', childDontRegisterViews: true, linearView_IsExpanded: true, linearView_Expandable: true, ignoreClick: true }; reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); @@ -716,12 +714,11 @@ export class CurrentUserUtils { const reqdOpts:DocumentOptions = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, backgroundColor: params.backgroundColor ??"transparent", /// a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below). Otherwise, assume a transparent background - color: Colors.WHITE, isSystem: true, //dontUndo: true, + color: Colors.WHITE, isSystem: true, _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, - _stayInCollection: true, _layout_hideContextMenu: true, _lockedPosition: true, - _removeDropProperties: new List([ "_stayInCollection"]), + _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _lockedPosition: true, }; const reqdFuncs:{[key:string]:any} = { ...params.funcs, @@ -732,14 +729,14 @@ export class CurrentUserUtils { /// Initializes all the default buttons for the top bar context menu static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { - const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 }; + const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List(['width', "linearView_IsExpanded"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true, linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 }; const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined); const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); if (!params.subMenu) { return this.setupContextMenuButton(params, menuBtnDoc); } else { - const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true, + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List(['width', "linearView_IsExpanded"]), childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true, linearView_SubMenu: true, linearView_Expandable: true, }; const items = params.subMenu?.map(sub => @@ -786,13 +783,13 @@ export class CurrentUserUtils { const sharedDocOpts:DocumentOptions = { title: "My Shared Docs", userColor: "rgb(202, 202, 202)", - isFolder:true, + isFolder:true, undoIgnoreFields:new List(['treeViewSortCriterion']), childContextMenuFilters: new List([dashboardFilter!,]), childContextMenuScripts: new List([addToDashboards!,]), childContextMenuLabels: new List(["Add to Dashboards",]), childContextMenuIcons: new List(["user-plus",]), "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, + childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" @@ -805,14 +802,14 @@ export class CurrentUserUtils { static setupImportSidebar(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", - _stayInCollection: true, _layout_hideContextMenu: true, childLimitHeight: 0, - childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true, + _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0, + childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true, dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go." }; const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts); const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", - _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, + _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", isSystem: true }; DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); return myImports; @@ -864,7 +861,7 @@ export class CurrentUserUtils { this.setupDocTemplates(doc); // sets up the template menu of templates this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); - DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents + DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs) diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 87833d322..85101fcab 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,42 +15,36 @@ import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; -export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties +export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove /** * Initialize drag * @param _reference: The HTMLElement that is being dragged * @param docFunc: The Dash document being moved - * @param moveFunc: The function called when the document is moved - * @param dropAction: What to do with the document when it is dropped - * @param dragStarted: Method to call when the drag is started */ -export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, dragStarted?: () => void) { - const onRowMove = async (e: PointerEvent) => { +export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | undefined) { + const onRowMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener('pointermove', onRowMove); document.removeEventListener('pointerup', onRowUp); - const doc = await docFunc(); + const doc = docFunc(); if (doc) { const dragData = new DragManager.DocumentDragData([doc]); - dragData.dropAction = dropAction; - dragData.moveDocument = moveFunc; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); - dragStarted?.(); } }; const onRowUp = (): void => { document.removeEventListener('pointermove', onRowMove); document.removeEventListener('pointerup', onRowUp); }; - const onItemDown = async (e: React.PointerEvent) => { + const onItemDown = (e: React.PointerEvent) => { if (e.button === 0) { e.stopPropagation(); if (e.shiftKey) { e.persist(); - const dragDoc = await docFunc(); + const dragDoc = docFunc(); dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]); } else { document.addEventListener('pointermove', onRowMove); @@ -132,7 +126,7 @@ export namespace DragManager { userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move' - removeDropProperties?: string[]; + dropPropertiesToRemove?: string[]; moveDocument?: MoveFunction; removeDocument?: RemoveFunction; isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts @@ -189,7 +183,7 @@ export namespace DragManager { const handler = (e: Event) => dropFunc(e, (e as CustomEvent).detail); const preDropHandler = (e: Event) => { const de = (e as CustomEvent).detail; - (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.targetDropAction) as dropActionType); + (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.dropAction) as dropActionType); }; element.addEventListener('dashOnDrop', handler); doc && element.addEventListener('dashPreDrop', preDropHandler); @@ -230,11 +224,13 @@ export namespace DragManager { ) ).filter(d => d); !['same', 'proto'].includes(docDragData.dropAction as any) && - docDragData.droppedDocuments.forEach((drop: Doc, i: number) => { - const dragProps = StrListCast(dragData.draggedDocuments[i].removeDropProperties); - const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps)); - remProps.map(prop => (drop[prop] = undefined)); - }); + docDragData.droppedDocuments + // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any)) + .forEach((drop: Doc, i: number) => { + const dragProps = StrListCast(dragData.draggedDocuments[i].dropPropertiesToRemove); + const remProps = (dragData?.dropPropertiesToRemove || []).concat(Array.from(dragProps)); + [...remProps, 'dropPropertiesToRemove'].map(prop => (drop[prop] = undefined)); + }); } return e; }; @@ -586,6 +582,7 @@ export namespace DragManager { async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) { const dropArgs = { + cancelable: true, // allows preventDefault() to be called to cancel the drop bubbles: true, detail: { ...pos, @@ -598,8 +595,9 @@ export namespace DragManager { }, }; target.dispatchEvent(new CustomEvent('dashPreDrop', dropArgs)); + UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes) await finishDrag?.(complete); - target.dispatchEvent(new CustomEvent('dashOnDrop', dropArgs)); + UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called options?.dragComplete?.(complete); endDrag?.(); } diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index f46ea393a..47997cc5c 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -57,11 +57,11 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) { - if (data.removeDropProperties || dbox.removeDropProperties) { + if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) { //dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon dbox = Doc.MakeEmbedding(dbox); - const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []); - const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps)); + const dragProps = Cast(dbox.dropPropertiesToRemove, listSpec('string'), []); + const remProps = (data.dropPropertiesToRemove || []).concat(Array.from(dragProps)); remProps.map(prop => (dbox[prop] = undefined)); } } else if (!doc.onDragStart && !doc.isButtonBar) { @@ -81,7 +81,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt', }); dbox.dragFactory = layoutDoc; - dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; + dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined; dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)'); } else if (doc.isButtonBar) { dbox.ignoreClick = true; diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 6fef9d660..2379cb2ab 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -91,7 +91,7 @@ export namespace UndoManager { let currentBatch: UndoBatch | undefined; export let batchCounter = observable.box(0); let undoing = false; - let tempEvents: UndoEvent[] | undefined = undefined; + export let tempEvents: UndoEvent[] | undefined = undefined; export function AddEvent(event: UndoEvent, value?: any): void { if (currentBatch && batchCounter.get() && !undoing) { @@ -187,15 +187,11 @@ export namespace UndoManager { return false; }); - export function RunInTempBatch(fn: () => T) { + export function StartTempBatch() { tempEvents = []; - try { - const success = runInAction(fn); - if (!success) UndoManager.UndoTempBatch(); - return success; - } finally { - tempEvents = undefined; - } + } + export function EndTempBatch(success: boolean) { + UndoManager.UndoTempBatch(success); } //TODO Make this return the return value export function RunInBatch(fn: () => T, batchName: string) { @@ -206,10 +202,11 @@ export namespace UndoManager { batch.end(); } } - export const UndoTempBatch = action(() => { - if (tempEvents) { + export const UndoTempBatch = action((success: any) => { + if (tempEvents && !success) { undoing = true; for (let i = tempEvents.length - 1; i >= 0; i--) { + currentBatch?.includes(tempEvents[i]) && currentBatch.splice(currentBatch.indexOf(tempEvents[i])); tempEvents[i].undo(); } undoing = false; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 50cf2226e..6aae302ac 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -389,7 +389,7 @@ export class DashboardView extends React.Component { _forceActive: true, _width: 30, _height: 30, - _stayInCollection: true, + _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: 'New trail', toolTip: 'Create new trail', @@ -412,7 +412,7 @@ export class DashboardView extends React.Component { _layout_fitWidth: true, _gridGap: 5, _forceActive: true, - childDropAction: 'embed', + childDragAction: 'embed', treeViewTruncateTitleWidth: 150, ignoreClick: true, layout_headerButton: myTrailsBtn, @@ -421,7 +421,7 @@ export class DashboardView extends React.Component { _lockedPosition: true, layout_boxShadow: '0 0', childDontRegisterViews: true, - targetDropAction: 'same', + dropAction: 'same', isSystem: true, layout_explainer: 'All of the trails that you have created will appear here.', }; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index db24229dc..0709d6cb9 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -172,8 +172,8 @@ export function ViewBoxAnnotatableComponent

() return true; } const first = doc instanceof Doc ? doc : doc[0]; - if (!first?._stayInCollection && addDocument !== returnFalse) { - return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey)); + if (!first?._dragOnlyWithinContainer && addDocument !== returnFalse) { + return this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey); } return false; }; @@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent

() .map(doc => { // only make a pushpin if we have acl's to edit the document //DocUtils.LeavePushpin(doc); - doc._stayInCollection = undefined; + doc._dragOnlyWithinContainer = undefined; doc.embedContainer = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 528b82e3e..41ce08d3c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -603,7 +603,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P let actualdH = Math.max(docheight + dH * scale, 20); let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth)); let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight)); - const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false; + const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable; const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { @@ -636,7 +636,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } } if (!modifyNativeDim) { - actualdH = (nheight / nwidth) * docwidth; //, actualdH); + actualdH = (nheight / nwidth) * NumCast(doc._width); //, actualdH); } doc._height = actualdH; } @@ -756,7 +756,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P hideDecorations || seldocview.props.hideOpenButton || seldocview.rootDoc.layout_hideOpenButton || - SelectionManager.Views().some(docView => docView.rootDoc._stayInCollection || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) || + SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P seldocview.rootDoc.hideDeleteButton || SelectionManager.Views().some(docView => { const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit; - return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); + return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin; }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0951bff22..54ad519de 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -4,8 +4,7 @@ import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../fields/Doc'; import { InkData, InkTool } from '../../fields/InkField'; -import { ScriptField } from '../../fields/ScriptField'; -import { Cast, FieldValue, NumCast } from '../../fields/Types'; +import { NumCast } from '../../fields/Types'; import MobileInkOverlay from '../../mobile/MobileInkOverlay'; import { GestureUtils } from '../../pen-gestures/GestureUtils'; import { MobileInkOverlayContent } from '../../server/Message'; @@ -35,7 +34,6 @@ import { InkTranscription } from './InkTranscription'; import { checkInksToGroup } from './nodes/button/FontIconBox'; import { DocumentView } from './nodes/DocumentView'; import { RadialMenu } from './nodes/RadialMenu'; -import HorizontalPalette from './Palette'; import { Touchable } from './Touchable'; import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu'; @@ -79,7 +77,6 @@ export class GestureOverlay extends Touchable { private _overlayRef = React.createRef(); private _d1: Doc | undefined; private _inkToTextDoc: Doc | undefined; - private _thumbDoc: Doc | undefined; private thumbIdentifier?: number; private pointerIdentifier?: number; private _hands: Map = new Map(); @@ -93,78 +90,12 @@ export class GestureOverlay extends Touchable { GestureOverlay.Instances.push(this); } - static setupThumbButtons(doc: Doc) { - const docProtoData: { title: string; icon: string; drag?: string; toolType?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [ - { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' }, - { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' }, - { - title: 'notepad', - icon: 'clipboard', - pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()', - pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', - clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }), - backgroundColor: 'orange', - }, - { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' }, - { title: 'ignore gestures', icon: 'signature', toolType: 'ignoregesture', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'green' }, - ]; - return docProtoData.map(data => - Docs.Create.FontIconDocument({ - _nativeWidth: 10, - _nativeHeight: 10, - _width: 10, - _height: 10, - title: data.title, - icon: data.icon, - toolType: data.toolType, - _dropAction: data.pointerDown ? 'copy' : undefined, - ignoreClick: data.ignoreClick, - onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, - clipboard: data.clipboard, - onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, - onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, - backgroundColor: data.backgroundColor, - dragFactory: data.dragFactory, - isSystem: true, - }) - ); - } - - static setupThumbDoc(userDoc: Doc) { - if (!userDoc.thumbDoc) { - const thumbDoc = Docs.Create.LinearDocument(GestureOverlay.setupThumbButtons(userDoc), { - _width: 100, - _height: 50, - ignoreClick: true, - _lockedPosition: true, - title: 'buttons', - _layout_autoHeight: true, - _yMargin: 5, - linearView_IsExpanded: true, - backgroundColor: 'white', - isSystem: true, - }); - thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], { - _width: 300, - _height: 25, - _layout_autoHeight: true, - linearView_IsExpanded: true, - flexDirection: 'column', - isSystem: true, - }); - userDoc.thumbDoc = thumbDoc; - } - return Cast(userDoc.thumbDoc, Doc); - } - componentWillUnmount() { GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1); GestureOverlay.Instance = GestureOverlay.Instances.lastElement(); } componentDidMount = () => { GestureOverlay.Instance = this; - this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc)); - this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc)); }; // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group @@ -395,24 +326,6 @@ export class GestureOverlay extends Touchable { this.thumbIdentifier = thumb?.identifier; this._hands.set(thumb.identifier, fingers); - const others = fingers.filter(f => f !== thumb); - const minX = Math.min(...others.map(f => f.clientX)); - const minY = Math.min(...others.map(f => f.clientY)); - - // load up the palette collection around the thumb - const thumbDoc = await Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc); - if (thumbDoc) { - runInAction(() => { - RadialMenu.Instance._display = false; - this._inkToTextDoc = FieldValue(Cast(thumbDoc.inkToTextDoc, Doc)); - this._thumbDoc = thumbDoc; - this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; - this._menuX = thumb.clientX + 50; - this._menuY = thumb.clientY; - this._palette = ; - }); - } this.removeMoveListeners(); document.removeEventListener('touchmove', this.handleHandMove); @@ -462,11 +375,6 @@ export class GestureOverlay extends Touchable { if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) { this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1); } - } else if (this._thumbDoc) { - if (Math.abs(pt.clientX - this._thumbX) > 15 * window.devicePixelRatio) { - this._thumbDoc.selectedIndex = Math.max(-1, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX)); - this._thumbX = pt.clientX; - } } } } @@ -485,7 +393,6 @@ export class GestureOverlay extends Touchable { if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier); this._palette = undefined; this.thumbIdentifier = undefined; - this._thumbDoc = undefined; // this chunk of code is for handling the ink to text toolglass let scriptWorked = false; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1a9d15c1a..dbe8fb608 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -30,6 +30,7 @@ import { ColorScheme, SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; @@ -571,7 +572,7 @@ export class MainView extends React.Component { @action createNewFolder = async () => { - const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true }); + const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }); Doc.AddDocToList(Doc.MyFilesystem, 'data', folder); }; @@ -582,6 +583,8 @@ export class MainView extends React.Component { waitForDoubleClick = () => (this._exploreMode ? 'never' : undefined); headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1); mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1); + addHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true); + removeHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true); @computed get headerBarDocView() { return (

@@ -589,18 +592,19 @@ export class MainView extends React.Component { key="headerBarDoc" Document={this.headerBarDoc} DataDoc={undefined} - addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={DefaultStyleProvider} rootSelected={returnTrue} - removeDocument={returnFalse} + addDocument={this.addHeaderDoc} + removeDocument={this.removeHeaderDoc} fitContentsToBox={returnTrue} isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events) isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive ScreenToLocalTransform={this.headerBarScreenXf} childHideResizeHandles={returnTrue} + childDragAction="move" dontRegisterView={true} hideResizeHandles={true} PanelWidth={this.headerBarDocWidth} @@ -865,7 +869,7 @@ export class MainView extends React.Component { this._leftMenuFlyoutWidth = 0; }); - remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true); + remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, '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) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true); diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss deleted file mode 100644 index 0ec879288..000000000 --- a/src/client/views/Palette.scss +++ /dev/null @@ -1,30 +0,0 @@ -.palette-container { - .palette-thumb { - touch-action: pan-x; - position: absolute; - height: 70px; - overflow: hidden; - - .palette-thumbContent { - transition: transform .3s; - width: max-content; - overflow: hidden; - - .collectionView { - overflow: visible; - - .collectionLinearView-outer { - overflow: visible; - } - } - } - - .palette-cover { - width: 50px; - height: 50px; - position: absolute; - bottom: 0; - border: 1px solid black; - } - } -} \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx deleted file mode 100644 index 749eb08a2..000000000 --- a/src/client/views/Palette.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { IReactionDisposer, observable, reaction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc } from '../../fields/Doc'; -import { NumCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from '../../Utils'; -import { Transform } from '../util/Transform'; -import { DocumentView } from './nodes/DocumentView'; -import './Palette.scss'; - -export interface PaletteProps { - x: number; - y: number; - thumb: number[]; - thumbDoc: Doc; -} - -@observer -export default class Palette extends React.Component { - private _selectedDisposer?: IReactionDisposer; - @observable private _selectedIndex: number = 0; - - componentDidMount = () => { - this._selectedDisposer = reaction( - () => NumCast(this.props.thumbDoc.selectedIndex), - i => (this._selectedIndex = i), - { fireImmediately: true } - ); - }; - - componentWillUnmount = () => { - this._selectedDisposer?.(); - }; - - render() { - return ( -
-
-
- window.screen.width} - PanelHeight={() => window.screen.height} - renderDepth={0} - isDocumentActive={returnTrue} - isContentActive={emptyFunction} - focus={emptyFunction} - docViewPath={returnEmptyDoclist} - styleProvider={returnEmptyString} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - /> -
-
-
-
- ); - } -} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 1f2e21dd5..09aac053a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -287,7 +287,6 @@ export class PropertiesView extends React.Component { pinToPres={emptyFunction} bringToFront={returnFalse} dontRegisterView={true} - dropAction={undefined} />
); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 2cca2c5a9..330ccc583 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -265,6 +265,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { TraceMobx(); const firstDoc = this.props.docViews[0].props.Document; const templateName = StrCast(firstDoc.layout_fieldKey, 'layout').replace('layout_', ''); - const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data); - const addedTypes = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data); + const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data); const templateMenu: Array = []; this.props.templates?.forEach((checked, template) => templateMenu.push()); templateMenu.push(); @@ -122,7 +122,6 @@ export class TemplateMenu extends React.Component { rootSelected={returnFalse} onCheckedClick={this.scriptField} onChildClick={this.scriptField} - dropAction={undefined} isAnyChildContentActive={returnFalse} isContentActive={returnTrue} bringToFront={emptyFunction} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 0eb61a0b2..ea02bcd4c 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Opt } from '../../../fields/Doc'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { returnFalse, returnZero, StopEvent } from '../../../Utils'; +import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; @@ -60,6 +60,8 @@ export class CollectionCarouselView extends CollectionSubView() { NativeHeight={returnZero} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} + isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive} + isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction} hideCaptions={show_captions ? true : false} renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 675f23970..9a3ab8074 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -10,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -250,7 +250,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} rootSelected={this.rootSelected} layout_showTitle={this.props.childlayout_showTitle} - dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} + dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={noteTakingDocTransform} @@ -619,7 +619,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } @computed get backgroundEvents() { - return SnappingManager.GetIsDragging(); + return this.props.isContentActive() === false ? 'none' : undefined; } observer: any; diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 57d9bbb49..15570b195 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -52,18 +52,21 @@ export class CollectionPileView extends CollectionSubView() { @computed get toggleIcon() { return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' }); } + @computed get contentEvents() { + const isStarburst = this.layoutEngine() === computeStarburstLayout.name; + return this.props.isContentActive() && isStarburst ? undefined : 'none'; + } // returns the contents of the pileup in a CollectionFreeFormView @computed get contents() { - const isStarburst = this.layoutEngine() === computeStarburstLayout.name; return ( -
+
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index a55b70e22..a19d8e696 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -104,7 +104,7 @@ .collectionStackedTimeline-left-resizer, .collectionStackedTimeline-resizer { - background: $medium-gray; + background: $dark-gray; position: absolute; top: 0; height: 100%; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 9d5cb257a..e835fa659 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -119,8 +119,7 @@ export class CollectionStackedTimeline extends CollectionSubView scriptContext.clickAnchor(this, clientX))`, { - // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick) + ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { self: Doc.name, scriptContext: 'any', clientX: 'number', @@ -406,7 +405,7 @@ export class CollectionStackedTimeline extends CollectionSubView Math.max(m, o.level), 0) + 2; return this.clipDuration === 0 ? null : ( -
+
(this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); + isContentActive = () => (this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); @observable _renderCount = 5; isChildContentActive = () => @@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView { runInAction(() => (this._cursor = 'grabbing')); + const batch = UndoManager.StartBatch('stacking width'); setupMoveUpEvents( this, e, this.onDividerMove, - action(() => (this._cursor = 'grab')), + action(() => { + this._cursor = 'grab'; + batch.end(); + }), emptyFunction ); }; @@ -700,7 +704,7 @@ export class CollectionStackingView extends CollectionSubView @@ -727,7 +731,7 @@ export class CollectionStackingView extends CollectionSubView (this._scroll = e.currentTarget.scrollTop))} onDrop={this.onExternalDrop.bind(this)} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d1b7f6ff6..539dde7e0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -187,7 +187,7 @@ export function CollectionSubView(moreProps?: X) { protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) { if (de.complete.docDragData) { - // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction. + // if dropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction. // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys) if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) { de.complete.docDragData.dropAction = targetAction; @@ -212,16 +212,16 @@ export function CollectionSubView(moreProps?: X) { const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); if (movedDocs.length) { - const canAdd = this.props.Document._type_collection === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document); - added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse); + const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc); + added = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse); } else { - ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); + ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData }); added = addedDocs.length ? this.addDocument(addedDocs) : true; } added && e.stopPropagation(); return added; } else { - ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); + ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData }); added = this.addDocument(docDragData.droppedDocuments); } !added && alert('You cannot perform this move'); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 60e6815e5..192d48c64 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -34,8 +34,6 @@ export class CollectionTimeView extends CollectionSubView() { async componentDidMount() { this.props.setContentView?.(this); - //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), ""); - ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' }); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ed1e0c067..7598bb753 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,7 +3,6 @@ import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData, Height, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; -import { InkTool } from '../../../fields/InkField'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; @@ -90,8 +89,7 @@ export class CollectionTreeView extends CollectionSubView this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); - isContentActive = (outsideReaction?: boolean) => - Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false; + isContentActive = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this.props.isContentActive() ? true : false); componentWillUnmount() { this._isDisposing = true; @@ -261,7 +259,7 @@ export class CollectionTreeView extends CollectionSubView this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20)))); @@ -276,7 +274,7 @@ export class CollectionTreeView extends CollectionSubView this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); - const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); + const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; return (
@@ -440,12 +438,12 @@ export class CollectionTreeView extends CollectionSubView boolean; childHideResizeHandles?: () => boolean; childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection - childCanEmbedOnDrag?: boolean; + childDragAction?: dropActionType; childXPadding?: number; childYPadding?: number; childLayoutString?: string; @@ -220,7 +221,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles); childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); - isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive(); + isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive()); render() { TraceMobx(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 3b99339af..97d4d989b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -583,7 +583,6 @@ export class TabMinimapView extends React.Component { isContentActive={emptyFunction} isAnyChildContentActive={returnFalse} select={emptyFunction} - dropAction={undefined} isSelected={returnFalse} dontRegisterView={true} fieldKey={Doc.LayoutFieldKey(this.props.document)} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 6bc30c451..91ae2b3cc 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -21,7 +21,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from '../EditableView'; import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss'; import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView'; @@ -45,7 +45,7 @@ export interface TreeViewProps { dataDoc?: Doc; treeViewParent: Doc; renderDepth: number; - dropAction: dropActionType; + dragAction: dropActionType; addDocTab: (doc: Doc, where: OpenWhere) => boolean; panelWidth: () => number; panelHeight: () => number; @@ -276,7 +276,7 @@ export class TreeView extends React.Component { }; onPointerEnter = (e: React.PointerEvent): void => { this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc); - if (e.buttons === 1 && SnappingManager.GetIsDragging()) { + if (e.buttons === 1 && SnappingManager.GetIsDragging() && this.props.isContentActive()) { this._header.current!.className = 'treeView-header'; document.removeEventListener('pointermove', this.onDragMove, true); document.removeEventListener('pointerup', this.onDragUp, true); @@ -341,7 +341,7 @@ export class TreeView extends React.Component { }; makeFolder = () => { - const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true }); + const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }); TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView }; return this.props.addDocument(folder); }; @@ -391,7 +391,7 @@ export class TreeView extends React.Component { const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd; if (canAdd) { this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true); - const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false)); + const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false); this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false); return res; } @@ -460,7 +460,7 @@ export class TreeView extends React.Component { addDoc, remDoc, moveDoc, - this.props.dropAction, + this.props.dragAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, @@ -585,12 +585,12 @@ export class TreeView extends React.Component { downY = e.clientY; e.stopPropagation(); }} - onClick={e => { + onClick={undoable(e => { if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]); e.stopPropagation(); } - }}> + }, 'sort order')}> {!docs ? null : TreeView.GetChildElements( @@ -604,7 +604,7 @@ export class TreeView extends React.Component { addDoc, remDoc, moveDoc, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, + StrCast(this.doc.childDragAction, this.props.dragAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, @@ -904,7 +904,6 @@ export class TreeView extends React.Component { hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} styleProvider={this.titleStyleProvider} - enableDragWhenActive={true} onClickScriptDisable="never" // tree docViews have a script to show fields, etc. docViewPath={this.props.treeView.props.docViewPath} treeViewDoc={this.props.treeView.props.Document} @@ -914,7 +913,7 @@ export class TreeView extends React.Component { pinToPres={emptyFunction} onClick={this.onChildClick} onDoubleClick={this.onChildDoubleClick} - dropAction={this.props.dropAction} + dragAction={this.props.dragAction} moveDocument={this.move} removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} @@ -950,7 +949,7 @@ export class TreeView extends React.Component { fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined, textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined, outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined, - pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined, + pointerEvents: !this.props.isContentActive() ? 'none' : undefined, }}> {view}
@@ -1132,7 +1131,7 @@ export class TreeView extends React.Component { add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean, remove: undefined | ((doc: Doc | Doc[]) => boolean), move: DragManager.MoveFunction, - dropAction: dropActionType, + dragAction: dropActionType, addDocTab: (doc: Doc, where: OpenWhere) => boolean, styleProvider: undefined | StyleProviderFunc, screenToLocalXf: () => Transform, @@ -1218,7 +1217,7 @@ export class TreeView extends React.Component { panelHeight={rowHeight} dontRegisterView={dontRegisterView} moveDocument={move} - dropAction={dropAction} + dragAction={dragAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} isContentActive={isContentActive} @@ -1239,6 +1238,6 @@ export class TreeView extends React.Component { ScriptingGlobals.add(function TreeView_addNewFolder() { TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined }; - const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true }; + const opts = { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true }; return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id)); }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 36472dd2e..d7e073c5f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1240,7 +1240,7 @@ export class CollectionFreeFormView extends CollectionSubView this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => this.props.isContentActive(); @undoBatch @action @@ -1267,7 +1267,7 @@ export class CollectionFreeFormView extends CollectionSubView e.preventDefault()} onContextMenu={this.onContextMenu} style={{ - pointerEvents: SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement()) ? 'all' : (this.props.pointerEvents?.() as any), + pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any), textAlign: this.isAnnotationOverlay ? 'initial' : undefined, transform: `scale(${this.nativeDimScaling || 1})`, width: `${100 / (this.nativeDimScaling || 1)}%`, diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 77cb7f88c..898452ccb 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -386,7 +386,6 @@ export class MarqueeView extends React.Component diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 65578f214..8f90e4444 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Opt } from '../../../../fields/Doc'; @@ -50,34 +50,10 @@ export class CollectionLinearView extends CollectionSubView() { width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); - - this._selectedDisposer = reaction( - () => NumCast(this.layoutDoc.selectedIndex), - i => - runInAction(() => { - this._selectedIndex = i; - let selected: any = undefined; - this.childLayoutPairs.map(async (pair, ind) => { - const isSelected = this._selectedIndex === ind; - if (isSelected) { - selected = pair; - } else { - ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log); - } - }); - if (selected && selected.layout) { - ScriptCast(DocCast(selected.layout.proto)?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log); - } - }), - { fireImmediately: true } - ); } protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - //used for stacking and masonry view - this._dropDisposer && this._dropDisposer(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); - } + this._dropDisposer?.(); + if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); }; dimension = () => NumCast(this.rootDoc._height); @@ -89,12 +65,8 @@ export class CollectionLinearView extends CollectionSubView() { @action exitLongLinks = () => { - if (DocumentLinksButton.StartLink) { - if (DocumentLinksButton.StartLink.Document) { - action((e: React.PointerEvent) => { - Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc); - }); - } + if (DocumentLinksButton.StartLink?.Document) { + action((e: React.PointerEvent) => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc)); } DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; @@ -201,7 +173,7 @@ export class CollectionLinearView extends CollectionSubView() { moveDocument={this.props.moveDocument} addDocTab={this.props.addDocTab} pinToPres={emptyFunction} - dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} + dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType} rootSelected={this.props.isSelected} removeDocument={this.props.removeDocument} ScreenToLocalTransform={docXf} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 34fa0343d..32b42223b 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -5,7 +5,6 @@ import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -194,11 +193,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { let dropInd = -1; if (de.complete.docDragData && this._mainCont) { let curInd = -1; - de.complete.docDragData?.droppedDocuments.forEach( - action((d: Doc) => { - curInd = this.childDocs.indexOf(d); - }) - ); + de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.x < de.x && brect.x + brect.width > de.x) { @@ -234,7 +229,14 @@ export class CollectionMulticolumnView extends CollectionSubView() { onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); - isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); + isChildContentActive = () => { + const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive; + return this.props.isContentActive?.() === false || childDocsActive === false + ? false // + : this.props.isDocumentActive?.() && childDocsActive + ? true + : undefined; + }; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return ( { - curInd = this.childDocs.indexOf(d); - }) - ); + de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d))); Array.from(this._mainCont.children).forEach((child, index) => { const brect = child.getBoundingClientRect(); if (brect.y < de.y && brect.y + brect.height > de.y) { @@ -234,7 +229,14 @@ export class CollectionMultirowView extends CollectionSubView() { onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); - isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); + isChildContentActive = () => { + const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive; + return this.props.isContentActive?.() === false || childDocsActive === false + ? false // + : this.props.isDocumentActive?.() && childDocsActive + ? true + : undefined; + }; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return (
); diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 4f3503751..e8e606030 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -53,13 +53,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { }; onPointerEnter = (e: any) => { - if (!SnappingManager.GetIsDragging()) return; - document.removeEventListener('pointermove', this.onPointerMove); - document.addEventListener('pointermove', this.onPointerMove); + if (SnappingManager.GetIsDragging() && this.props.isContentActive()) { + document.removeEventListener('pointermove', this.onPointerMove); + document.addEventListener('pointermove', this.onPointerMove); + } }; onPointerMove = (e: any) => { - if (!SnappingManager.GetIsDragging()) return; const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); // this.schemaView?._selectedDocs.has(doc) ?? false; if (this._ref && dragIsRow) { diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 97264508c..a958f53ea 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -76,7 +76,7 @@ export class SchemaTableCell extends React.Component { isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'embed', + dragAction: 'move', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 5af05e491..9ceac3b8c 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -78,7 +78,7 @@ export class LinkMenuItem extends React.Component { e, e => { const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed'); - dragData.removeDropProperties = ['hidden']; + dragData.dropPropertiesToRemove = ['hidden']; DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); return true; }, diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 664a0fa4f..745efea08 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -70,7 +70,6 @@ export class LinkPopup extends React.Component { linkSearch={true} linkCreated={this.props.linkCreated} fieldKey="data" - dropAction="move" isSelected={returnTrue} isContentActive={returnTrue} select={returnTrue} diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss index 660045a6f..a12f1c12b 100644 --- a/src/client/views/nodes/ComparisonBox.scss +++ b/src/client/views/nodes/ComparisonBox.scss @@ -93,4 +93,4 @@ display: flex; } } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index e6a4635c0..d747c4527 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -2,11 +2,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils'; -import { Docs } from '../../documents/Documents'; +import { Docs, DocUtils } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; -import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; @@ -32,15 +31,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { this._disposers[disposerId]?.(); if (ele) { - // create disposers identified by disposerId to remove drag & drop listeners this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc); } }; @@ -50,29 +46,40 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey)); + res && (droppedDocs.lastElement().embedContainer = this.dataDoc); + return res; } }; private registerSliding = (e: React.PointerEvent, targetWidth: number) => { - e.button !== 2 && + if (e.button !== 2) { setupMoveUpEvents( this, e, this.onPointerMove, emptyFunction, + action((e, doubleTap) => { + if (doubleTap) { + this._isAnyChildContentActive = true; + if (!this.dataDoc[this.fieldKey + '_1']) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); + if (!this.dataDoc[this.fieldKey + '_2']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc); + } + }), + false, + undefined, action(() => { - // on click, animate slider movement to the targetWidth + if (this._isAnyChildContentActive) return; this._animating = 'all 200ms'; + // on click, animate slider movement to the targetWidth this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth(); setTimeout( action(() => (this._animating = '')), 200 ); - }), - false + }) ); + } }; @action @@ -86,7 +93,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { const anchor = Docs.Create.ConfigDocument({ - title: 'ImgAnchor:' + this.rootDoc.title, + title: 'CompareAnchor:' + this.rootDoc.title, // set presentation timing properties for restoring view presTransition: 1000, annotationOn: this.rootDoc, @@ -105,12 +112,29 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent boolean, which: string) => this.remDoc(doc, which) && addDocument(doc); + addDoc = (doc: Doc, which: string) => { + this.dataDoc[which] = doc; + return true; + }; + remDoc = (doc: Doc, which: string) => { + if (this.dataDoc[which] === doc) { + this.dataDoc[which] = undefined; + return true; + } + return false; + }; + + whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive)); docStyleProvider = (doc: Opt, props: Opt, property: string): any => { if (property === StyleProp.PointerEvents) return 'none'; return this.props.styleProvider?.(doc, props, property); }; - + moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true); + moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true); + remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true); + remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true); render() { const clearButton = (which: string) => { return ( @@ -118,33 +142,35 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent e.stopPropagation()} // prevent triggering slider movement in registerSliding onClick={e => this.clearDoc(e, which)}> - +
); }; const displayDoc = (which: string) => { - const whichDoc = Cast(this.dataDoc[which], Doc, null); - // if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); - const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc; - return whichDoc ? ( + const whichDoc = DocCast(this.dataDoc[which]); + const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc); + return targetDoc ? ( <> {clearButton(which)} // placeholder image if doc is missing ) : (
- +
); }; @@ -157,7 +183,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent +
{displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
{displayBox(`${this.fieldKey}_1`, 0, 0)} @@ -169,7 +195,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined, }} - onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */ + onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */ >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 23148ed34..5470a72f5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -14,7 +14,7 @@ import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; -import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, return18, returnEmptyString, returnFalse, returnTrue, returnVal, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -47,6 +47,7 @@ import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; import { FieldViewProps } from './FieldView'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import { KeyValueBox } from './KeyValueBox'; import { LinkAnchorBox } from './LinkAnchorBox'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; @@ -163,6 +164,7 @@ export interface DocumentViewSharedProps { docViewPath: () => DocumentView[]; childHideDecorationTitle?: () => boolean; childHideResizeHandles?: () => boolean; + childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar. dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt styleProvider: Opt; setTitleFocus?: () => void; @@ -182,7 +184,7 @@ export interface DocumentViewSharedProps { pinToPres: (document: Doc, pinProps: PinProps) => void; ScreenToLocalTransform: () => Transform; bringToFront: (doc: Doc, sendToBack?: boolean) => void; - canEmbedOnDrag?: boolean; + dragAction?: dropActionType; treeViewDoc?: Doc; xPadding?: number; yPadding?: number; @@ -194,7 +196,6 @@ export interface DocumentViewSharedProps { forceAutoHeight?: boolean; disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over. onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected - enableDragWhenActive?: boolean; waitForDoubleClickToClick?: () => 'never' | 'always' | undefined; defaultDoubleClick?: () => 'default' | 'ignore' | undefined; pointerEvents?: () => Opt; @@ -314,7 +315,7 @@ export class DocumentViewInternal extends DocComponent { + if (de.complete.docDragData && this.isContentActive()) { + targetAction && (de.complete.docDragData.dropAction = targetAction); + e.stopPropagation(); + } + }; setupHandlers() { this.cleanupHandlers(false); if (this._mainCont.current) { - this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document); + this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc); this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); } @@ -387,7 +393,7 @@ export class DocumentViewInternal extends DocComponent (ffview.ChildDrag = this.props.DocumentView())); DragManager.StartDocumentDrag( @@ -478,7 +484,7 @@ export class DocumentViewInternal extends DocComponent (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title)); + clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title); } else { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { @@ -506,7 +512,7 @@ export class DocumentViewInternal extends DocComponent { this._longPressSelector = setTimeout(() => { if (DocumentView.LongPress) { - if (this.rootDoc.dontUndo) { + if (this.rootDoc.undoIgnoreFields) { runInAction(() => (UndoStack.HideInline = !UndoStack.HideInline)); } else { this.props.select(false); @@ -535,7 +541,7 @@ export class DocumentViewInternal extends DocComponent { - if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return; + if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false; if (this.props.Document === Doc.ActiveDashboard) { + e.stopPropagation(); alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.'); - return; + e.preventDefault(); + return true; } const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData; if (linkdrag) { @@ -625,8 +633,10 @@ export class DocumentViewInternal extends DocComponent (this.layoutDoc._height = height); setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); isContentActive = (outsideReaction?: boolean): boolean | undefined => { - return this.props.isContentActive() === false + // true - if the document has been activated directly or indirectly (by having its children selected) + // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive + // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking) + return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' || (this.rootDoc.pointerEvents === 'none' && !StrCast(this.props.LayoutTemplateString).includes(KeyValueBox.name)) ? false : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || @@ -871,17 +884,18 @@ export class DocumentViewInternal extends DocComponent Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc)); const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc)); return !this.props.LayoutTemplateString && - !this.props.isSelected() && + !this.isContentActive() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb && !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) && - ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking) && - !this._componentView?.isAnyChildContentActive?.() + ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking) ? true : false; }; childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)]; - contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents); + + /// disable pointer events on content when there's an enabled onClick script, or if contents are marked inactive + contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler) || this.isContentActive() === false ? 'none' : this.pointerEvents); @computed get contents() { TraceMobx(); const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString; @@ -889,7 +903,7 @@ export class DocumentViewInternal extends DocComponent {!this._retryThumb || !this.thumbShown() ? null : ( diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 85dd779fc..4ebf22ddf 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -45,7 +45,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { @observer export class FieldView extends React.Component { public static LayoutString(fieldType: { name: string }, fieldStr: string) { - return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "" + return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "" } @computed diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9acbee1e7..64d6814df 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -387,7 +387,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} style={{ - display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none', + display: (this.props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none', width: 'min(10%, 25px)', height: 'min(10%, 25px)', background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', @@ -505,7 +505,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { document.addEventListener('pointerup', this.onDividerUp); }; - getFieldView = async () => { + getFieldView = () => { const rows = this.rows.filter(row => row.isChecked); if (rows.length > 1) { - const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true }); + const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document).title}`, _chromeHidden: true }); for (const row of rows) { - const field = this.createFieldView(DocCast(this.props.Document.data), row); + const field = this.createFieldView(DocCast(this.props.Document), row); field && Doc.AddDocToList(parent, 'data', field); row.uncheck(); } return parent; } - return rows.length ? this.createFieldView(DocCast(this.props.Document.data), rows.lastElement()) : undefined; + return rows.length ? this.createFieldView(DocCast(this.props.Document), rows.lastElement()) : undefined; }; createFieldView = (templateDoc: Doc, row: KeyValuePair) => { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index d0db60b37..01acdccb7 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -67,7 +67,6 @@ export class KeyValuePair extends React.Component { isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'embed', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index e86b881a8..9bcd04cf5 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -47,7 +47,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { if (separation > 100) { const dragData = new DragManager.DocumentDragData([this.rootDoc]); dragData.dropAction = 'embed'; - dragData.removeDropProperties = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; + dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); return true; } else { diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 93e54ffb7..de0b57fd7 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -512,9 +512,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { - return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'; - }; + pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none'); + @computed get annotationLayer() { return (
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index 72f37b62c..a9154c5bb 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -510,7 +510,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { - return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'; + return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'; }; @computed get annotationLayer() { return ( diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6d040ca1a..fd4c6366b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -559,7 +559,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent ); } - isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected() || (this.props.renderDepth === 0 && LightboxView.IsLightboxDocView(this.props.docViewPath())); @computed get renderPdfView() { TraceMobx(); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; @@ -592,7 +591,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); - annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none'); + pointerEvents = () => + !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown() + ? 'all' // + : 'none'; + annotationPointerEvents = () => (this.props.isContentActive() && (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none'); render() { const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); @@ -1013,7 +1015,11 @@ export class WebBox extends ViewBoxAnnotatableComponent + style={{ + pointerEvents: this.pointerEvents(), // + position: SnappingManager.GetIsDragging() ? 'absolute' : undefined, + display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined, + }}>
{ - if (this._editorView) { + if (this._editorView && (this._editorView as any).docView) { const state = this._editorView.state.apply(tx); this._editorView.updateState(state); @@ -2020,17 +2020,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent= 10 ? '-selected' : ''; + const selPad = (active && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0; + const selPaddingClass = active && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : ''; const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., return styleFromLayoutString?.height === '0px' ? null : (
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4f7d2e1c1..ef1c3911c 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1029,9 +1029,6 @@ export class PresBox extends ViewBoxBaseComponent() { removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 40; - isContentActive = (outsideReaction?: boolean) => this.props.isContentActive(outsideReaction); - //.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false; - /** * For sorting the array so that the order is maintained when it is dropped. */ @@ -2462,6 +2459,7 @@ export class PresBox extends ViewBoxBaseComponent() { childIgnoreNativeSize={true} moveDocument={returnFalse} ignoreUnrendered={true} + childDragAction="move" //childLayoutFitWidth={returnTrue} childOpacity={returnOne} childClickScript={PresBox.navigateToDocScript} diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 34e069046..f31cf6147 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -192,13 +192,14 @@ export class PresElementBox extends ViewBoxBaseComponent() { const dragArray = this.presBoxView?._dragArray ?? []; const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []); if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc); - dragData.dropAction = 'move'; dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc; dragData.moveDocument = this.props.moveDocument; const dragItem: HTMLElement[] = []; + const classesToRestore = new Map(); if (dragArray.length === 1) { const doc = this._itemRef.current || dragArray[0]; if (doc) { + classesToRestore.set(doc, doc.className); doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide'; dragItem.push(doc); } @@ -212,16 +213,19 @@ export class PresElementBox extends ViewBoxBaseComponent() { dragItem.push(doc); } - // const dropEvent = () => runInAction(() => this._dragging = false); if (activeItem) { + runInAction(() => (this._dragging = true)); DragManager.StartDocumentDrag( dragItem.map(ele => ele), dragData, e.clientX, e.clientY, - undefined + undefined, + action(() => { + Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1])); + this._dragging = false; + }) ); - // runInAction(() => this._dragging = true); return true; } return false; @@ -536,7 +540,6 @@ export class PresElementBox extends ViewBoxBaseComponent() {
{ e.stopPropagation(); if (this._itemRef.current && this._dragRef.current) { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index dd202418b..8f6b8cd41 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -483,7 +483,10 @@ export class PDFViewer extends React.Component { } }; - pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); + pointerEvents = () => + this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() + ? 'all' // + : 'none'; @computed get annotationLayer() { return (
@@ -514,10 +517,10 @@ export class PDFViewer extends React.Component { panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; + opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { - if (this.inlineTextAnnotations.includes(doc)) return 'none'; + if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none'; return 'all'; } return this.props.styleProvider?.(doc, props, property); @@ -536,8 +539,8 @@ export class PDFViewer extends React.Component { NativeWidth={returnZero} NativeHeight={returnZero} setContentView={emptyFunction} // override setContentView to do nothing - pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it. - childPointerEvents="all" // but freeform children need to get events to allow text editing, etc + pointerEvents={SnappingManager.GetIsDragging() && this.props.isContentActive() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it. + childPointerEvents={this.props.isContentActive() !== false ? 'all' : 'none'} // but freeform children need to get events to allow text editing, etc renderDepth={this.props.renderDepth + 1} isAnnotationOverlay={true} fieldKey={this.props.fieldKey + '_annotations'} @@ -549,7 +552,6 @@ export class PDFViewer extends React.Component { ScreenToLocalTransform={this.overlayTransform} isAnyChildContentActive={returnFalse} isAnnotationOverlayScrollable={true} - dropAction="embed" childFilters={childFilters} select={emptyFunction} bringToFront={emptyFunction} @@ -558,14 +560,14 @@ export class PDFViewer extends React.Component {
); @computed get overlayTransparentAnnotations() { - return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined); + return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined); } @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined); } @computed get overlayLayer() { return ( -
+
{this.overlayTransparentAnnotations} {this.overlayOpaqueAnnotations}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c674a20d2..c9f7e4114 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -776,7 +776,7 @@ export namespace Doc { linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks)); } }); - Doc.SetInPlace(copy, 'title', 'CLONE: ' + doc.title, true); + Doc.SetInPlace(copy, 'title', '>:' + doc.title, true); copy.cloneOf = doc; cloneMap.set(doc[Id], copy); diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 757d507be..76b287be7 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -95,12 +95,12 @@ export const documentSchema = createSchema({ link_displayArrow: 'boolean', // toggles directed arrows // drag drop properties - _stayInCollection: 'boolean', // whether document can be dropped into a different collection + _dragOnlyWithinContainer: 'boolean', // whether document can be dropped into a different collection dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. - dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move") - targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move' - childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy") - removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped + dropAction: 'string', // override specifying what should happen when something is dropped on this document (can be "embed", "copy", "move") + dragAction: 'string', // override specifying what should happen when this document s dragged (can be "embed", "copy", "move") + childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy") + dropPropertiesToRemove: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped }); export const collectionSchema = createSchema({ diff --git a/src/fields/util.ts b/src/fields/util.ts index f365adf4b..e3d1abc53 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -7,7 +7,7 @@ import { SerializationHelper } from '../client/util/SerializationHelper'; import { UndoManager } from '../client/util/UndoManager'; import { returnZero } from '../Utils'; import CursorField from './CursorField'; -import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc'; +import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, StrListCast, updateCachedAcls } from './Doc'; import { AclAdmin, AclEdit, AclPrivate, AclSelfEdit, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols'; import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols'; import { List } from './List'; @@ -86,7 +86,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } !receiver[Initializing] && - !receiver.dontUndo && + !StrListCast(receiver.undoIgnoreFields).includes(prop.toString()) && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent( { -- cgit v1.2.3-70-g09d2 From e0757ecfe8666a13a38b1c396d4a9e5138b4a3bd Mon Sep 17 00:00:00 2001 From: eperelm2 Date: Thu, 22 Jun 2023 11:26:24 -0400 Subject: double clicking works --- src/client/views/PropertiesView.tsx | 196 ++++++++++++++++++++++++++++++++---- 1 file changed, 176 insertions(+), 20 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index a0c45bc70..60e93cdfd 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1050,11 +1050,34 @@ export class PropertiesView extends React.Component { } @computed get optionsSubMenu() { + let isDouble = false; + return (
(this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}> -
(this.openContexts = false, this.openLinks = false, this.openTransform = false, - this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))} - onClick={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openOptions = !this.openOptions + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = true; + this.openTransform = false; + this.openFields = false; + this.openSharing = false; + this.openLayout = false; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} + style={{ backgroundColor: this.openOptions ? 'black' : '' }}> Options
@@ -1070,11 +1093,32 @@ export class PropertiesView extends React.Component { } @computed get sharingSubMenu() { + let isDouble:boolean = false; + return (
-
(this.openContexts = false, this.openLinks = false, this.openTransform = false, - this.openOptions = false, this.openFields = false, this.openFilters = false, this.openLayout))} - onClick={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openSharing = !this.openSharing + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = false; + this.openSharing = true; + this.openLayout = false; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + })} + style={{ backgroundColor: this.openSharing ? 'black' : '' }}> Sharing {'&'} Permissions
@@ -1127,11 +1171,34 @@ export class PropertiesView extends React.Component { }; @computed get filtersSubMenu() { + let isDouble: boolean = false; + return (
-
(this.openContexts = false, this.openLinks = false, this.openTransform = false, - this.openOptions = false, this.openFields = false, this.openSharing = false, this.openLayout = false))} - onClick={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openFilters = !this.openFilters + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = false; + this.openSharing = false; + this.openLayout = false; + this.openFilters = true; + + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} + style={{ backgroundColor: this.openFilters ? 'black' : '' }}> Filters
@@ -1147,6 +1214,8 @@ export class PropertiesView extends React.Component { } @computed get inkSubMenu() { + let isDouble = false; + return ( <> {!this.isInk ? null : ( @@ -1162,8 +1231,27 @@ export class PropertiesView extends React.Component { )}
-
(this.openTransform = !this.openTransform))} onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openOptions = false, - this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false, this.openTransform = true))} +
{ + if (!isDouble){ + this.openTransform = !this.openTransform + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = false; + this.openTransform = true; + this.openFields = false; + this.openSharing = false; + this.openLayout = false; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + })} style={{ backgroundColor: this.openTransform ? 'black' : '' }}> Transform
@@ -1177,11 +1265,34 @@ export class PropertiesView extends React.Component { } @computed get fieldsSubMenu() { + let isDouble: boolean = false; + return (
-
(this.openContexts = false, this.openLinks = false, this.openTransform = false, - this.openOptions = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))} - onClick={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openFields = !this.openFields + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = true; + this.openSharing = false; + this.openLayout = false; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} + style={{ backgroundColor: this.openFields ? 'black' : '' }}> Fields {'&'} Tags
@@ -1199,11 +1310,33 @@ export class PropertiesView extends React.Component { } @computed get contextsSubMenu() { + let isDouble = false; return (
-
(this.openLinks = false, this.openTransform = false, this.openOptions = false, this.openFields = false, - this.openSharing = false, this.openFilters = false, this.openLayout = false ))} - onClick ={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openContexts = !this.openContexts + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = true; + this.openLinks = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = false; + this.openSharing = false; + this.openLayout = false; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} + style={{ backgroundColor: this.openContexts ? 'black' : '' }}> Other Contexts
@@ -1260,11 +1393,34 @@ export class PropertiesView extends React.Component { } @computed get layoutSubMenu() { + let isDouble: boolean = false; + return (
-
(this.openContexts = false, this.openLinks = false, this.openTransform = false, - this.openOptions = false, this.openFields = false, this.openSharing = false, this.openFilters = false ))} - onClick={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}> +
{ + if (!isDouble){ + this.openLayout = !this.openLayout + } } )} + onDoubleClick={action(() => { + isDouble = true; + + this.openContexts = false; + this.openLinks = false; + this.openOptions = false; + this.openTransform = false; + this.openFields = false; + this.openSharing = false; + this.openLayout = true; + this.openFilters = false; + + + setTimeout(() => { + isDouble = false; + }, 300) + } + + )} + style={{ backgroundColor: this.openLayout ? 'black' : '' }}> Layout
-- cgit v1.2.3-70-g09d2 From 32239432b79664f577bf0c84d4688c4e5cf7f14f Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 23 Jun 2023 11:11:07 -0400 Subject: acls in proto so that layout acls start cleared --- src/client/documents/Documents.ts | 6 ++---- src/client/util/SharingManager.tsx | 3 ++- src/client/views/DocComponent.tsx | 2 +- src/client/views/PropertiesView.tsx | 4 +++- src/fields/util.ts | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7dde70a2e..05994ebbd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -839,9 +839,7 @@ export namespace Docs { const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); // dataProps['acl-Override'] = SharingPermissions.Unset; - if (dataProps['acl-Public'] == undefined && Doc.GetProto(dataProps)['acl-Public'] == undefined){ - dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; - } + dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps.isSystem = viewProps.isSystem; dataProps.isDataDoc = true; @@ -859,7 +857,7 @@ export namespace Docs { // users placeholderDoc as proto if it exists const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); - + if (placeholderDoc) { dataDoc.proto = proto; } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 3a3d393a0..a95874fd7 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -174,7 +174,7 @@ export class SharingManager extends React.Component<{}> { // ! ensures it returns true if document has been shared successfully, false otherwise return !docs - .map(doc => (this.layoutDocAcls ? doc : doc[DocData])) + .map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))) .map(doc => { doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); @@ -339,6 +339,7 @@ export class SharingManager extends React.Component<{}> { const acl = `acl-${StrCast(group.title)}`; const isDashboard = dashboards.indexOf(doc) !== -1; distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard); + distributeAcls(acl, SharingPermissions.None, Doc.GetProto(doc), undefined, undefined, isDashboard); const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 98aa9f880..6f350e0fc 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent

() const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]) const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl const permission = HierarchyMapping.get(permissionSymbol)!.name - distributeAcls(key, permission, d) + distributeAcls(key, permission, Doc.GetProto(d)) } }) ); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 825a802cd..6bca08d23 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -425,12 +425,14 @@ export class PropertiesView extends React.Component { * @returns the sharing and permissions panel. */ @computed get sharingTable() { + console.log('doc', this.selectedDoc) + console.log('proto', Doc.GetProto(this.selectedDoc!)) // all selected docs const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData])); const target = docs[0]; - const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin + const showAdmin = GetEffectiveAcl(target) == AclAdmin const individualTableEntries = []; const usersAdded: string[] = []; // all shared users being added - organized by denormalized email diff --git a/src/fields/util.ts b/src/fields/util.ts index f31213869..1ac450b9a 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -242,7 +242,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc if (!visited) visited = [] as Doc[]; if (!target || visited.includes(target)) return; if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) { - Doc.GetProto(target)[key] = acl; + target[key] = acl; if (target !== Doc.GetProto(target)) { //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???) updateCachedAcls(target); -- cgit v1.2.3-70-g09d2 From a807da6b6fed067888f817e2273b29b8b6219e52 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 23 Jun 2023 12:05:24 -0400 Subject: fixed collection acls --- src/client/documents/Documents.ts | 6 +++--- src/client/views/PropertiesView.tsx | 2 -- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 8 ++++++-- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 05994ebbd..0ab0a2ca5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1011,7 +1011,7 @@ export namespace Docs { I.rotation = 0; I.defaultDoubleClick = 'click'; I.author_date = new DateField(); - // I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; //I['acl-Override'] = SharingPermissions.Unset; I[Initializing] = false; return I; @@ -1348,8 +1348,8 @@ export namespace DocUtils { source, target, { - // 'acl-Public': SharingPermissions.Augment, - // '_acl-Public': SharingPermissions.Augment, + 'acl-Public': SharingPermissions.Augment, + '_acl-Public': SharingPermissions.Augment, title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 6bca08d23..d5d09389f 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -425,8 +425,6 @@ export class PropertiesView extends React.Component { * @returns the sharing and permissions panel. */ @computed get sharingTable() { - console.log('doc', this.selectedDoc) - console.log('proto', Doc.GetProto(this.selectedDoc!)) // all selected docs const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData])); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 77cb7f88c..430e8ecd5 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -9,7 +9,7 @@ import { RichTextField } from '../../../../fields/RichTextField'; import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; -import { GetEffectiveAcl } from '../../../../fields/util'; +import { GetEffectiveAcl, SharingPermissions } from '../../../../fields/util'; import { intersectRect, returnFalse, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents'; @@ -390,7 +390,11 @@ export class MarqueeView extends React.Component (d.embedContainer = newCollection)); + newCollection['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + selected.forEach(d => { + d.embedContainer = newCollection; + d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + }); this.hideMarquee(); return newCollection; }); -- cgit v1.2.3-70-g09d2 From 777381bc14e825a81f7bcb8b8ab305e1ac39ce00 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Mon, 26 Jun 2023 11:18:35 -0400 Subject: PropertiesView layout acls work --- src/client/util/SharingManager.tsx | 25 ++++++++++--------- src/client/views/PropertiesView.tsx | 49 +++++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 25 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 59a88b404..0f7ce26d2 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -214,9 +214,7 @@ export class SharingManager extends React.Component<{}> { const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); - if (acl == 'acl-Public' && this.layoutDocAcls){ - acl = 'acl-Public-layout'; - } + if (acl == 'acl-Public' && this.layoutDocAcls) acl = 'acl-Public-layout'; // ! ensures it returns true if document has been shared successfully, false otherwise return !docs .map(doc => (this.layoutDocAcls ? doc : doc[DocData])) @@ -274,7 +272,8 @@ export class SharingManager extends React.Component<{}> { /** * Called from the properties sidebar to change permissions of a user. */ - shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => { + shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => { + if (layout) this.layoutDocAcls = true; if (shareWith !== 'Public' && shareWith !== 'Override') { const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith)); docs.forEach(doc => { @@ -285,11 +284,17 @@ export class SharingManager extends React.Component<{}> { const dashboards = DocListCast(Doc.MyDashboards.data); docs.forEach(doc => { const isDashboard = dashboards.indexOf(doc) !== -1; - if (this.overridePrevious) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data)); - if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard); + if (this.overridePrevious) { + this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout); + } + if (GetEffectiveAcl(doc) === AclAdmin) { + if ( shareWith == 'Public' && layout) shareWith = 'Public-layout'; + distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard); + } this.setDashboardBackground(doc, permission as SharingPermissions); }); } + this.layoutDocAcls = false; }; /** @@ -607,16 +612,12 @@ export class SharingManager extends React.Component<{}> { // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-'; let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]); if (this.layoutDocAcls){ - if (groupKey == 'acl-Public'){ - groupKey = 'acl-Public-layout' - } + if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout'; if (targetDoc[DocAcl][groupKey]){ permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name; } else{ - if (groupKey == 'acl-Public-layout'){ - groupKey = 'acl-Public' - } + if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public'; if (targetDoc['embedContainer']){ permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]); } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index d5d09389f..0a8eabdb1 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -8,7 +8,7 @@ import { Lambda, action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils'; -import { Doc, Field, FieldResult, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; +import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; @@ -303,7 +303,7 @@ export class PropertiesView extends React.Component { @undoBatch changePermissions = (e: any, user: string) => { const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc! : DocCast(doc)[DocData])); - SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); + SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls); }; /** @@ -450,8 +450,17 @@ export class PropertiesView extends React.Component { // sorts and then adds each user to the table usersAdded.sort(this.sortUsers); usersAdded.map(userEmail => { - const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]); - individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user + const userKey = `acl-${normalizeEmail(userEmail)}`; + var permission; + if (this.layoutDocAcls){ + if (target[DocAcl][userKey]){ + permission = HierarchyMapping.get(target[DocAcl][userKey])?.name; + } + else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]); + else permission = StrCast(Doc.GetProto(target)?.[userKey]); + } + else permission = StrCast(target[userKey]); + individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user }); // adds current user @@ -473,17 +482,33 @@ export class PropertiesView extends React.Component { if (group.title != 'Public' && this.selectedDoc) { const groupKey = 'acl-' + normalizeEmail(StrCast(group.title)); if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) { - const permission = StrCast(target[groupKey]); - groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false)); + var permission; + if (this.layoutDocAcls){ + if (target[DocAcl][groupKey]){ + permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name; + } + else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]); + else permission = StrCast(Doc.GetProto(target)?.[groupKey]); + } + else permission = StrCast(target[groupKey]); + groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false)); } } }); + // public permission + let publicPermission = StrCast(target['acl-Public']); + if (this.layoutDocAcls){ + if (target['acl-Public-layout']) publicPermission = StrCast(target['acl-Public-layout']); + else if (target['embedContainer']) publicPermission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))['acl-Public']); + else StrCast(Doc.GetProto(target)['acl-Public']); + } + return (

{' '} Public / Guest Users -
{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}
+
{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}
{' '}

Individual Users with Access to this Document{' '} @@ -994,12 +1019,10 @@ export class PropertiesView extends React.Component { {!this.openSharing ? null : (
- {/* {!Doc.noviceMode ? ( // what is the layout checkbox for? -
- (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> -
Layout
-
- ) : null} */} +
+ (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> +
Layout
+
{/*
{"Re-distribute sharing settings"}
}>
+ this.changeVal(val as string, 'curr')} + fillWidth + password + /> + this.changeVal(val as string, 'new')} + fillWidth + password + /> + this.changeVal(val as string, 'conf')} + fillWidth + password + /> + {!this.passwordResultText ? null :
{this.passwordResultText}
} +
); } @@ -346,14 +396,15 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement='bottom-start' - color={StrCast(Doc.SharingDoc().userColor)} + color={this.userColor} + fillWidth />
@@ -379,7 +430,7 @@ export class SettingsManager extends React.Component<{}> { dropdownType={DropdownType.SELECT} type={Type.TERT} placement='bottom-start' - color={StrCast(Doc.SharingDoc().userColor)} + color={this.userColor} />
@@ -390,12 +441,12 @@ export class SettingsManager extends React.Component<{}> { text={"Manage Groups"} type={Type.TERT} onClick={() => GroupManager.Instance?.open()} - color={StrCast(Doc.SharingDoc().userColor)} + color={this.userColor} /> (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))}/>
@@ -414,23 +465,35 @@ export class SettingsManager extends React.Component<{}> { { title: 'Appearance', ele: this.appearanceContent }, { title: 'Text', ele: this.textContent }, ]; - return (
-
+
- {tabs.map(tab => ( -
(this.activeTab = tab.title))}> - {tab.title} -
- ))} + {tabs.map(tab => { + const isActive = this.activeTab === tab.title + return ( +
(this.activeTab = tab.title)) + }> + {tab.title} +
+ ) + })}
-
{Doc.CurrentUserEmail}
+
{Doc.CurrentUserEmail}
@@ -439,12 +502,13 @@ export class SettingsManager extends React.Component<{}> {
-
+
{tabs.map(tab => (
{tab.ele} @@ -462,7 +526,7 @@ export class SettingsManager extends React.Component<{}> { isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} - dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }} + dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.UserDoc().userColor, 'string', null) }} /> ); } diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss index 8a0e5480e..b205a0f1e 100644 --- a/src/client/views/AntimodeMenu.scss +++ b/src/client/views/AntimodeMenu.scss @@ -5,11 +5,15 @@ position: absolute; z-index: 10001; height: $antimodemenu-height; - background: $dark-gray; - border-bottom: $standard-border; + width: fit-content; + border-radius: $standard-border-radius; + overflow: hidden; // box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); // border-radius: 0px 6px 6px 6px; display: flex; + justify-content: center; + align-items: center; + gap: 3px; &.with-rows { flex-direction: column @@ -20,30 +24,6 @@ height: 35px; } - .antimodeMenu-button { - background-color: transparent; - width: 35px; - height: 35px; - padding: 5; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - position: relative; - - .svg { - margin: 0; - } - - &.active { - background-color: #121212; - } - } - - .antimodeMenu-button:hover { - background-color: rgba(0, 0, 0, 0.4); - } - .antimodeMenu-dragger { height: 100%; transition: width .2s; diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index de1207ce4..c41ea7053 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -1,6 +1,8 @@ import React = require('react'); import { observable, action, runInAction } from 'mobx'; import './AntimodeMenu.scss'; +import { StrCast } from '../../fields/Types'; +import { Doc } from '../../fields/Doc'; export interface AntimodeMenuProps {} /** @@ -148,6 +150,7 @@ export abstract class AntimodeMenu extends React.Co left: this._left, top: this._top, opacity: this._opacity, + background: StrCast(Doc.UserDoc().userBackgroundColor), transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -173,6 +176,7 @@ export abstract class AntimodeMenu extends React.Co height: 'inherit', width: 200, opacity: this._opacity, + background: StrCast(Doc.UserDoc().userBackgroundColor), transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -195,6 +199,7 @@ export abstract class AntimodeMenu extends React.Co left: this._left, top: this._top, opacity: this._opacity, + background: StrCast(Doc.UserDoc().userBackgroundColor), transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss index b8a6f6c05..b95312ea0 100644 --- a/src/client/views/DashboardView.scss +++ b/src/client/views/DashboardView.scss @@ -14,6 +14,7 @@ flex-direction: column; width: 250px; min-width: 250px; + gap: 5px; } .all-dashboards { @@ -56,15 +57,26 @@ display: flex; justify-content: center; align-items: center; + position: relative; &:hover { color: $light-blue; border: solid 2px $light-blue; } + + .background { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + z-index: -1; + } } .dashboard-container { border-radius: 10px; + position: relative; cursor: pointer; width: 250px; height: 200px; @@ -99,10 +111,20 @@ .more { z-index: 100; } + + .background { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + z-index: -1; + } } .new-dashboard { color: $dark-gray; + padding: 10px; display: flex; width: 100%; height: 100%; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 3bd6ac61c..dd1079d88 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, ColorPicker, FontSize, IconButton, Size, Type } from 'browndash-components'; +import { Button, ColorPicker, EditableText, FontSize, IconButton, Size, Type } from 'browndash-components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -24,7 +24,7 @@ import { ContextMenu } from './ContextMenu'; import './DashboardView.scss'; import { Colors } from './global/globalEnums'; import { MainViewModal } from './MainViewModal'; -import { ButtonType } from './nodes/button/FontIconBox'; +import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { FaPlus } from 'react-icons/fa'; enum DashboardGroup { @@ -43,13 +43,16 @@ export class DashboardView extends React.Component { @observable private selectedDashboardGroup = DashboardGroup.MyDashboards; @observable private newDashboardName: string | undefined = undefined; - @observable private newDashboardColor: string | undefined = undefined; + @observable private newDashboardColor: string | undefined = "#AFAFAF"; @action abortCreateNewDashboard = () => { this.newDashboardName = undefined; }; @action setNewDashboardName(name: string) { this.newDashboardName = name; } + @action setNewDashboardColor(color: string) { + this.newDashboardColor = color; + } @action selectDashboardGroup = (group: DashboardGroup) => { @@ -95,23 +98,33 @@ export class DashboardView extends React.Component { const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1; const placeholder = `Dashboard ${dashboardCount}`; return ( -
+
Create New Dashboard
-
- Title - this.setNewDashboardName((e.target as any).value)} /> -
-
- Background - { - this.newDashboardColor = color; - }} - /> -
+ this.setNewDashboardName(val as string)} + fillWidth + /> + { + this.setNewDashboardColor(color); + }} + />
-
); @@ -150,32 +163,49 @@ export class DashboardView extends React.Component { }; render() { + const color = StrCast(Doc.UserDoc().userColor) + const variant = StrCast(Doc.UserDoc().userVariantColor) return ( <>
-
-
-
this.selectDashboardGroup(DashboardGroup.MyDashboards)}> - My Dashboards -
-
this.selectDashboardGroup(DashboardGroup.SharedDashboards)}> - Shared Dashboards -
+
{this.getDashboards().map(dashboard => { const href = ImageCast(dashboard.thumb)?.url.href; return ( -
this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}> +
this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
- e.stopPropagation()} defaultValue={StrCast(dashboard.title)} onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)} /> + (Doc.GetProto(dashboard).title = val)} + /> {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ?
unviewed
:
}
-
+
); })} @@ -200,6 +234,10 @@ export class DashboardView extends React.Component { this.setNewDashboardName(''); }}> + +
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 7043edcee..4deed5ed9 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -6,6 +6,7 @@ import { ObjectField } from '../../fields/ObjectField'; import './EditableView.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; import { OverlayView } from './OverlayView'; +import { EditableText } from 'browndash-components'; export interface EditableProps { /** @@ -231,39 +232,49 @@ export class EditableView extends React.Component { onChange: this.props.autosuggestProps.onChange, }} /> - ) : this.props.oneLine !== false && this.props.GetValue()?.toString().indexOf('\n') === -1 ? ( - (this._inputref = r)} - style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }} - placeholder={this.props.placeholder} - onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} - defaultValue={this.props.GetValue()} - autoFocus={true} - onChange={this.onChange} - onKeyDown={this.onKeyDown} - onKeyPress={this.stopPropagation} - onPointerDown={this.stopPropagation} - onClick={this.stopPropagation} - onPointerUp={this.stopPropagation} + ) : - ) : ( -