From b235c94614141752c98d24a395682a02297d9e6a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 11:15:59 -0400 Subject: cleaned up rootSelected() to work with templates. adjusted nudge to favor moving documents over panning collection --- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 17 +++++++++++------ .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 6b7c3de56..37cf942c6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -100,7 +100,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: } rootSelected = (outsideReaction?: boolean) => { - return this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument || this.props.Document.forceActive ? this.props.rootSelected(outsideReaction) : false); + return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction)); } // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 821840e0b..86df9c080 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -102,7 +102,7 @@ export class CollectionView extends Touchable { return viewField as any as CollectionViewType; } - active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.rootSelected(outsideReaction) && BoolCast(this.props.Document.forceActive)) || this._isChildActive || this.props.renderDepth === 0; + active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0; whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7dca19073..a77f09163 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -15,7 +15,7 @@ import { ScriptField } from "../../../../new_fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types"; import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; -import { aggregateBounds, intersectRect, returnOne, Utils, returnZero } from "../../../../Utils"; +import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs } from "../../../documents/Documents"; @@ -43,6 +43,7 @@ import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); +import { CollectionViewType } from "../CollectionView"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -860,7 +861,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u LibraryPath: this.libraryPath, FreezeDimensions: this.props.freezeChildDimensions, layoutKey: undefined, - rootSelected: this.rootSelected, + rootSelected: childData ? this.rootSelected : returnFalse, dropAction: StrCast(this.props.Document.childDropAction) as dropActionType, //onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them onClick: this.onChildClickHandler, @@ -1105,10 +1106,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u _nudgeTime = 0; nudge = action((x: number, y: number) => { - this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(), - NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "Ease", true); - this._nudgeTime = Date.now(); - setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document.panTransformType = undefined), 500); + if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) { // bcz: this isn't ideal, but want to try it out... + this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(), + NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "Ease", true); + this._nudgeTime = Date.now(); + setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document.panTransformType = undefined), 500); + return true; + } + return false; }); @computed get marqueeView() { return void; isSelected: () => boolean; isAnnotationOverlay?: boolean; - nudge:(x:number, y:number) => void; + nudge: (x: number, y: number) => boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } -- cgit v1.2.3-70-g09d2 From 87268612b9b765d74f1a5317b0894be0ceb1dfd1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 16:43:50 -0400 Subject: fixed map view pointer events. --- src/client/views/collections/CollectionMapView.scss | 3 +++ src/client/views/collections/CollectionMapView.tsx | 8 ++++---- src/client/views/collections/CollectionView.tsx | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index c74433902..df7853da6 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -1,4 +1,7 @@ .collectionMapView-contents { width: 100%; height: 100%; +} +.collectionMapView-contents-none { + pointer-events: none; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index b67daeb53..5075bbf7a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -7,6 +7,7 @@ import { NumCast, StrCast } from "../../../new_fields/Types"; import { CollectionSubView } from "./CollectionSubView"; import { Utils } from "../../../Utils"; import { Opt } from "../../../new_fields/Doc"; +import "./CollectionMapView.scss"; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); @@ -25,11 +26,10 @@ class CollectionMapView extends CollectionSubView center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0; } return ( -
+
(this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { return viewField as any as CollectionViewType; } - active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0; + active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || this.props.Document.forceActive || this._isChildActive || this.props.renderDepth === 0) ? true : false; whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); -- cgit v1.2.3-70-g09d2 From 78986ae808dc9bbb5763e3f74097b7a1bc61f49a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 18:45:52 -0400 Subject: more mapview adjustments --- src/client/documents/Documents.ts | 4 - src/client/views/PreviewCursor.tsx | 2 +- .../views/collections/CollectionMapView.scss | 3 - src/client/views/collections/CollectionMapView.tsx | 104 ++++++++++++++------- 4 files changed, 69 insertions(+), 44 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3f46c4013..e6f3b21ca 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -49,10 +49,6 @@ import { ContextMenuProps } from "../views/ContextMenuItem"; import { ContextMenu } from "../views/ContextMenu"; import { LinkBox } from "../views/nodes/LinkBox"; import { ScreenshotBox } from "../views/nodes/ScreenshotBox"; -import CollectionMapView from "../views/collections/CollectionMapView"; -import LocationField, { LocationData } from "../../new_fields/LocationField"; -import { action } from "mobx"; -const requestImageSize = require('../util/request-image-size'); const path = require('path'); export interface DocumentOptions { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 92ba13d2f..df30c1215 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -113,7 +113,7 @@ export class PreviewCursor extends React.Component<{}> { addLiveText: (doc: Doc) => void, getTransform: () => Transform, addDocument: (doc: Doc) => boolean, - nudge: (nudgeX: number, nudgeY: number) => void) { + nudge: (nudgeX: number, nudgeY: number) => boolean) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; this._addLiveTextDoc = addLiveText; diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index df7853da6..c74433902 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -1,7 +1,4 @@ .collectionMapView-contents { width: 100%; height: 100%; -} -.collectionMapView-contents-none { - pointer-events: none; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 5075bbf7a..569f8ba7a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,13 +1,20 @@ +import { GoogleApiWrapper, Map, MapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; -import { makeInterface } from "../../../new_fields/Schema"; +import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; -import React = require("react"); -import { Map, Marker, MapProps, GoogleApiWrapper } from "google-maps-react"; -import { NumCast, StrCast } from "../../../new_fields/Types"; -import { CollectionSubView } from "./CollectionSubView"; -import { Utils } from "../../../Utils"; -import { Opt } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; +import { TraceMobx } from "../../../new_fields/util"; import "./CollectionMapView.scss"; +import { CollectionSubView } from "./CollectionSubView"; +import React = require("react"); +import { DocumentManager } from "../../util/DocumentManager"; +import { UndoManager } from "../../util/UndoManager"; +import { returnTrue } from "../../../Utils"; +import { CancellationError } from "bluebird"; +import { ln } from "shelljs"; +import { dfareporting } from "googleapis/build/src/apis/dfareporting"; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); @@ -17,28 +24,65 @@ export type LocationData = google.maps.LatLngLiteral & { address?: string }; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapDocument) { + getLocation = (doc: Opt, fieldKey: string, defaultLocation?: LocationData) => { + if (doc) { + let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); + let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); + const address = Cast(doc[fieldKey + "-address"], "string", null); + if (address) { + // use geo service to convert to lat/lng + lat = lat; + lng = lng; + } + if (lat === undefined) lat = defaultLocation?.lat; + if (lng === undefined) lng = defaultLocation?.lng; + return ({ lat, lng }); + } + return ({ lat: 35.1592238, lng: -98.4466577 }); + } + renderMarker(layout: Doc, icon: Opt) { + const location = this.getLocation(layout, "location"); + return location.lat === undefined || location.lng === undefined ? (null) : + { + this.props.Document.mapCenterLat = location.lat; + this.props.Document.mapCenterLng = location.lng; + if (layout.isLinkButton && DocListCast(layout.links).length) { + const batch = UndoManager.StartBatch("follow link click"); + await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { + this.props.addDocTab(doc, where); + finished?.(); + }, false, this.props.ContainingCollectionDoc, batch.end, undefined); + } else { + ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout }); + } + }} + icon={icon} + />; + } render() { - const { childLayoutPairs, props } = this; - const { Document } = props; - const center: LocationData = { lat: NumCast(Document.mapCenterLat), lng: NumCast(Document.mapCenterLng) }; - if (!center.lat) { - center.lat = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLat, 0) : 0; - center.lng = childLayoutPairs.length ? NumCast(childLayoutPairs[0].layout.locationLng, 0) : 0; + const { childLayoutPairs } = this; + const { Document } = this.props; + let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter"); + if (center.lat === undefined) { + center = this.getLocation(childLayoutPairs.map(pair => pair.layout).find(returnTrue), "location", { lat: 35.1592238, lng: -98.4466577 }); } - return ( -
(this.props.active() && e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + TraceMobx(); + return center.lat === undefined || center.lng === undefined ? (null) : +
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > {childLayoutPairs.map(({ layout }) => { - const location: LocationData = { - lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), - lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) - }; let icon: Opt, iconUrl: Opt; if ((iconUrl = StrCast(Document.mapIconUrl, null))) { const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); @@ -48,22 +92,10 @@ class CollectionMapView extends CollectionSubView url: iconUrl }; } - return ( - { - Document.mapCenterLat = location.lat; - Document.mapCenterLng = location.lng; - }} - icon={icon} - /> - ); + return this.renderMarker(layout, icon); })} -
- ); +
; } } -- cgit v1.2.3-70-g09d2 From 479964e7c13f7b9b1102b36575ad8f4cc8dc00c2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 21:00:50 -0400 Subject: cleaned up some field key stuff for mapview --- src/client/views/collections/CollectionMapView.tsx | 87 ++++++++++++---------- 1 file changed, 47 insertions(+), 40 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 569f8ba7a..44bb22827 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -11,10 +11,6 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager } from "../../util/UndoManager"; -import { returnTrue } from "../../../Utils"; -import { CancellationError } from "bluebird"; -import { ln } from "shelljs"; -import { dfareporting } from "googleapis/build/src/apis/dfareporting"; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); @@ -24,32 +20,33 @@ export type LocationData = google.maps.LatLngLiteral & { address?: string }; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapDocument) { - getLocation = (doc: Opt, fieldKey: string, defaultLocation?: LocationData) => { + getLocation = (doc: Opt, fieldKey: string) => { if (doc) { let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); + let zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); const address = Cast(doc[fieldKey + "-address"], "string", null); if (address) { // use geo service to convert to lat/lng lat = lat; lng = lng; } - if (lat === undefined) lat = defaultLocation?.lat; - if (lng === undefined) lng = defaultLocation?.lng; - return ({ lat, lng }); + return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined; } - return ({ lat: 35.1592238, lng: -98.4466577 }); + return undefined; } renderMarker(layout: Doc, icon: Opt) { - const location = this.getLocation(layout, "location"); - return location.lat === undefined || location.lng === undefined ? (null) : + const location = this.getLocation(layout, "mapLocation"); + return !location ? (null) : { - this.props.Document.mapCenterLat = location.lat; - this.props.Document.mapCenterLng = location.lng; + this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0; + this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; + this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; + location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom); if (layout.isLinkButton && DocListCast(layout.links).length) { const batch = UndoManager.StartBatch("follow link click"); await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { @@ -67,35 +64,45 @@ class CollectionMapView extends CollectionSubView const { childLayoutPairs } = this; const { Document } = this.props; let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter"); - if (center.lat === undefined) { - center = this.getLocation(childLayoutPairs.map(pair => pair.layout).find(returnTrue), "location", { lat: 35.1592238, lng: -98.4466577 }); + if (center === undefined) { + center = childLayoutPairs.map(pair => this.getLocation(pair.layout, "mapLocation")).find(layout => layout); + if (center === undefined) { + center = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; // nowhere, OK + } } TraceMobx(); - return center.lat === undefined || center.lng === undefined ? (null) : -
e.stopPropagation()} - onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - - {childLayoutPairs.map(({ layout }) => { - let icon: Opt, iconUrl: Opt; - if ((iconUrl = StrCast(Document.mapIconUrl, null))) { - const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); - icon = { - size: iconSize, - scaledSize: iconSize, - url: iconUrl - }; - } - return this.renderMarker(layout, icon); - })} - -
; + return
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + console.log(e)} + onRecenter={e => console.log(e)} + onDragend={(centerMoved, center) => console.log(centerMoved, center)} + onProjectionChanged={e => console.log(e)} + onCenterChanged={(e => { + Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat; + Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng; + })} + > + {childLayoutPairs.map(({ layout }) => { + let icon: Opt, iconUrl: Opt; + if ((iconUrl = StrCast(Document.mapIconUrl, null))) { + const iconSize = new google.maps.Size(NumCast(layout["mapLocation-iconWidth"], 45), NumCast(layout["mapLocation-iconHeight"], 45)); + icon = { + size: iconSize, + scaledSize: iconSize, + url: iconUrl + }; + } + return this.renderMarker(layout, icon); + })} + +
; } } -- cgit v1.2.3-70-g09d2 From 14b3f6b3580b1987bdf9f8f865a79087e801d367 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 21:10:10 -0400 Subject: fromlast --- src/client/views/collections/CollectionMapView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 44bb22827..2bf3d7c32 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -82,7 +82,7 @@ class CollectionMapView extends CollectionSubView center={center} onBoundsChanged={e => console.log(e)} onRecenter={e => console.log(e)} - onDragend={(centerMoved, center) => console.log(centerMoved, center)} + onDragend={e => console.log(e)} onProjectionChanged={e => console.log(e)} onCenterChanged={(e => { Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat; -- cgit v1.2.3-70-g09d2 From 130e26103461f23a2d8b57ea2eef6b17eefb8f8e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 13 Apr 2020 21:46:20 -0400 Subject: updated childDetailedView stuff for pivot view --- src/client/views/TemplateMenu.tsx | 4 ++-- src/client/views/collections/CollectionTimeView.tsx | 10 ++++++---- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 4 ++-- src/client/views/collections/CollectionViewChromes.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 8 ++++---- src/new_fields/Doc.ts | 8 -------- src/new_fields/documentSchemas.ts | 2 +- 8 files changed, 18 insertions(+), 24 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 4d7f1e443..b76137f06 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -167,8 +167,8 @@ export class TemplateMenu extends React.Component { } } -Scripting.addGlobal(function switchView(doc: Doc, template: Doc) { - if (template.dragFactory) { +Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) { + if (template?.dragFactory) { template = Cast(template.dragFactory, Doc, null); } const templateTitle = StrCast(template?.title); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 53de2fbbe..31b720b81 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -5,7 +5,7 @@ import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; import { RichTextField } from "../../../new_fields/RichTextField"; import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; -import { NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; +import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types"; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { Scripting } from "../../util/Scripting"; import { ContextMenu } from "../ContextMenu"; @@ -19,6 +19,7 @@ const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; import React = require("react"); +import { DocumentView } from "../nodes/DocumentView"; @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { @@ -28,14 +29,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { componentWillUnmount() { this.props.Document.onChildClick = undefined; } - componentDidMount() { - const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked; - const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(thisContainer.childDetailed, alias, 'layout_detailView'); alias.layoutKey='layout_detailedView'; alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; + async componentDidMount() { + const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" }); this.props.Document._fitToBox = true; if (!this.props.Document.onViewDefClick) { this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); } + this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked; + DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), ""); } layoutEngine = () => this._layoutEngine; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 23fb259fc..b96ee4bc4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -754,7 +754,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as })); Document.childLayout = heroView; - Document.childDetailed = detailView; + Document.childDetailView = detailView; Document._viewType = CollectionViewType.Time; Document._forceActive = true; Document._pivotField = "company"; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8cc5146d5..f94277862 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -232,8 +232,8 @@ export class CollectionView extends Touchable { if (this.props.Document.childLayout instanceof Doc) { layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" }); } - if (this.props.Document.childDetailed instanceof Doc) { - layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, "onRight"), icon: "project-diagram" }); + if (this.props.Document.childDetailView instanceof Doc) { + layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailView as Doc, "onRight"), icon: "project-diagram" }); } layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index ba95dce00..e494c337a 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -48,8 +48,8 @@ export class CollectionViewBaseChrome extends React.Component click item view", - script: "this.target.childDetailed = getDocTemplate(this.source?.[0])", - immediate: (source: Doc[]) => this.target.childDetailed = Doc.getDocTemplate(source?.[0]), + script: "this.target.childDetailView = getDocTemplate(this.source?.[0])", + immediate: (source: Doc[]) => this.target.childDetailView = Doc.getDocTemplate(source?.[0]), initialize: emptyFunction, }; _contentCommand = { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cc3c39154..5b6478e66 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -541,7 +541,7 @@ export class DocumentView extends DocComponent(Docu deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument?.(this.props.Document); } // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - static makeCustomViewClicked = (doc: Doc, creator: (documents: Array, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { + static makeCustomViewClicked = (doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { const batch = UndoManager.StartBatch("makeCustomViewClicked"); runInAction(() => { doc.layoutKey = "layout_" + templateSignature; @@ -563,7 +563,7 @@ export class DocumentView extends DocComponent(Docu !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); return docLayoutTemplate; } - static createCustomView = (doc: Doc, creator: (documents: Array, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { + static createCustomView = (doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { const templateName = templateSignature.replace(/\(.*\)/, ""); docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, StrCast(doc.type), templateSignature); @@ -584,10 +584,10 @@ export class DocumentView extends DocComponent(Docu } else if (doc.data instanceof ImageField) { fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); } - const docTemplate = docLayoutTemplate || creator(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); - Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); } @undoBatch diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 92abb7a71..bcf0d1aec 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -603,14 +603,6 @@ export namespace Doc { return undefined; } export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) { - if (!templateDoc) { - target.layout = undefined; - target._nativeWidth = undefined; - target._nativeHeight = undefined; - target.type = undefined; - return; - } - if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) { if (target.resolvedDataDoc) { target[targetKey] = new PrefetchProxy(templateDoc); diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index b11941f40..bc63e9df8 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -74,7 +74,7 @@ export const positionSchema = createSchema({ export const collectionSchema = createSchema({ childLayout: Doc, // layout template for children of a collecion - childDetailed: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) + childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) onChildClick: ScriptField, // script to run for each child when its clicked onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view }); -- cgit v1.2.3-70-g09d2 From a888dacc8e6a6400d52cdded0015e05ac5581d3d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 14 Apr 2020 00:31:20 -0700 Subject: chrome dropdown changes and custom loading container for mapview --- .../views/collections/CollectionMapView.scss | 18 +++++++++++++ src/client/views/collections/CollectionMapView.tsx | 31 +++++++++++++--------- .../views/collections/CollectionViewChromes.tsx | 18 ++++++------- 3 files changed, 46 insertions(+), 21 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index c74433902..4956ad946 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -1,4 +1,22 @@ .collectionMapView-contents { width: 100%; height: 100%; +} + +.loadingWrapper { + width: 100%; + height: 100%; + background-color: yellow; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + + .loadingGif { + align-self: center; + justify-self: center; + width: 20%; + height: 20% + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 44bb22827..6d5dcdf1d 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -11,20 +11,21 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager } from "../../util/UndoManager"; +import * as ReactDOM from 'react-dom'; -type MapDocument = makeInterface<[typeof documentSchema]>; -const MapDocument = makeInterface(documentSchema); +type MapSchema = makeInterface<[typeof documentSchema]>; +const MapSchema = makeInterface(documentSchema); export type LocationData = google.maps.LatLngLiteral & { address?: string }; @observer -class CollectionMapView extends CollectionSubView & { google: any }>(MapDocument) { +class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { getLocation = (doc: Opt, fieldKey: string) => { if (doc) { let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); - let zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); + const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); const address = Cast(doc[fieldKey + "-address"], "string", null); if (address) { // use geo service to convert to lat/lng @@ -79,12 +80,12 @@ class CollectionMapView extends CollectionSubView google={this.props.google} zoom={center.zoom || 10} initialCenter={center} - center={center} - onBoundsChanged={e => console.log(e)} - onRecenter={e => console.log(e)} - onDragend={(centerMoved, center) => console.log(centerMoved, center)} - onProjectionChanged={e => console.log(e)} - onCenterChanged={(e => { + onBoundsChanged={(props, map, e) => console.log("ON_BOUNDS_CHANGED", props, map, e)} + onRecenter={(props, map, e) => console.log("ON_RECENTER", props, map, e)} + onDragend={(centerMoved, center) => console.log("ON_DRAGEND", centerMoved, center)} + onProjectionChanged={(props, map, e) => console.log("ON_PROJ_CHANGED", props, map, e)} + onCenterChanged={((props, map, e) => { + console.log("ON_CENTER_CHANGED", props, map, e); Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat; Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng; })} @@ -92,7 +93,9 @@ class CollectionMapView extends CollectionSubView {childLayoutPairs.map(({ layout }) => { let icon: Opt, iconUrl: Opt; if ((iconUrl = StrCast(Document.mapIconUrl, null))) { - const iconSize = new google.maps.Size(NumCast(layout["mapLocation-iconWidth"], 45), NumCast(layout["mapLocation-iconHeight"], 45)); + const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); + const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45); + const iconSize = new google.maps.Size(iconWidth, iconHeight); icon = { size: iconSize, scaledSize: iconSize, @@ -107,4 +110,8 @@ class CollectionMapView extends CollectionSubView } -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any; \ No newline at end of file +const LoadingContainer = () => { + return
; +}; + +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index ba95dce00..0940e9186 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -402,15 +402,15 @@ export class CollectionViewBaseChrome extends React.Component - - - - - - - - - + {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : ( + + ))}
-- cgit v1.2.3-70-g09d2 From 8a537327fb685fa9267e25e550ec0aa7a3ae86cc Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 14 Apr 2020 04:01:24 -0700 Subject: added bidirectional geocoding for map view --- src/client/views/collections/CollectionMapView.tsx | 128 +++++++++++++++------ webpack.config.js | 64 +++++------ 2 files changed, 126 insertions(+), 66 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index a2d3c328d..8cdc145b8 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -4,37 +4,44 @@ import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; +import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager } from "../../util/UndoManager"; +import { IReactionDisposer, reaction } from "mobx"; +import requestPromise = require("request-promise"); type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); -export type LocationData = google.maps.LatLngLiteral & { address?: string }; +export type LocationData = google.maps.LatLngLiteral & { + address?: string + resolvedAddress?: string; + zoom?: number; +}; + +const base = "https://maps.googleapis.com/maps/api/geocode/json?"; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { - getLocation = (doc: Opt, fieldKey: string) => { + private mapRef = React.createRef(); + private addressUpdaters: IReactionDisposer[] = []; + private latlngUpdaters: IReactionDisposer[] = []; + + getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - let lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); - let lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); + const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); + const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); - const address = Cast(doc[fieldKey + "-address"], "string", null); - if (address) { - // use geo service to convert to lat/lng - lat = lat; - lng = lng; - } return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined; } return undefined; } + renderMarker(layout: Doc, icon: Opt) { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : @@ -43,6 +50,7 @@ class CollectionMapView extends CollectionSubView & label={StrCast(layout.title)} position={{ lat: location.lat, lng: location.lng }} onClick={async () => { + this.map.panTo(location); this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0; this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; @@ -60,6 +68,73 @@ class CollectionMapView extends CollectionSubView & icon={icon} />; } + + private get contents() { + this.addressUpdaters.forEach(disposer => disposer()); + this.addressUpdaters = []; + this.latlngUpdaters.forEach(disposer => disposer()); + this.latlngUpdaters = []; + return this.childLayoutPairs.map(({ layout }) => { + let icon: Opt, iconUrl: Opt; + if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) { + const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); + const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45); + const iconSize = new google.maps.Size(iconWidth, iconHeight); + icon = { + size: iconSize, + scaledSize: iconSize, + url: iconUrl + }; + } + this.addressUpdaters.push(reaction( + () => ({ + lat: NumCast(layout["mapLocation-lat"]), + lng: NumCast(layout["mapLocation-lng"]) + }), + ({ lat, lng }) => { + if (!BoolCast(layout._ignoreNextUpdate)) { + if (lat !== undefined && lng !== undefined) { + const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`; + requestPromise.get(target).then(res => { + layout._ignoreNextUpdate = true; + layout["mapLocation-address"] = JSON.parse(res).results[0]?.formatted_address || ""; + }); + } + } else { + layout._ignoreNextUpdate = false; + } + } + )); + this.latlngUpdaters.push(reaction( + () => ({ address: Cast(layout["mapLocation-address"], "string", null) }), + ({ address }) => { + if (!BoolCast(layout._ignoreNextUpdate)) { + if (address && address.length) { + const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; + requestPromise.get(target).then(res => { + const result = JSON.parse(res).results[0]; + const { lat, lng } = result.geometry.location; + layout._ignoreNextUpdate = true; + layout["mapLocation-lat"] = lat; + layout._ignoreNextUpdate = true; + layout["mapLocation-lng"] = lng; + layout._ignoreNextUpdate = true; + layout["mapLocation-address"] = result.formatted_address; + }); + } + } else { + layout._ignoreNextUpdate = false; + } + } + )); + return this.renderMarker(layout, icon); + }); + } + + private get map() { + return (this.mapRef.current as any).map; + } + render() { const { childLayoutPairs } = this; const { Document } = this.props; @@ -76,33 +151,18 @@ class CollectionMapView extends CollectionSubView & onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > console.log("ON_BOUNDS_CHANGED", props, map, e)} - onRecenter={(props, map, e) => console.log("ON_RECENTER", props, map, e)} - onDragend={(centerMoved, center) => console.log("ON_DRAGEND", centerMoved, center)} - onProjectionChanged={(props, map, e) => console.log("ON_PROJ_CHANGED", props, map, e)} - onCenterChanged={((props, map, e) => { - console.log("ON_CENTER_CHANGED", props, map, e); - Document[this.props.fieldKey + "-mapCenter-lat"] = typeof e?.center?.lat === "number" ? e.center.lat : center!.lat; - Document[this.props.fieldKey + "-mapCenter-lng"] = typeof e?.center?.lng === "number" ? e.center.lng : center!.lng; - })} + center={center} + onDragend={() => { + const { center } = this.map; + Document[this.props.fieldKey + "-mapCenter-lat"] = center.lat(); + Document[this.props.fieldKey + "-mapCenter-lng"] = center.lng(); + }} > - {childLayoutPairs.map(({ layout }) => { - let icon: Opt, iconUrl: Opt; - if ((iconUrl = StrCast(Document.mapIconUrl, null))) { - const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); - const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45); - const iconSize = new google.maps.Size(iconWidth, iconHeight); - icon = { - size: iconSize, - scaledSize: iconSize, - url: iconUrl - }; - } - return this.renderMarker(layout, icon); - })} + {this.contents} ; } diff --git a/webpack.config.js b/webpack.config.js index 1027f29a6..6265883fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,7 +20,7 @@ const dotenv = require('dotenv'); function transferEnvironmentVariables() { const prefix = "_CLIENT_"; - const env = dotenv.config({ debug: true }).parsed; + const env = dotenv.config().parsed; if (env) { plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((mapping, envKey) => { if (envKey.startsWith(prefix)) { @@ -64,42 +64,42 @@ module.exports = { }, module: { rules: [{ - test: [/\.tsx?$/], - use: [{ - loader: 'ts-loader', - options: { - transpileOnly: true - } - }] - }, - { - test: /\.scss|css$/, - use: [{ - loader: "style-loader" + test: [/\.tsx?$/], + use: [{ + loader: 'ts-loader', + options: { + transpileOnly: true + } + }] }, { - loader: "css-loader" + test: /\.scss|css$/, + use: [{ + loader: "style-loader" + }, + { + loader: "css-loader" + }, + { + loader: "sass-loader" + } + ] }, { - loader: "sass-loader" + test: /\.(jpg|png|pdf)$/, + use: [{ + loader: 'file-loader' + }] + }, + { + test: /\.(png|jpg|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + limit: 8192 + } + }] } - ] - }, - { - test: /\.(jpg|png|pdf)$/, - use: [{ - loader: 'file-loader' - }] - }, - { - test: /\.(png|jpg|gif)$/i, - use: [{ - loader: 'url-loader', - options: { - limit: 8192 - } - }] - } ] }, plugins, -- cgit v1.2.3-70-g09d2 From f29782b8cde4a5b0711f619d1bd7c4e223c900b9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 14 Apr 2020 09:46:40 -0400 Subject: update maps to use local variables not on Doc --- src/client/views/collections/CollectionMapView.tsx | 71 ++++++++++++---------- 1 file changed, 38 insertions(+), 33 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 8cdc145b8..5d0b0c982 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,4 +1,4 @@ -import { GoogleApiWrapper, Map, MapProps, Marker } from "google-maps-react"; +import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; @@ -11,7 +11,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager } from "../../util/UndoManager"; -import { IReactionDisposer, reaction } from "mobx"; +import { IReactionDisposer, reaction, action } from "mobx"; import requestPromise = require("request-promise"); type MapSchema = makeInterface<[typeof documentSchema]>; @@ -28,7 +28,7 @@ const base = "https://maps.googleapis.com/maps/api/geocode/json?"; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { - private mapRef = React.createRef(); + private mapRef = React.createRef(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -51,7 +51,6 @@ class CollectionMapView extends CollectionSubView & position={{ lat: location.lat, lng: location.lng }} onClick={async () => { this.map.panTo(location); - this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = 0; this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom); @@ -69,12 +68,14 @@ class CollectionMapView extends CollectionSubView & />; } + _cancelAddrReq = new Map(); + _cancelLocReq = new Map(); private get contents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; - return this.childLayoutPairs.map(({ layout }) => { + return this.childLayoutPairs.map(({ layout, data }) => { let icon: Opt, iconUrl: Opt; if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) { const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); @@ -92,38 +93,42 @@ class CollectionMapView extends CollectionSubView & lng: NumCast(layout["mapLocation-lng"]) }), ({ lat, lng }) => { - if (!BoolCast(layout._ignoreNextUpdate)) { - if (lat !== undefined && lng !== undefined) { - const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`; - requestPromise.get(target).then(res => { - layout._ignoreNextUpdate = true; - layout["mapLocation-address"] = JSON.parse(res).results[0]?.formatted_address || ""; - }); - } - } else { - layout._ignoreNextUpdate = false; + if (this._cancelLocReq.get(layout[Id])) { + this._cancelLocReq.set(layout[Id], false); + } + else if (lat !== undefined && lng !== undefined) { + const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`; + requestPromise.get(target).then(res => { + const formatted_address = JSON.parse(res).results[0].formatted_address || ""; + if (formatted_address !== layout["mapLocation-address"]) { + this._cancelAddrReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } + }); } } )); this.latlngUpdaters.push(reaction( () => ({ address: Cast(layout["mapLocation-address"], "string", null) }), ({ address }) => { - if (!BoolCast(layout._ignoreNextUpdate)) { - if (address && address.length) { - const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; - requestPromise.get(target).then(res => { - const result = JSON.parse(res).results[0]; - const { lat, lng } = result.geometry.location; - layout._ignoreNextUpdate = true; - layout["mapLocation-lat"] = lat; - layout._ignoreNextUpdate = true; - layout["mapLocation-lng"] = lng; - layout._ignoreNextUpdate = true; - layout["mapLocation-address"] = result.formatted_address; - }); - } - } else { - layout._ignoreNextUpdate = false; + if (this._cancelAddrReq.get(layout[Id])) { + this._cancelAddrReq.set(layout[Id], false); + } + else if (address?.length) { + const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; + requestPromise.get(target).then(action((res: any) => { + const { geometry, formatted_address } = JSON.parse(res).results[0]; + const { lat, lng } = geometry.location; + if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) { + this._cancelLocReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-lat", lat, true); + Doc.SetInPlace(layout, "mapLocation-lng", lng, true); + } + if (formatted_address !== address) { + this._cancelAddrReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } + })); } } )); @@ -150,7 +155,7 @@ class CollectionMapView extends CollectionSubView & style={{ pointerEvents: this.props.active() ? undefined : "none" }} onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - & }} > {this.contents} - + ; } -- cgit v1.2.3-70-g09d2 From 26893de4b384678041523a0bbf71f895ad1cd2b7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 14 Apr 2020 10:50:15 -0400 Subject: added map undo batches. fixed marker flicker on drag. split up code. --- src/client/views/collections/CollectionMapView.tsx | 111 ++++++++++----------- src/client/views/nodes/DocumentView.tsx | 2 +- 2 files changed, 55 insertions(+), 58 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 5d0b0c982..fced52275 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -10,8 +10,8 @@ import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; -import { UndoManager } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, action } from "mobx"; +import { UndoManager, undoBatch } from "../../util/UndoManager"; +import { IReactionDisposer, reaction, action, computed } from "mobx"; import requestPromise = require("request-promise"); type MapSchema = makeInterface<[typeof documentSchema]>; @@ -28,7 +28,13 @@ const base = "https://maps.googleapis.com/maps/api/geocode/json?"; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { - private mapRef = React.createRef(); + // private mapRef = React.createRef(); + // private get map() { + // return (this.mapRef.current as any).map; + // } + + private _cancelAddrReq = new Map(); + private _cancelLocReq = new Map(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -42,62 +48,62 @@ class CollectionMapView extends CollectionSubView & return undefined; } - renderMarker(layout: Doc, icon: Opt) { + markerClick = action(async (layout: Doc, location: LocationData) => { + //this.map.panTo(location); + const batch = UndoManager.StartBatch("marker click"); + this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; + this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; + location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom); + if (layout.isLinkButton && DocListCast(layout.links).length) { + await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { + this.props.addDocTab(doc, where); + finished?.(); + }, false, this.props.ContainingCollectionDoc, batch.end, undefined); + } else { + ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout }); + batch.end(); + } + }); + + renderMarkerIcon(layout: Doc) { + const iconUrl = StrCast(this.props.Document.mapIconUrl, null); + if (iconUrl) { + const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); + const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45); + const iconSize = new google.maps.Size(iconWidth, iconHeight); + return { + size: iconSize, + scaledSize: iconSize, + url: iconUrl + }; + } + } + renderMarker(layout: Doc) { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : { - this.map.panTo(location); - this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; - this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; - location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom); - if (layout.isLinkButton && DocListCast(layout.links).length) { - const batch = UndoManager.StartBatch("follow link click"); - await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { - this.props.addDocTab(doc, where); - finished?.(); - }, false, this.props.ContainingCollectionDoc, batch.end, undefined); - } else { - ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout }); - } - }} - icon={icon} + position={location} + onClick={() => this.markerClick(layout, location)} + icon={this.renderMarkerIcon(layout)} />; } - _cancelAddrReq = new Map(); - _cancelLocReq = new Map(); - private get contents() { + @computed get contents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; - return this.childLayoutPairs.map(({ layout, data }) => { - let icon: Opt, iconUrl: Opt; - if ((iconUrl = StrCast(this.props.Document.mapIconUrl, null))) { - const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); - const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45); - const iconSize = new google.maps.Size(iconWidth, iconHeight); - icon = { - size: iconSize, - scaledSize: iconSize, - url: iconUrl - }; - } + return this.childLayoutPairs.map(({ layout }) => { this.addressUpdaters.push(reaction( - () => ({ - lat: NumCast(layout["mapLocation-lat"]), - lng: NumCast(layout["mapLocation-lng"]) - }), + () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }), ({ lat, lng }) => { if (this._cancelLocReq.get(layout[Id])) { this._cancelLocReq.set(layout[Id], false); } else if (lat !== undefined && lng !== undefined) { - const target = `${base}latlng=${lat},${lng}&key=${process.env.GOOGLE_MAPS_GEO!}`; + const target = `${base}latlng=${NumCast(lat)},${NumCast(lng)}&key=${process.env.GOOGLE_MAPS_GEO!}`; requestPromise.get(target).then(res => { const formatted_address = JSON.parse(res).results[0].formatted_address || ""; if (formatted_address !== layout["mapLocation-address"]) { @@ -132,14 +138,9 @@ class CollectionMapView extends CollectionSubView & } } )); - return this.renderMarker(layout, icon); + return this.renderMarker(layout); }); } - - private get map() { - return (this.mapRef.current as any).map; - } - render() { const { childLayoutPairs } = this; const { Document } = this.props; @@ -156,16 +157,15 @@ class CollectionMapView extends CollectionSubView & onWheel={e => e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { - const { center } = this.map; - Document[this.props.fieldKey + "-mapCenter-lat"] = center.lat(); - Document[this.props.fieldKey + "-mapCenter-lng"] = center.lng(); - }} + onDragend={undoBatch(action((e: any, map: any) => { + Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); + Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); + }))} > {this.contents} @@ -174,8 +174,5 @@ class CollectionMapView extends CollectionSubView & } -const LoadingContainer = () => { - return
; -}; - +const LoadingContainer = () =>
; export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5b6478e66..0ef7ece9c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -586,7 +586,7 @@ export class DocumentView extends DocComponent(Docu } const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); } -- cgit v1.2.3-70-g09d2 From 3494d987b72b1e60bf1ded41443391952578e323 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 14 Apr 2020 13:32:33 -0400 Subject: fixed maps to support drag/drop, and fixed to work with docs that just have an address. --- .../views/collections/CollectionMapView.scss | 6 ++- src/client/views/collections/CollectionMapView.tsx | 59 ++++++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- 3 files changed, 44 insertions(+), 23 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index 4956ad946..cb58b1750 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -1,6 +1,10 @@ -.collectionMapView-contents { +.collectionMapView { width: 100%; height: 100%; + .collectionMapView-contents { + width: 100%; + height: 100%; + } } .loadingWrapper { diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index fced52275..a80f0557f 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -40,10 +40,25 @@ class CollectionMapView extends CollectionSubView & getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null); - const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null); - const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null); - return lat !== undefined && lng !== undefined ? ({ lat, lng, zoom }) : undefined; + const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; + const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; + const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; + const address: Opt = Cast(doc[fieldKey + "-address"], "string", null); + if (lat !== undefined && lng !== undefined) { + return ({ lat, lng, zoom }); + } else if (address) { + setTimeout(() => { + const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; + requestPromise.get(target).then(action((res: any) => { + const { lat, lng } = JSON.parse(res).results[0].geometry.location; + if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { + Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); + Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); + } + })); + }); + return ({ lat: 35.1592238, lng: -98.444512, zoom: 15 }); + } } return undefined; } @@ -152,23 +167,25 @@ class CollectionMapView extends CollectionSubView & } } TraceMobx(); - return
e.stopPropagation()} - onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - { - Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); - Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); - }))} - > - {this.contents} - + return
+
e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + { + Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); + Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); + }))} + > + {this.contents} + +
; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 9dd3d640b..454c3a5f2 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -309,7 +309,7 @@ export class MarqueeView extends React.Component { + getCollection = (selected: Doc[], asTemplate: boolean, isBackground?: boolean) => { const bounds = this.Bounds; // const inkData = this.ink ? this.ink.inkData : undefined; const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument; -- cgit v1.2.3-70-g09d2 From 3d4a7582dcd0df151f9571fdeb24507acefe49e1 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 14 Apr 2020 13:48:25 -0700 Subject: map view cleanup --- .../views/collections/CollectionMapView.scss | 7 +- src/client/views/collections/CollectionMapView.tsx | 142 ++++++++++++--------- 2 files changed, 87 insertions(+), 62 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index cb58b1750..2e642be6b 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -1,6 +1,7 @@ .collectionMapView { width: 100%; height: 100%; + .collectionMapView-contents { width: 100%; height: 100%; @@ -10,7 +11,7 @@ .loadingWrapper { width: 100%; height: 100%; - background-color: yellow; + background-color: pink; display: flex; flex-direction: column; justify-content: center; @@ -20,7 +21,7 @@ .loadingGif { align-self: center; justify-self: center; - width: 20%; - height: 20% + width: 50px; + height: 50px; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index a80f0557f..b6772c5a2 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -4,14 +4,13 @@ import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { TraceMobx } from "../../../new_fields/util"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, action, computed } from "mobx"; +import { IReactionDisposer, reaction, computed, runInAction } from "mobx"; import requestPromise = require("request-promise"); type MapSchema = makeInterface<[typeof documentSchema]>; @@ -23,21 +22,31 @@ export type LocationData = google.maps.LatLngLiteral & { zoom?: number; }; -const base = "https://maps.googleapis.com/maps/api/geocode/json?"; +// Nowhere, Oklahoma +const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; + +const query = async (data: string | google.maps.LatLngLiteral) => { + const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`; + const target = `https://maps.googleapis.com/maps/api/geocode/json?${contents}&key=${process.env.GOOGLE_MAPS_GEO}`; + return JSON.parse(await requestPromise.get(target)); +}; @observer class CollectionMapView extends CollectionSubView & { google: any }>(MapSchema) { - // private mapRef = React.createRef(); - // private get map() { - // return (this.mapRef.current as any).map; - // } - private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; + /** + * Note that all the uses of runInAction below are not included + * as a way to update observables (documents handle this already + * in their property setters), but rather to create a single bulk + * update and thus prevent uneeded invocations of the location- + * and address–updating reactions. + */ + getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; @@ -48,27 +57,31 @@ class CollectionMapView extends CollectionSubView & return ({ lat, lng, zoom }); } else if (address) { setTimeout(() => { - const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; - requestPromise.get(target).then(action((res: any) => { - const { lat, lng } = JSON.parse(res).results[0].geometry.location; - if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { - Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); - Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); + query(address).then(({ results }) => { + if (results?.length) { + const { lat, lng } = results[0].geometry.location; + if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { + runInAction(() => { + Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); + Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); + }); + } } - })); + }); }); - return ({ lat: 35.1592238, lng: -98.444512, zoom: 15 }); + return defaultLocation; } } return undefined; } - markerClick = action(async (layout: Doc, location: LocationData) => { - //this.map.panTo(location); + private markerClick = async (layout: Doc, { lat, lng, zoom }: LocationData) => { const batch = UndoManager.StartBatch("marker click"); - this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = location.lat; - this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = location.lng; - location.zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = location.zoom); + runInAction(() => { + this.layoutDoc[this.props.fieldKey + "-mapCenter-lat"] = lat; + this.layoutDoc[this.props.fieldKey + "-mapCenter-lng"] = lng; + zoom && (this.layoutDoc[this.props.fieldKey + "-mapCenter-zoom"] = zoom); + }); if (layout.isLinkButton && DocListCast(layout.links).length) { await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { this.props.addDocTab(doc, where); @@ -78,7 +91,7 @@ class CollectionMapView extends CollectionSubView & ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout }); batch.end(); } - }); + } renderMarkerIcon(layout: Doc) { const iconUrl = StrCast(this.props.Document.mapIconUrl, null); @@ -93,6 +106,7 @@ class CollectionMapView extends CollectionSubView & }; } } + renderMarker(layout: Doc) { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : @@ -116,14 +130,14 @@ class CollectionMapView extends CollectionSubView & ({ lat, lng }) => { if (this._cancelLocReq.get(layout[Id])) { this._cancelLocReq.set(layout[Id], false); - } - else if (lat !== undefined && lng !== undefined) { - const target = `${base}latlng=${NumCast(lat)},${NumCast(lng)}&key=${process.env.GOOGLE_MAPS_GEO!}`; - requestPromise.get(target).then(res => { - const formatted_address = JSON.parse(res).results[0].formatted_address || ""; - if (formatted_address !== layout["mapLocation-address"]) { - this._cancelAddrReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } else if (lat !== undefined && lng !== undefined) { + query({ lat: NumCast(lat), lng: NumCast(lng) }).then(({ results }) => { + if (results?.length) { + const { formatted_address } = results[0]; + if (formatted_address !== layout["mapLocation-address"]) { + this._cancelAddrReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } } }); } @@ -134,54 +148,58 @@ class CollectionMapView extends CollectionSubView & ({ address }) => { if (this._cancelAddrReq.get(layout[Id])) { this._cancelAddrReq.set(layout[Id], false); - } - else if (address?.length) { - const target = `${base}address=${address.replace(/\s+/g, "+")}&key=${process.env.GOOGLE_MAPS_GEO!}`; - requestPromise.get(target).then(action((res: any) => { - const { geometry, formatted_address } = JSON.parse(res).results[0]; - const { lat, lng } = geometry.location; - if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) { - this._cancelLocReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-lat", lat, true); - Doc.SetInPlace(layout, "mapLocation-lng", lng, true); - } - if (formatted_address !== address) { - this._cancelAddrReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } else if (address?.length) { + query(address).then(({ results }) => { + if (results?.length) { + const { geometry, formatted_address } = results[0]; + const { lat, lng } = geometry.location; + runInAction(() => { + if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) { + this._cancelLocReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-lat", lat, true); + Doc.SetInPlace(layout, "mapLocation-lng", lng, true); + } + if (formatted_address !== address) { + this._cancelAddrReq.set(layout[Id], true); + Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); + } + }); } - })); + }); } } )); return this.renderMarker(layout); }); } + render() { const { childLayoutPairs } = this; - const { Document } = this.props; - let center = this.getLocation(Document, this.props.fieldKey + "-mapCenter"); + const { Document, fieldKey, active, google } = this.props; + let center = this.getLocation(Document, fieldKey + "-mapCenter"); if (center === undefined) { center = childLayoutPairs.map(pair => this.getLocation(pair.layout, "mapLocation")).find(layout => layout); if (center === undefined) { - center = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; // nowhere, OK + center = defaultLocation; } } - TraceMobx(); return
e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > { - Document[this.props.fieldKey + "-mapCenter-lat"] = map.center.lat(); - Document[this.props.fieldKey + "-mapCenter-lng"] = map.center.lng(); - }))} + onDragend={undoBatch((_props: MapProps, map: google.maps.Map) => { + const { lat, lng } = map.getCenter(); + runInAction(() => { + Document[fieldKey + "-mapCenter-lat"] = lat(); + Document[fieldKey + "-mapCenter-lng"] = lng(); + }); + })} > {this.contents} @@ -191,5 +209,11 @@ class CollectionMapView extends CollectionSubView & } -const LoadingContainer = () =>
; -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS!, LoadingContainer })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ + apiKey: process.env.GOOGLE_MAPS!, + LoadingContainer: () => ( +
+ +
+ ) +})(CollectionMapView) as any; \ No newline at end of file -- cgit v1.2.3-70-g09d2 From a9613d73947ee9b000c695b8f3d7685feed96a27 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 14 Apr 2020 17:37:20 -0400 Subject: fixed goldenlayout borders. made filter flyout semistransparent. fixed map to stay inbounds when filter is expanded. --- src/client/goldenLayout.js | 4 +++- src/client/views/collections/CollectionMapView.scss | 3 +++ src/client/views/collections/CollectionView.scss | 1 - src/client/views/collections/CollectionView.tsx | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index b510385ff..2d4283b02 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -1551,7 +1551,7 @@ }, dimensions: { borderWidth: 5, - borderGrabWidth: 15, + borderGrabWidth: 5, minItemHeight: 10, minItemWidth: 10, headerHeight: 20, @@ -2796,11 +2796,13 @@ if (this._isVertical) { dragHandle.css('top', -handleExcessPos); dragHandle.css('height', this._size + handleExcessSize); + element.css('cursor', 'row-resize'); element.addClass('lm_vertical'); element['height'](this._size); } else { dragHandle.css('left', -handleExcessPos); dragHandle.css('width', this._size + handleExcessSize); + element.css('cursor', 'col-resize'); element.addClass('lm_horizontal'); element['width'](this._size); } diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss index cb58b1750..7e4f9a0f4 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/CollectionMapView.scss @@ -4,6 +4,9 @@ .collectionMapView-contents { width: 100%; height: 100%; + > div { + position: unset !important; // when the sidebar filter flys out, this prevents the map from extending outside the document box + } } } diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss index b92c5fdd1..a0b73b90f 100644 --- a/src/client/views/collections/CollectionView.scss +++ b/src/client/views/collections/CollectionView.scss @@ -11,7 +11,6 @@ height: 100%; overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying... - .collectionTimeView-dragger { background-color: lightgray; height: 40px; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f94277862..13a657800 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -389,7 +389,7 @@ export class CollectionView extends Touchable { return false; }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0)); } - filterBackground = () => "dimGray"; + filterBackground = () => "rgba(105, 105, 105, 0.432)"; get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves) @computed get scriptField() { const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)"; -- cgit v1.2.3-70-g09d2 From 53570be88cded2ec15338857b039564b16545d47 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 14 Apr 2020 16:21:51 -0700 Subject: factored out and handled edge case, also basic error checking on request --- src/client/views/collections/CollectionMapView.tsx | 93 ++++++++++++---------- 1 file changed, 50 insertions(+), 43 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index b6772c5a2..64946e59e 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,6 +1,6 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; +import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; @@ -28,7 +28,11 @@ const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; const query = async (data: string | google.maps.LatLngLiteral) => { const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`; const target = `https://maps.googleapis.com/maps/api/geocode/json?${contents}&key=${process.env.GOOGLE_MAPS_GEO}`; - return JSON.parse(await requestPromise.get(target)); + try { + return JSON.parse(await requestPromise.get(target)); + } catch { + return undefined; + } }; @observer @@ -36,6 +40,7 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); + private _initialLookupPending = new Map(); private addressUpdaters: IReactionDisposer[] = []; private latlngUpdaters: IReactionDisposer[] = []; @@ -47,7 +52,7 @@ class CollectionMapView extends CollectionSubView & * and address–updating reactions. */ - getLocation = (doc: Opt, fieldKey: string): Opt => { + private getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; @@ -56,19 +61,13 @@ class CollectionMapView extends CollectionSubView & if (lat !== undefined && lng !== undefined) { return ({ lat, lng, zoom }); } else if (address) { - setTimeout(() => { - query(address).then(({ results }) => { - if (results?.length) { - const { lat, lng } = results[0].geometry.location; - if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { - runInAction(() => { - Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); - Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); - }); - } - } + const id = doc[Id]; + if (!this._initialLookupPending.get(id)) { + this._initialLookupPending.set(id, true); + setTimeout(() => { + this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); }); - }); + } return defaultLocation; } } @@ -93,7 +92,7 @@ class CollectionMapView extends CollectionSubView & } } - renderMarkerIcon(layout: Doc) { + private renderMarkerIcon = (layout: Doc) => { const iconUrl = StrCast(this.props.Document.mapIconUrl, null); if (iconUrl) { const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45); @@ -107,7 +106,7 @@ class CollectionMapView extends CollectionSubView & } } - renderMarker(layout: Doc) { + private renderMarker = (layout: Doc) => { const location = this.getLocation(layout, "mapLocation"); return !location ? (null) : & />; } + private respondToAddressChange = async (newAddress: string, doc: Doc) => { + const { results } = await query(newAddress); + if (!results?.length) { + return; + } + const { geometry, formatted_address } = results[0]; + const { lat, lng } = geometry.location; + runInAction(() => { + if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { + this._cancelLocReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-lat", lat, true); + Doc.SetInPlace(doc, "mapLocation-lng", lng, true); + } + if (formatted_address !== newAddress) { + this._cancelAddrReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + } + }); + } + + private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { + const { results } = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); + if (!results?.length) { + return; + } + const { formatted_address } = results[0]; + if (formatted_address !== doc["mapLocation-address"]) { + this._cancelAddrReq.set(doc[Id], true); + Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + } + } + @computed get contents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; @@ -131,15 +162,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelLocReq.get(layout[Id])) { this._cancelLocReq.set(layout[Id], false); } else if (lat !== undefined && lng !== undefined) { - query({ lat: NumCast(lat), lng: NumCast(lng) }).then(({ results }) => { - if (results?.length) { - const { formatted_address } = results[0]; - if (formatted_address !== layout["mapLocation-address"]) { - this._cancelAddrReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); - } - } - }); + this.respondToLocationChange(lat, lng, layout); } } )); @@ -149,23 +172,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelAddrReq.get(layout[Id])) { this._cancelAddrReq.set(layout[Id], false); } else if (address?.length) { - query(address).then(({ results }) => { - if (results?.length) { - const { geometry, formatted_address } = results[0]; - const { lat, lng } = geometry.location; - runInAction(() => { - if (layout["mapLocation-lat"] !== lat || layout["mapLocation-lng"] !== lng) { - this._cancelLocReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-lat", lat, true); - Doc.SetInPlace(layout, "mapLocation-lng", lng, true); - } - if (formatted_address !== address) { - this._cancelAddrReq.set(layout[Id], true); - Doc.SetInPlace(layout, "mapLocation-address", formatted_address, true); - } - }); - } - }); + this.respondToAddressChange(address, layout); } } )); -- cgit v1.2.3-70-g09d2 From b9483279f4a778e0cc997e304b4f0b87e6ec1e5d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 00:29:00 -0400 Subject: changed collection chrome filter to just open filter sidebar. fixed scrollbar to make sure that all datapoints are included. simplified filter documents to have only a datadocument. --- src/client/documents/Documents.ts | 1 - .../views/collections/CollectionTreeView.tsx | 11 +- src/client/views/collections/CollectionView.tsx | 20 +- .../views/collections/CollectionViewChromes.scss | 3 +- .../views/collections/CollectionViewChromes.tsx | 316 ++++++--------------- 5 files changed, 100 insertions(+), 251 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e6f3b21ca..129ab0403 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -142,7 +142,6 @@ export interface DocumentOptions { treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewOpen?: boolean; // whether this document is expanded in a tree view treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked - isFacetFilter?: boolean; // whether document functions as a facet filter in a tree view limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b96ee4bc4..9ec8c4c5b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -840,12 +840,11 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey } }); const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => - Docs.Create.TextDocument("", { - title: facetValue.toString(), - treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", - {}, - { layoutDoc, facetHeader, facetValue }) - })); + { const doc = new Doc(); + doc.title = facetValue.toString(); + doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",{},{ layoutDoc, facetHeader, facetValue }); + return doc; + }); return new List(facetValueDocSet); }); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 13a657800..831691f8c 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -45,8 +45,6 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ObjectField } from '../../../new_fields/ObjectField'; import CollectionMapView from './CollectionMapView'; -import { ClientUtils } from '../../util/ClientUtils'; -import { GoogleApiWrapper } from 'google-maps-react'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -279,7 +277,8 @@ export class CollectionView extends Touchable { onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); } - @observable _facetWidth = 0; + get _facetWidth() { return NumCast(this.props.Document._facetWidth) } + set _facetWidth(value) { this.props.Document._facetWidth = value; } bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth(); getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0); @@ -365,17 +364,22 @@ export class CollectionView extends Touchable { Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox newFacet.treeViewExpandedView = "layout"; newFacet.treeViewOpen = true; - newFacet[newFacetField + "-min"] = ranged === undefined ? minVal : ranged[0]; - newFacet[newFacetField + "-max"] = ranged === undefined ? maxVal : ranged[1]; - Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = minVal; - Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = maxVal; + const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05); + const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05); + newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0]; + newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1]; + Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal; + Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal; newFacet.target = this.props.Document; const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); } else { - newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + newFacet = new Doc(); + newFacet.title = facetHeader; + newFacet.treeViewOpen = true; + newFacet.type = DocumentType.COL; const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); } diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index a691b4805..9529ef525 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -67,10 +67,11 @@ color:grey; margin-top:auto; margin-bottom:auto; + margin-left: 5px; } .collectionViewBaseChrome-viewSpecs { - margin-left: 10px; + margin-left: 25px; display: grid; .collectionViewBaseChrome-filterIcon { diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 66f627083..cef85e148 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -6,7 +6,6 @@ import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils"; import { DragManager } from "../../util/DragManager"; @@ -16,8 +15,6 @@ import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss"; import { CollectionViewType } from "./CollectionView"; import { CollectionView } from "./CollectionView"; import "./CollectionViewChromes.scss"; -import * as Autosuggest from 'react-autosuggest'; -import KeyRestrictionRow from "./KeyRestrictionRow"; const datepicker = require('js-datepicker'); interface CollectionViewChromeProps { @@ -84,62 +81,10 @@ export class CollectionViewBaseChrome extends React.Component(); private _viewRef = React.createRef(); - private _autosuggestRef = React.createRef(); @observable private _currentKey: string = ""; - @observable private _viewSpecsOpen: boolean = false; - @observable private _dateWithinValue: string = ""; - @observable private _dateValue: Date | string = ""; - @observable private _keyRestrictions: [JSX.Element, string][] = []; - @observable private suggestions: string[] = []; - @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } - - getFilters = (script: string) => { - const re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g; - const arr: any[] = re.exec(script); - const toReturn: Filter[] = []; - if (arr !== null) { - const filter: Filter = { - key: arr[2], - value: arr[3], - contains: (arr[1] === "!") ? false : true, - }; - toReturn.push(filter); - script = script.replace(arr[0], ""); - if (re.exec(script) !== null) { - toReturn.push(...this.getFilters(script)); - } - else { return toReturn; } - } - return toReturn; - } - - addKeyRestrictions = (fields: Filter[]) => { - - if (fields.length !== 0) { - for (let i = 0; i < fields.length; i++) { - this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[i][1] = value)} />, ""]); - - } - if (this._keyRestrictions.length === 1) { - this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); - } - } - else { - this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]); - this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); - } - } componentDidMount = () => { - - let fields: Filter[] = []; - if (this.filterValue) { - const string = this.filterValue.script.originalScript; - fields = this.getFilters(string); - } - runInAction(() => { - this.addKeyRestrictions(fields); // chrome status is one of disabled, collapsed, or visible. this determines initial state from document const chromeStatus = this.props.CollectionView.props.Document._chromeStatus; if (chromeStatus) { @@ -158,7 +103,7 @@ export class CollectionViewBaseChrome extends React.Component { //@ts-ignore - this.props.CollectionView.props.Document._viewType = e.target.selectedOptions[0].value; + this.document._viewType = e.target.selectedOptions[0].value; } commandChanged = (e: React.ChangeEvent) => { @@ -167,104 +112,93 @@ export class CollectionViewBaseChrome extends React.Component { - if (this._viewSpecsOpen) this.closeViewSpecs(); - else { - this._viewSpecsOpen = true; - - //@ts-ignore - if (!e.target?.classList[0]?.startsWith("qs")) { - this.closeDatePicker(); - } - - e.stopPropagation(); - document.removeEventListener("pointerdown", this.closeViewSpecs); - document.addEventListener("pointerdown", this.closeViewSpecs); - } + toggleViewSpecs = (e: React.SyntheticEvent) => { + this.document._facetWidth = this.document._facetWidth ? 0 : 200; + e.stopPropagation(); } @action closeViewSpecs = () => { - this._viewSpecsOpen = false; - document.removeEventListener("pointerdown", this.closeViewSpecs); - } - - @action - openDatePicker = (e: React.PointerEvent) => { - this.openViewSpecs(e); - if (this._picker) { - this._picker.alwaysShow = true; - this._picker.show(); - // TODO: calendar is offset when zoomed in/out - // this._picker.calendar.style.position = "absolute"; - // let transform = this.props.CollectionView.props.ScreenToLocalTransform(); - // let x = parseInt(this._picker.calendar.style.left) / transform.Scale; - // let y = parseInt(this._picker.calendar.style.top) / transform.Scale; - // this._picker.calendar.style.left = x; - // this._picker.calendar.style.top = y; - - e.stopPropagation(); - } - } - - @action - addKeyRestriction = (e: React.MouseEvent) => { - const index = this._keyRestrictions.length; - this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]); - - this.openViewSpecs(e); - } - - @action.bound - applyFilter = (e: React.MouseEvent) => { - - this.openViewSpecs(e); - - const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")"; - const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0; - const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0; - const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0; - const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7; - let dateRestrictionScript = ""; - if (this._dateValue instanceof Date) { - const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset); - const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1); - dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; - } - else { - const createdDate = new Date(this._dateValue); - if (!isNaN(createdDate.getTime())) { - const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset); - const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1); - dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; - } - } - const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ? - `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` : - `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` : - "true"; + this.document._facetWidth = 0; + } + + // @action + // openDatePicker = (e: React.PointerEvent) => { + // if (this._picker) { + // this._picker.alwaysShow = true; + // this._picker.show(); + // // TODO: calendar is offset when zoomed in/out + // // this._picker.calendar.style.position = "absolute"; + // // let transform = this.props.CollectionView.props.ScreenToLocalTransform(); + // // let x = parseInt(this._picker.calendar.style.left) / transform.Scale; + // // let y = parseInt(this._picker.calendar.style.top) / transform.Scale; + // // this._picker.calendar.style.left = x; + // // this._picker.calendar.style.top = y; + + // e.stopPropagation(); + // } + // } + + // runInAction(() => this._dateValue = e.target.value)} + // onPointerDown={this.openDatePicker} + // placeholder="Value" /> + // @action.bound + // applyFilter = (e: React.MouseEvent) => { + // const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")"; + // const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0; + // const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0; + // const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0; + // const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7; + // let dateRestrictionScript = ""; + // if (this._dateValue instanceof Date) { + // const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset); + // const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1); + // dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; + // } + // else { + // const createdDate = new Date(this._dateValue); + // if (!isNaN(createdDate.getTime())) { + // const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset); + // const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1); + // dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; + // } + // } + // const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ? + // `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` : + // `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` : + // "true"; + + // this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name }); + // } + + // datePickerRef = (node: HTMLInputElement) => { + // if (node) { + // try { + // this._picker = datepicker("#" + node.id, { + // disabler: (date: Date) => date > new Date(), + // onSelect: (instance: any, date: Date) => runInAction(() => {}), // this._dateValue = date), + // dateSelected: new Date() + // }); + // } catch (e) { + // console.log("date picker exception:" + e); + // } + // } + // } - this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name }); - } - - @action - closeDatePicker = () => { - if (this._picker) { - this._picker.alwaysShow = false; - this._picker.hide(); - } - document.removeEventListener("pointerdown", this.closeDatePicker); - } @action toggleCollapse = () => { - this.props.CollectionView.props.Document._chromeStatus = this.props.CollectionView.props.Document._chromeStatus === "enabled" ? "collapsed" : "enabled"; + this.document._chromeStatus = this.document._chromeStatus === "enabled" ? "collapsed" : "enabled"; if (this.props.collapse) { this.props.collapse(this.props.CollectionView.props.Document._chromeStatus !== "enabled"); } } subChrome = () => { - const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled"; + const collapsed = this.document._chromeStatus !== "enabled"; if (collapsed) return null; switch (this.props.type) { case CollectionViewType.Stacking: return (); @@ -279,13 +213,6 @@ export class CollectionViewBaseChrome extends React.Component { - this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction("true", { doc: Doc.name }); - this._keyRestrictions = []; - this.addKeyRestrictions([]); - } - private dropDisposer?: DragManager.DragDropDisposer; protected createDropTarget = (ele: HTMLDivElement) => { this.dropDisposer && this.dropDisposer(); @@ -304,48 +231,6 @@ export class CollectionViewBaseChrome extends React.Component { - if (node) { - try { - this._picker = datepicker("#" + node.id, { - disabler: (date: Date) => date > new Date(), - onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date), - dateSelected: new Date() - }); - } catch (e) { - console.log("date picker exception:" + e); - } - } - } - - renderSuggestion = (suggestion: string) => { - return

{suggestion}

; - } - getSuggestionValue = (suggestion: string) => suggestion; - - @action - onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { - this._currentKey = newValue; - } - onSuggestionFetch = async ({ value }: { value: string }) => { - const sugg = await this.getKeySuggestions(value); - runInAction(() => this.suggestions = sugg); - } - @action - onSuggestionClear = () => { - this.suggestions = []; - } - getKeySuggestions = async (value: string): Promise => { - return this._buttonizableCommands.filter(c => c.title.indexOf(value) !== -1).map(c => c.title); - } - - autoSuggestDown = (e: React.PointerEvent) => { - e.stopPropagation(); - } - - private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; - private _sensitivity: number = 16; - dragViewDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e, down, delta) => { const vtype = this.props.CollectionView.collectionViewType; @@ -367,7 +252,6 @@ export class CollectionViewBaseChrome extends React.Component -
+
+
+ +
+
+
@@ -414,49 +303,6 @@ export class CollectionViewBaseChrome extends React.Component
-
-
- -
-
- {this._keyRestrictions.map(i => i[0])} -
-
- CREATED WITHIN: -
- - runInAction(() => this._dateValue = e.target.value)} - onPointerDown={this.openDatePicker} - placeholder="Value" /> -
-
- - - -
-
-
-- cgit v1.2.3-70-g09d2 From d5c0ed9a29593583a4180973f854106dc10d4b30 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 02:32:47 -0400 Subject: moved filter to right side of collection. got rid of back button in pivot view - click background. --- src/Utils.ts | 10 +++-- .../views/collections/CollectionTimeView.tsx | 30 +++++++-------- src/client/views/collections/CollectionView.scss | 4 +- src/client/views/collections/CollectionView.tsx | 43 +++++++++++++++------- .../views/collections/CollectionViewChromes.scss | 8 +++- .../views/collections/CollectionViewChromes.tsx | 12 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- 8 files changed, 69 insertions(+), 43 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index a8cde0624..58f272ba5 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -475,7 +475,9 @@ export function setupMoveUpEvents( e: React.PointerEvent, moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, upEvent: (e: PointerEvent) => void, - clickEvent: (e: PointerEvent) => void) { + clickEvent: (e: PointerEvent) => void, + stopPropagation: boolean = true +) { (target as any)._downX = (target as any)._lastX = e.clientX; (target as any)._downY = (target as any)._lastY = e.clientY; @@ -499,8 +501,10 @@ export function setupMoveUpEvents( document.removeEventListener("pointermove", _moveEvent); document.removeEventListener("pointerup", _upEvent); }; - e.stopPropagation(); - e.preventDefault(); + if (stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } document.removeEventListener("pointermove", _moveEvent); document.removeEventListener("pointerup", _upEvent); document.addEventListener("pointermove", _moveEvent); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 31b720b81..1760e2419 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -72,8 +72,22 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { }), returnFalse, emptyFunction); } + contentsDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => { + let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex); + if (prevFilterIndex > 0) { + prevFilterIndex--; + this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField); + this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField); + this.props.Document._prevFilterIndex = prevFilterIndex; + } else { + this.props.Document._docFilters = new List([]); + } + }), false); + } + @computed get contents() { - return
+ return
; } @@ -132,20 +146,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb"; }; return
-
; } diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss index a0b73b90f..d43dd387a 100644 --- a/src/client/views/collections/CollectionView.scss +++ b/src/client/views/collections/CollectionView.scss @@ -20,7 +20,7 @@ top: 55%; border: 1px black solid; z-index: 2; - left: -10px; + right: -10px; } .collectionTimeView-treeView { display: flex; @@ -28,7 +28,7 @@ width: 200px; height: 100%; position: absolute; - left: 0; + right: 0; top: 0; .collectionTimeView-addfacet { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 831691f8c..0a3577bc7 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -13,7 +13,7 @@ import { List } from '../../../new_fields/List'; import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; import { TraceMobx } from '../../../new_fields/util'; -import { Utils, setupMoveUpEvents, returnFalse, returnZero } from '../../../Utils'; +import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; @@ -45,6 +45,7 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ObjectField } from '../../../new_fields/ObjectField'; import CollectionMapView from './CollectionMapView'; +import { Transform } from 'prosemirror-transform'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -281,7 +282,6 @@ export class CollectionView extends Touchable { set _facetWidth(value) { this.props.Document._facetWidth = value; } bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth(); - getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0); facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth)); @computed get dataDoc() { @@ -389,7 +389,7 @@ export class CollectionView extends Touchable { onPointerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); + this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); return false; }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0)); } @@ -415,27 +415,44 @@ export class CollectionView extends Touchable {
e.stopPropagation()}>
- Facet Filters + Facet Filters
- @@ -461,7 +478,7 @@ export class CollectionView extends Touchable { }} onContextMenu={this.onContextMenu}> {this.showIsTagged()} -
+
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
{this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => @@ -471,9 +488,7 @@ export class CollectionView extends Touchable { : ""))} {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) : -
- -
+
} {this.filterView}
); diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 9529ef525..5203eb55f 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -61,7 +61,8 @@ pointer-events: all; // margin-top: 10px; } - .collectionViewBaseChrome-template { + .collectionViewBaseChrome-template, + .collectionViewBaseChrome-viewModes { display: grid; background: rgb(238, 238, 238); color:grey; @@ -69,9 +70,12 @@ margin-bottom:auto; margin-left: 5px; } + .collectionViewBaseChrome-viewModes { + margin-left: 25px; + } .collectionViewBaseChrome-viewSpecs { - margin-left: 25px; + margin-left: 5px; display: grid; .collectionViewBaseChrome-filterIcon { diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index cef85e148..7315d2c4e 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -276,12 +276,7 @@ export class CollectionViewBaseChrome extends React.Component -
-
- -
-
-
+
@@ -303,6 +298,11 @@ export class CollectionViewBaseChrome extends React.Component
+
+
+ +
+
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a77f09163..d98fe190c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -907,6 +907,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u onViewDefDivClick = (e: React.MouseEvent, payload: any) => { (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload }); + e.stopPropagation(); } private viewDefToJSX(viewDef: ViewDefBounds): Opt { const { x, y, z } = viewDef; @@ -991,6 +992,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u {...this.getChildDocumentViewProps(pair.layout, pair.data)} dataProvider={this.childDataProvider} LayoutDoc={this.childLayoutDocFunc} + pointerEvents={this.props.layoutEngine?.() !== undefined ? "none" : undefined} jitterRotation={NumCast(this.props.Document.jitterRotation)} fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} FreezeDimensions={BoolCast(this.props.freezeChildDimensions)} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index e7cb2c015..05ad98c43 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -22,6 +22,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { width?: number; height?: number; jitterRotation: number; + pointerEvents?: "none"; transition?: string; fitToBox?: boolean; } @@ -92,7 +93,7 @@ export class CollectionFreeFormDocumentView extends DocComponent {!this.props.fitToBox ? -- cgit v1.2.3-70-g09d2 From a6d17ef79fb94334735998d3d11f7ea068dea93a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 03:15:25 -0400 Subject: cleaned up some more of the pivot view event handling api --- .../views/collections/CollectionTimeView.tsx | 23 ++++++++++------------ src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +++++-- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 1760e2419..e05223ca0 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,6 +1,6 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; import { RichTextField } from "../../../new_fields/RichTextField"; @@ -26,18 +26,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { _changing = false; @observable _layoutEngine = "pivot"; @observable _collapsed: boolean = false; - componentWillUnmount() { - this.props.Document.onChildClick = undefined; - } + @observable _childClickedScript: Opt; + @observable _viewDefDivClick: Opt; async componentDidMount() { - const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; - this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" }); - this.props.Document._fitToBox = true; - if (!this.props.Document.onViewDefClick) { - this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); - } - this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked; - DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), ""); + const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), ""); + const childText = "const alias = getAlias(this); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; + runInAction(() => { + this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! }); + this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); + }); } layoutEngine = () => this._layoutEngine; @@ -88,7 +85,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @computed get contents() { return
- +
; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 0a3577bc7..19e235ff2 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -278,7 +278,7 @@ export class CollectionView extends Touchable { onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); } - get _facetWidth() { return NumCast(this.props.Document._facetWidth) } + get _facetWidth() { return NumCast(this.props.Document._facetWidth); } set _facetWidth(value) { this.props.Document._facetWidth = value; } bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d98fe190c..f19181a20 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,6 +44,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { CollectionViewType } from "../CollectionView"; +import { Script } from "vm"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -69,6 +70,8 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); export type collectionFreeformViewProps = { forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) + childClickScript?: ScriptField; + viewDefDivClick?: ScriptField; }; @observer @@ -847,7 +850,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u } @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; } - @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); } backgroundHalo = () => BoolCast(this.Document.useClusters); getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { @@ -906,7 +909,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u } onViewDefDivClick = (e: React.MouseEvent, payload: any) => { - (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload }); + (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload }); e.stopPropagation(); } private viewDefToJSX(viewDef: ViewDefBounds): Opt { -- cgit v1.2.3-70-g09d2 From 142cb372c6dec9fa641786784cbdd6dca6896d33 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 00:42:07 -0700 Subject: added robust error checking for lat, lng and address values, reverts to previous on invalid entry or zero results --- src/client/views/collections/CollectionMapView.tsx | 63 +++++++++++++++------- 1 file changed, 44 insertions(+), 19 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 64946e59e..5b8b6e75a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -12,6 +12,7 @@ import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; import { IReactionDisposer, reaction, computed, runInAction } from "mobx"; import requestPromise = require("request-promise"); +import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -119,11 +120,11 @@ class CollectionMapView extends CollectionSubView & } private respondToAddressChange = async (newAddress: string, doc: Doc) => { - const { results } = await query(newAddress); - if (!results?.length) { - return; + const response = await query(newAddress); + if (!response || response.status === "ZERO_RESULTS") { + return false; } - const { geometry, formatted_address } = results[0]; + const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { @@ -136,18 +137,20 @@ class CollectionMapView extends CollectionSubView & Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } }); + return true; } private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { - const { results } = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); - if (!results?.length) { - return; + const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); + if (!response || response.status === "ZERO_RESULTS") { + return false; } - const { formatted_address } = results[0]; + const { formatted_address } = response.results[0]; if (formatted_address !== doc["mapLocation-address"]) { this._cancelAddrReq.set(doc[Id], true); Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } + return true; } @computed get contents() { @@ -156,23 +159,45 @@ class CollectionMapView extends CollectionSubView & this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; return this.childLayoutPairs.map(({ layout }) => { + const id = layout[Id]; this.addressUpdaters.push(reaction( () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }), - ({ lat, lng }) => { - if (this._cancelLocReq.get(layout[Id])) { - this._cancelLocReq.set(layout[Id], false); - } else if (lat !== undefined && lng !== undefined) { - this.respondToLocationChange(lat, lng, layout); + emptyFunction, + { + equals: (previous, { lat, lng }) => { + if (this._cancelLocReq.get(id)) { + this._cancelLocReq.set(id, false); + } else if (lat !== undefined && lng !== undefined) { + this.respondToLocationChange(lat, lng, layout).then(success => { + if (!success) { + this._cancelLocReq.set(id, true); + runInAction(() => { + layout["mapLocation-lat"] = previous.lat; + layout["mapLocation-lng"] = previous.lng; + }); + } + }); + } + return previous === { lat, lng }; } } )); this.latlngUpdaters.push(reaction( - () => ({ address: Cast(layout["mapLocation-address"], "string", null) }), - ({ address }) => { - if (this._cancelAddrReq.get(layout[Id])) { - this._cancelAddrReq.set(layout[Id], false); - } else if (address?.length) { - this.respondToAddressChange(address, layout); + () => Cast(layout["mapLocation-address"], "string", null), + emptyFunction, + { + equals: (previous, address) => { + if (this._cancelAddrReq.get(id)) { + this._cancelAddrReq.set(id, false); + } else if (address?.length) { + this.respondToAddressChange(address, layout).then(success => { + if (!success) { + this._cancelAddrReq.set(id, true); + layout["mapLocation-address"] = previous; + } + }); + } + return previous === address; } } )); -- cgit v1.2.3-70-g09d2 From 15f3a7898268b8fc6eded456515726a2c40b6b46 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 01:22:18 -0700 Subject: using interesting mobx paradigm to manage value interception --- src/client/views/collections/CollectionMapView.tsx | 49 ++++++++++------------ 1 file changed, 21 insertions(+), 28 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 5b8b6e75a..4fbccac33 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -10,7 +10,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, computed, runInAction } from "mobx"; +import { IReactionDisposer, reaction, computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); import { emptyFunction } from "../../../Utils"; @@ -42,8 +42,8 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); private _initialLookupPending = new Map(); - private addressUpdaters: IReactionDisposer[] = []; - private latlngUpdaters: IReactionDisposer[] = []; + private addressUpdaters: Lambda[] = []; + private latlngUpdaters: Lambda[] = []; /** * Note that all the uses of runInAction below are not included @@ -153,18 +153,17 @@ class CollectionMapView extends CollectionSubView & return true; } - @computed get contents() { + @computed get reactiveContents() { this.addressUpdaters.forEach(disposer => disposer()); this.addressUpdaters = []; this.latlngUpdaters.forEach(disposer => disposer()); this.latlngUpdaters = []; return this.childLayoutPairs.map(({ layout }) => { const id = layout[Id]; - this.addressUpdaters.push(reaction( - () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }), - emptyFunction, - { - equals: (previous, { lat, lng }) => { + this.addressUpdaters.push( + computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) + .observe(({ oldValue, newValue }) => { + const { lat, lng } = newValue; if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); } else if (lat !== undefined && lng !== undefined) { @@ -172,35 +171,29 @@ class CollectionMapView extends CollectionSubView & if (!success) { this._cancelLocReq.set(id, true); runInAction(() => { - layout["mapLocation-lat"] = previous.lat; - layout["mapLocation-lng"] = previous.lng; + layout["mapLocation-lat"] = oldValue ? oldValue.lat : undefined; + layout["mapLocation-lng"] = oldValue ? oldValue.lng : undefined; }); } }); } - return previous === { lat, lng }; - } - } - )); - this.latlngUpdaters.push(reaction( - () => Cast(layout["mapLocation-address"], "string", null), - emptyFunction, - { - equals: (previous, address) => { + }) + ); + this.latlngUpdaters.push( + computed(() => Cast(layout["mapLocation-address"], "string", null)) + .observe(({ oldValue, newValue }) => { if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); - } else if (address?.length) { - this.respondToAddressChange(address, layout).then(success => { + } else if (newValue?.length) { + this.respondToAddressChange(newValue, layout).then(success => { if (!success) { this._cancelAddrReq.set(id, true); - layout["mapLocation-address"] = previous; + layout["mapLocation-address"] = oldValue; } }); } - return previous === address; - } - } - )); + }) + ); return this.renderMarker(layout); }); } @@ -233,7 +226,7 @@ class CollectionMapView extends CollectionSubView & }); })} > - {this.contents} + {this.reactiveContents}
; -- cgit v1.2.3-70-g09d2 From a3e2d21f848bae54b0de8d3a81a996f2f551c84f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 11:50:36 -0400 Subject: fixed onCheckedClick to use scriptingBox --- src/client/documents/Documents.ts | 2 ++ .../views/collections/CollectionTreeView.tsx | 27 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/ScriptingBox.tsx | 4 ++-- .../authentication/models/current_user_utils.ts | 6 +++-- 5 files changed, 23 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 129ab0403..608bd28ae 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -116,6 +116,8 @@ export interface DocumentOptions { boxShadow?: string; dontRegisterChildren?: boolean; "onClick-rawScript"?: string; // onClick script in raw text form + "onCheckedClick-rawScript"?: string; // onChecked script in raw text form + "onCheckedClick-params"?: List; // parameter list for onChecked treeview functions _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views schemaColumns?: List; dockingConfig?: string; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 9ec8c4c5b..d2c8cc3ad 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -19,7 +19,7 @@ import { makeTemplate } from '../../util/DropConverter'; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; +import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; @@ -397,11 +397,13 @@ class TreeView extends React.Component { } } + get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.props.document.onCheckedClick); } + @action bulletClick = (e: React.MouseEvent) => { - if (this.props.onCheckedClick && this.props.document.type !== DocumentType.COL) { + if (this.onCheckedClick && this.props.document.type !== DocumentType.COL) { // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check"; - ScriptCast(this.props.onCheckedClick).script.run({ + this.onCheckedClick.script.run({ this: this.props.document.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.props.document, heading: this.props.containingCollection.title, checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check", @@ -415,7 +417,7 @@ class TreeView extends React.Component { @computed get renderBullet() { - const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined; + const checked = this.props.document.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined; return
{}
; @@ -764,8 +766,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ - description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document, - "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", treeViewContainer: Doc.name }) + description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } @@ -818,7 +819,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields), - BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick || ScriptCast(this.props.Document.onCheckedClick), + BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick, this.props.onChildClick || ScriptCast(this.props.Document.onChildClick), this.props.ignoreFields) } @@ -839,12 +840,12 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey nonNumbers++; } }); - const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => - { const doc = new Doc(); - doc.title = facetValue.toString(); - doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",{},{ layoutDoc, facetHeader, facetValue }); - return doc; - }); + const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => { + const doc = new Doc(); + doc.title = facetValue.toString(); + doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue }); + return doc; + }); return new List(facetValueDocSet); }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cb7811fbb..54e4d549e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -746,7 +746,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); - onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) }); + onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); const funcs: ContextMenuProps[] = []; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index a257898ab..c607d6614 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -28,8 +28,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )", { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 }); - const onClick = Docs.Create.ScriptingDocument(ScriptField.MakeScript("console.log('click')"), { title: "onClick", isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 }, "onClick"); + const onClick = Docs.Create.ScriptingDocument(undefined, { title: "onClick", "onClick-rawScript": "console.log('click')", isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 }, "onClick"); + const onCheckedClick = Docs.Create.ScriptingDocument(undefined, + { title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 }, "onCheckedClick"); doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" }); - doc.clickFuncs = Docs.Create.TreeDocument([onClick], { title: "onClick funcs" }); + doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" }); } static updateUserDocument(doc: Doc) { -- cgit v1.2.3-70-g09d2 From 86885c0e97322ae99f331e594a5c67cf04cb4ec2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 12:33:21 -0400 Subject: extended getLocation to work with the map itself in addition to makers so that document titles can be used to indicate locations. --- src/client/views/collections/CollectionMapView.tsx | 35 +++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 4fbccac33..583594057 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,18 +1,17 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; +import { computed, Lambda, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult, Opt } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; +import { DocumentManager } from "../../util/DocumentManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); -import { DocumentManager } from "../../util/DocumentManager"; -import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); -import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -58,15 +57,15 @@ class CollectionMapView extends CollectionSubView & const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; - const address: Opt = Cast(doc[fieldKey + "-address"], "string", null); + const address: Opt = Cast(doc[fieldKey + "-address"], "string", (lat === undefined || lng === undefined ? Cast(doc.title, "string", null) : null)); if (lat !== undefined && lng !== undefined) { return ({ lat, lng, zoom }); } else if (address) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { - this._initialLookupPending.set(id, true); + this._initialLookupPending.set(id, true); `` setTimeout(() => { - this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); + this.respondToAddressChange(address, fieldKey, doc).then(() => this._initialLookupPending.delete(id)); }); } return defaultLocation; @@ -119,7 +118,7 @@ class CollectionMapView extends CollectionSubView & />; } - private respondToAddressChange = async (newAddress: string, doc: Doc) => { + private respondToAddressChange = async (newAddress: string, fieldKey: string, doc: Doc) => { const response = await query(newAddress); if (!response || response.status === "ZERO_RESULTS") { return false; @@ -127,28 +126,28 @@ class CollectionMapView extends CollectionSubView & const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { - if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { + if (doc[fieldKey + "-lat"] !== lat || doc[fieldKey + "-lng"] !== lng) { this._cancelLocReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-lat", lat, true); - Doc.SetInPlace(doc, "mapLocation-lng", lng, true); + Doc.SetInPlace(doc, fieldKey + "-lat", lat, true); + Doc.SetInPlace(doc, fieldKey + "-lng", lng, true); } if (formatted_address !== newAddress) { this._cancelAddrReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + Doc.SetInPlace(doc, fieldKey + "-address", formatted_address, true); } }); return true; } - private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { + private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, fieldKey: string, doc: Doc) => { const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); if (!response || response.status === "ZERO_RESULTS") { return false; } const { formatted_address } = response.results[0]; - if (formatted_address !== doc["mapLocation-address"]) { + if (formatted_address !== doc[fieldKey + "-address"]) { this._cancelAddrReq.set(doc[Id], true); - Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); + Doc.SetInPlace(doc, fieldKey + "-address", formatted_address, true); } return true; } @@ -167,7 +166,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); } else if (lat !== undefined && lng !== undefined) { - this.respondToLocationChange(lat, lng, layout).then(success => { + this.respondToLocationChange(lat, lng, "mapLocation", layout).then(success => { if (!success) { this._cancelLocReq.set(id, true); runInAction(() => { @@ -185,7 +184,7 @@ class CollectionMapView extends CollectionSubView & if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); } else if (newValue?.length) { - this.respondToAddressChange(newValue, layout).then(success => { + this.respondToAddressChange(newValue, "mapLocation", layout).then(success => { if (!success) { this._cancelAddrReq.set(id, true); layout["mapLocation-address"] = oldValue; -- cgit v1.2.3-70-g09d2 From 531aab6465459557055c0c02e35b1029192263b4 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 10:19:58 -0700 Subject: final cleanup --- src/client/views/collections/CollectionMapView.tsx | 83 +++++++++++----------- 1 file changed, 42 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 4fbccac33..062419b74 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,6 +1,6 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Id } from "../../../new_fields/FieldSymbols"; import { makeInterface } from "../../../new_fields/Schema"; @@ -10,9 +10,8 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { DocumentManager } from "../../util/DocumentManager"; import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { IReactionDisposer, reaction, computed, runInAction, Lambda } from "mobx"; +import { computed, runInAction, Lambda } from "mobx"; import requestPromise = require("request-promise"); -import { emptyFunction } from "../../../Utils"; type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -23,8 +22,14 @@ export type LocationData = google.maps.LatLngLiteral & { zoom?: number; }; +interface DocLatLng { + lat: FieldResult; + lng: FieldResult; +} + // Nowhere, Oklahoma const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 }; +const noResults = "ZERO_RESULTS"; const query = async (data: string | google.maps.LatLngLiteral) => { const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`; @@ -42,8 +47,7 @@ class CollectionMapView extends CollectionSubView & private _cancelAddrReq = new Map(); private _cancelLocReq = new Map(); private _initialLookupPending = new Map(); - private addressUpdaters: Lambda[] = []; - private latlngUpdaters: Lambda[] = []; + private responders: { location: Lambda, address: Lambda }[] = []; /** * Note that all the uses of runInAction below are not included @@ -66,7 +70,7 @@ class CollectionMapView extends CollectionSubView & if (!this._initialLookupPending.get(id)) { this._initialLookupPending.set(id, true); setTimeout(() => { - this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id)); + this.respondToAddressChange(doc, address).then(() => this._initialLookupPending.delete(id)); }); } return defaultLocation; @@ -119,30 +123,45 @@ class CollectionMapView extends CollectionSubView & />; } - private respondToAddressChange = async (newAddress: string, doc: Doc) => { + private respondToAddressChange = async (doc: Doc, newAddress: string, oldAddress?: string) => { + if (newAddress === oldAddress) { + return false; + } const response = await query(newAddress); - if (!response || response.status === "ZERO_RESULTS") { + const id = doc[Id]; + if (!response || response.status === noResults) { + this._cancelAddrReq.set(id, true); + doc["mapLocation-address"] = oldAddress; return false; } const { geometry, formatted_address } = response.results[0]; const { lat, lng } = geometry.location; runInAction(() => { if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) { - this._cancelLocReq.set(doc[Id], true); + this._cancelLocReq.set(id, true); Doc.SetInPlace(doc, "mapLocation-lat", lat, true); Doc.SetInPlace(doc, "mapLocation-lng", lng, true); } if (formatted_address !== newAddress) { - this._cancelAddrReq.set(doc[Id], true); + this._cancelAddrReq.set(id, true); Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true); } }); return true; } - private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => { - const response = await query({ lat: NumCast(newLat), lng: NumCast(newLng) }); - if (!response || response.status === "ZERO_RESULTS") { + private respondToLocationChange = async (doc: Doc, newLatLng: DocLatLng, oldLatLng: Opt) => { + if (newLatLng === oldLatLng) { + return false; + } + const response = await query({ lat: NumCast(newLatLng.lat), lng: NumCast(newLatLng.lng) }); + const id = doc[Id]; + if (!response || response.status === noResults) { + this._cancelLocReq.set(id, true); + runInAction(() => { + doc["mapLocation-lat"] = oldLatLng?.lat; + doc["mapLocation-lng"] = oldLatLng?.lng; + }); return false; } const { formatted_address } = response.results[0]; @@ -154,46 +173,28 @@ class CollectionMapView extends CollectionSubView & } @computed get reactiveContents() { - this.addressUpdaters.forEach(disposer => disposer()); - this.addressUpdaters = []; - this.latlngUpdaters.forEach(disposer => disposer()); - this.latlngUpdaters = []; + this.responders.forEach(({ location, address }) => { location(); address(); }); + this.responders = []; return this.childLayoutPairs.map(({ layout }) => { const id = layout[Id]; - this.addressUpdaters.push( - computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) + this.responders.push({ + location: computed(() => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] })) .observe(({ oldValue, newValue }) => { - const { lat, lng } = newValue; if (this._cancelLocReq.get(id)) { this._cancelLocReq.set(id, false); - } else if (lat !== undefined && lng !== undefined) { - this.respondToLocationChange(lat, lng, layout).then(success => { - if (!success) { - this._cancelLocReq.set(id, true); - runInAction(() => { - layout["mapLocation-lat"] = oldValue ? oldValue.lat : undefined; - layout["mapLocation-lng"] = oldValue ? oldValue.lng : undefined; - }); - } - }); + } else if (newValue.lat !== undefined && newValue.lng !== undefined) { + this.respondToLocationChange(layout, newValue, oldValue); } - }) - ); - this.latlngUpdaters.push( - computed(() => Cast(layout["mapLocation-address"], "string", null)) + }), + address: computed(() => Cast(layout["mapLocation-address"], "string", null)) .observe(({ oldValue, newValue }) => { if (this._cancelAddrReq.get(id)) { this._cancelAddrReq.set(id, false); } else if (newValue?.length) { - this.respondToAddressChange(newValue, layout).then(success => { - if (!success) { - this._cancelAddrReq.set(id, true); - layout["mapLocation-address"] = oldValue; - } - }); + this.respondToAddressChange(layout, newValue, oldValue); } }) - ); + }); return this.renderMarker(layout); }); } -- cgit v1.2.3-70-g09d2 From 9a2d00da67b0436d28dd64f8a25aa8a2b1a5140d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 10:40:07 -0700 Subject: syntax --- src/client/views/collections/CollectionMapView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 1d8ad2458..b5043d1c5 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -207,7 +207,7 @@ class CollectionMapView extends CollectionSubView & const { Document, fieldKey, active, google } = this.props; let center = this.getLocation(Document, `${fieldKey}-mapCenter`); if (center === undefined) { - center = childLayoutPairs.map(pair => this.getLocation(pair.layout, fieldKey)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From da6d058cd842209bdfd0f618e1cc19c99139135b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 14:14:57 -0400 Subject: improved title support for map entries using @ to reset address. added lockedPosition support for maps... kinda. --- src/client/views/collections/CollectionMapView.tsx | 43 ++++++++++++++-------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 583594057..f533a3874 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,5 +1,5 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react"; -import { computed, Lambda, runInAction } from "mobx"; +import { computed, Lambda, runInAction, action } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, FieldResult, Opt } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; @@ -54,22 +54,22 @@ class CollectionMapView extends CollectionSubView & private getLocation = (doc: Opt, fieldKey: string): Opt => { if (doc) { - const lat: Opt = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; - const lng: Opt = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; - const zoom: Opt = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; - const address: Opt = Cast(doc[fieldKey + "-address"], "string", (lat === undefined || lng === undefined ? Cast(doc.title, "string", null) : null)); - if (lat !== undefined && lng !== undefined) { - return ({ lat, lng, zoom }); - } else if (address) { + const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined; + const lat = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined; + const lng = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined; + const zoom = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined; + const address = titleLoc || StrCast(doc[fieldKey + "-address"], StrCast(doc.title)); + if (titleLoc || (address && (lat === undefined || lng === undefined))) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { - this._initialLookupPending.set(id, true); `` + this._initialLookupPending.set(id, true); setTimeout(() => { + titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true); this.respondToAddressChange(address, fieldKey, doc).then(() => this._initialLookupPending.delete(id)); }); } - return defaultLocation; } + return (lat === undefined || lng === undefined) ? defaultLocation : { lat, lng, zoom }; } return undefined; } @@ -217,12 +217,25 @@ class CollectionMapView extends CollectionSubView & zoom={center.zoom || 10} initialCenter={center} center={center} + onIdle={(_props: any, map: any) => { + if (this.layoutDoc.lockedTransform) { + map.setZoom(center?.zoom || 10); + } else { + center?.zoom !== map.getZoom() && undoBatch(action(() => { + Document[fieldKey + "-mapCenter-zoom"] = map.getZoom(); + }))(); + } + }} onDragend={undoBatch((_props: MapProps, map: google.maps.Map) => { - const { lat, lng } = map.getCenter(); - runInAction(() => { - Document[fieldKey + "-mapCenter-lat"] = lat(); - Document[fieldKey + "-mapCenter-lng"] = lng(); - }); + if (this.layoutDoc.lockedTransform) { + center?.lat && center.lng && map.setCenter(center); + } else { + const { lat, lng } = map.getCenter(); + runInAction(() => { + Document[fieldKey + "-mapCenter-lat"] = lat(); + Document[fieldKey + "-mapCenter-lng"] = lng(); + }); + } })} > {this.reactiveContents} -- cgit v1.2.3-70-g09d2 From 4d1782cbb359de2b672786df24985fbcf4be238a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 14:49:57 -0400 Subject: fixed bug just added with initial location of map --- src/client/views/collections/CollectionMapView.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 3051b1ae7..2cb25fd9a 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -57,13 +57,13 @@ class CollectionMapView extends CollectionSubView & * and address–updating reactions. */ - private getLocation = (doc: Opt, fieldKey: string): Opt => { + private getLocation = (doc: Opt, fieldKey: string, returnDefault: boolean = true): Opt => { if (doc) { const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined; const lat = Cast(doc[`${fieldKey}-lat`], "number", null) || (Cast(doc[`${fieldKey}-lat`], "string", null) && Number(Cast(doc[`${fieldKey}-lat`], "string", null))) || undefined; const lng = Cast(doc[`${fieldKey}-lng`], "number", null) || (Cast(doc[`${fieldKey}-lng`], "string", null) && Number(Cast(doc[`${fieldKey}-lng`], "string", null))) || undefined; const zoom = Cast(doc[`${fieldKey}-zoom`], "number", null) || (Cast(doc[`${fieldKey}-zoom`], "string", null) && Number(Cast(doc[`${fieldKey}-zoom`], "string", null))) || undefined; - const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title)); + const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title).replace(/^-/, "")); if (titleLoc || (address && (lat === undefined || lng === undefined))) { const id = doc[Id]; if (!this._initialLookupPending.get(id)) { @@ -74,7 +74,7 @@ class CollectionMapView extends CollectionSubView & }); } } - return (lat === undefined || lng === undefined) ? defaultLocation : { lat, lng, zoom }; + return (lat === undefined || lng === undefined) ? (returnDefault ? defaultLocation : undefined) : { lat, lng, zoom }; } return undefined; } @@ -205,9 +205,9 @@ class CollectionMapView extends CollectionSubView & render() { const { childLayoutPairs } = this; const { Document, fieldKey, active, google } = this.props; - let center = this.getLocation(Document, `${fieldKey}-mapCenter`); + let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey, false)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From 7d3b2880bebf034607b946360c3e08338cf1d115 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 14:04:13 -0700 Subject: fixed fieldKey errors: --- src/client/views/collections/CollectionMapView.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 2cb25fd9a..c80555a2e 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -99,8 +99,9 @@ class CollectionMapView extends CollectionSubView & } private renderMarkerIcon = (layout: Doc) => { - const { Document, fieldKey } = this.props; - const iconUrl = StrCast(Document.mapIconUrl, null); + const { Document } = this.props; + const fieldKey = Doc.LayoutFieldKey(layout); + const iconUrl = StrCast(layout.mapIconUrl, StrCast(Document.mapIconUrl)); if (iconUrl) { const iconWidth = NumCast(layout[`${fieldKey}-iconWidth`], 45); const iconHeight = NumCast(layout[`${fieldKey}-iconHeight`], 45); @@ -114,7 +115,7 @@ class CollectionMapView extends CollectionSubView & } private renderMarker = (layout: Doc) => { - const location = this.getLocation(layout, this.props.fieldKey); + const location = this.getLocation(layout, Doc.LayoutFieldKey(layout)); return !location ? (null) : & } @computed get reactiveContents() { - const { fieldKey } = this.props; this.responders.forEach(({ location, address }) => { location(); address(); }); this.responders = []; return this.childLayoutPairs.map(({ layout }) => { + const fieldKey = Doc.LayoutFieldKey(layout); const id = layout[Id]; this.responders.push({ location: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] })) @@ -207,7 +208,7 @@ class CollectionMapView extends CollectionSubView & const { Document, fieldKey, active, google } = this.props; let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, fieldKey, false)).find(layout => layout); + center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)).find(layout => layout); if (center === undefined) { center = defaultLocation; } -- cgit v1.2.3-70-g09d2 From e0f16b89cba102a4fcd156bb3d4148432eca2ab7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 15 Apr 2020 19:10:09 -0400 Subject: removed lock icon from top-level documents. made schema functions compile without typechecking. metadata #'s starting with a # are now numbers. --- src/client/util/RichTextRules.ts | 3 ++- src/client/views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 6bbe81115..3746199ba 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -110,7 +110,8 @@ export class RichTextRules { return state.tr; } if (value !== "" && value !== undefined) { - this.Document[DataSym][fieldKey] = value === "true" ? true : value === "false" ? false : value; + const num = value.match(/^[0-9.]/); + this.Document[DataSym][fieldKey] = value === "true" ? true : value === "false" ? false : (num ? Number(value) : value); } const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid }); return state.tr.deleteRange(start, end).insert(start, fieldView); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 57be77fdd..380d91d2f 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -736,7 +736,7 @@ export class SchemaTable extends React.Component { return (doc as any)[key][row + ${row}][(doc as any).schemaColumns[col + ${col}].heading]; } return ${script}`; - const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: true, transformer: this.createTransformer(row, col) }); + const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) }); if (compiled.compiled) { doc[field] = new ComputedField(compiled); return true; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e6ae2d801..c0d530160 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1163,7 +1163,7 @@ export class DocumentView extends DocComponent(Docu
: this.innards} - {this.Document.isBackground !== undefined || this.isSelected(false) ?
this.toggleBackground(true)}>
: (null)} + {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 ?
this.toggleBackground(true)}>
: (null)}
; { this._showKPQuery ? : undefined; } } -- cgit v1.2.3-70-g09d2 From 35a31253ed3c7f4d31cebc2d1f2d6b1b4084108c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 15 Apr 2020 22:27:38 -0700 Subject: cleanup --- src/client/views/collections/CollectionMapView.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index c80555a2e..7b7828d7d 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -208,10 +208,8 @@ class CollectionMapView extends CollectionSubView & const { Document, fieldKey, active, google } = this.props; let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false); if (center === undefined) { - center = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)).find(layout => layout); - if (center === undefined) { - center = defaultLocation; - } + const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false)); + center = childLocations.find(location => location) || defaultLocation; } return
& center={center} onIdle={(_props?: MapProps, map?: google.maps.Map) => { if (this.layoutDoc.lockedTransform) { - map?.setZoom(center?.zoom || 10); // reset zoom (probably can tell the map to disallow zooming somehow) + // reset zoom (ideally, we could probably can tell the map to disallow zooming somehow instead) + map?.setZoom(center?.zoom || 10); + map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); } else { const zoom = map?.getZoom(); - center?.zoom !== zoom && undoBatch(action(() => { + (center?.zoom !== zoom) && undoBatch(action(() => { Document[`${fieldKey}-mapCenter-zoom`] = zoom; }))(); } }} onDragend={(_props?: MapProps, map?: google.maps.Map) => { if (this.layoutDoc.lockedTransform) { - map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); // reset the drag (probably can tell the map to disallow dragging somehow) + // reset the drag (ideally, we could probably can tell the map to disallow dragging somehow instead) + map?.setCenter({ lat: center?.lat!, lng: center?.lng! }); } else { undoBatch(action(({ lat, lng }) => { Document[`${fieldKey}-mapCenter-lat`] = lat(); -- cgit v1.2.3-70-g09d2 From ab007b636b99de38f7dee8ff08d1259dfc47e02a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 16 Apr 2020 21:47:56 -0400 Subject: fixed detail view for pivot views for templates --- src/client/views/collections/CollectionTimeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index e05223ca0..1d1949c99 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -30,7 +30,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @observable _viewDefDivClick: Opt; async componentDidMount() { const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), ""); - const childText = "const alias = getAlias(this); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; + const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! }); this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); -- cgit v1.2.3-70-g09d2 From ac40232e5bbbb98680912eb3c505bab994079074 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 17 Apr 2020 09:39:25 -0400 Subject: fixed auto focus to work on clicked pt --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f19181a20..02f9bd487 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -31,7 +31,7 @@ import { ContextMenu } from "../../ContextMenu"; import { ContextMenuProps } from "../../ContextMenuItem"; import { InkingControl } from "../../InkingControl"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; -import { DocumentViewProps } from "../../nodes/DocumentView"; +import { DocumentViewProps, DocumentView } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import PDFMenu from "../../pdf/PDFMenu"; @@ -822,7 +822,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u if (!willZoom) { Doc.BrushDoc(this.props.Document); - !doc.z && this.scaleAtPt([NumCast(doc.x), NumCast(doc.y)], 1); + !doc.z && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1); } else { if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) { if (!doc.z) this.setPan(newPanX, newPanY, "Ease", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c0d530160..fb0b1d4f4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -317,6 +317,9 @@ export class DocumentView extends DocComponent(Docu if ((this.props.Document.onDragStart || (this.props.Document.rootDocument && this.props.Document.isTemplateForField)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } else { + DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0]; + DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)]; + this.props.focus(this.props.Document, false); SelectionManager.SelectDoc(this, e.ctrlKey); } @@ -326,6 +329,7 @@ export class DocumentView extends DocComponent(Docu preventDefault && e.preventDefault(); } } + static _focusHack: number[] = []; // bcz :this will get fixed... // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place -- cgit v1.2.3-70-g09d2 From 67c56296339bd1c760b704c1d14710ec6f575d49 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 17 Apr 2020 22:55:10 -0400 Subject: added freeze button for web boxes. cleaned up pointerevent setting throughout code. --- src/client/views/InkingStroke.scss | 8 +++- src/client/views/InkingStroke.tsx | 5 +-- src/client/views/Main.scss | 1 - src/client/views/MainView.scss | 1 + .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/MarqueeView.scss | 1 - src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.scss | 1 - src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.scss | 1 - src/client/views/nodes/KeyValueBox.scss | 1 - src/client/views/nodes/LabelBox.scss | 1 - src/client/views/nodes/LinkAnchorBox.scss | 1 - src/client/views/nodes/PresBox.scss | 1 - src/client/views/nodes/QueryBox.scss | 1 - src/client/views/nodes/ScreenshotBox.scss | 5 --- src/client/views/nodes/ScriptingBox.scss | 1 - src/client/views/nodes/SliderBox.scss | 1 - src/client/views/nodes/VideoBox.scss | 7 ++-- src/client/views/nodes/WebBox.scss | 15 ++++---- src/client/views/nodes/WebBox.tsx | 44 +++++++++------------- 22 files changed, 42 insertions(+), 64 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/InkingStroke.scss b/src/client/views/InkingStroke.scss index cdbfdcff3..433433a42 100644 --- a/src/client/views/InkingStroke.scss +++ b/src/client/views/InkingStroke.scss @@ -1,3 +1,7 @@ -.inkingStroke-marker { - mix-blend-mode: multiply +.inkingStroke { + mix-blend-mode: multiply; + stroke-linejoin: round; + stroke-linecap: round; + overflow: visible !important; + transform-origin: top left; } \ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index f66c04e1f..7a318d5c2 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,4 +1,3 @@ -import { computed } from "mobx"; import { observer } from "mobx-react"; import { documentSchema } from "../../new_fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../new_fields/InkField"; @@ -47,14 +46,12 @@ export class InkingStroke extends ViewBoxBaseComponent { ContextMenu.Instance.addItem({ diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 4709e7ef2..a2a9ceca5 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -24,7 +24,6 @@ body { .jsx-parser { width: 100%; height: 100%; - pointer-events: none; border-radius: inherit; position: inherit; // background: inherit; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index e95802e54..e9f2248ad 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -28,6 +28,7 @@ width: 100%; height: 100%; position: absolute; + pointer-events: all; top: 0; left: 0; z-index: 1; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d2c8cc3ad..510c9924b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -453,7 +453,7 @@ class TreeView extends React.Component { fontWeight: this.props.document.searchMatch ? "bold" : undefined, textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined, outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined, - pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none" + pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? undefined : "none" }} > {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? this.editableView("title") : diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 19e235ff2..877e4355f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -472,7 +472,7 @@ export class CollectionView extends Touchable { }; return (
(Docu const titleView = (!showTitle ? (null) :
(Docu transformOrigin: this._animate ? "center center" : undefined, transform: this._animate ? `scale(${this._animate})` : undefined, transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out", - pointerEvents: this.ignorePointerEvents ? "none" : "all", + pointerEvents: this.ignorePointerEvents ? "none" : undefined, color: StrCast(this.layoutDoc.color, "inherit"), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 7d40b3149..7a05ec3a3 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -25,7 +25,6 @@ overflow-x: hidden; color: initial; height: 100%; - pointer-events: all; max-height: 100%; display: flex; flex-direction: row; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d641dc791..62911dc5c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1185,7 +1185,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"]), opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1, color: this.props.hideOnLeave ? "white" : "inherit", - pointerEvents: interactive ? "none" : "all", + pointerEvents: interactive ? "none" : undefined, fontSize: NumCast(this.layoutDoc.fontSize, 13), fontFamily: StrCast(this.layoutDoc.fontFamily, "Crimson Text"), }} diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 7bbf4a368..49425c2c2 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -1,6 +1,5 @@ .imageBox, .imageBox-dragging { - pointer-events: all; border-radius: inherit; width: 100%; height: 100%; diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss index a26880c9e..eb7c2f32b 100644 --- a/src/client/views/nodes/KeyValueBox.scss +++ b/src/client/views/nodes/KeyValueBox.scss @@ -8,7 +8,6 @@ border-radius: $border-radius; box-sizing: border-box; display: inline-block; - pointer-events: all; cursor: default; .imageBox-cont img { width: auto; diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss index ab5b2c6b3..56dd86ff9 100644 --- a/src/client/views/nodes/LabelBox.scss +++ b/src/client/views/nodes/LabelBox.scss @@ -1,7 +1,6 @@ .labelBox-outerDiv { width: 100%; height: 100%; - pointer-events: all; border-radius: inherit; display: flex; flex-direction: column; diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss index 7b6093ebd..24f9c1ea0 100644 --- a/src/client/views/nodes/LinkAnchorBox.scss +++ b/src/client/views/nodes/LinkAnchorBox.scss @@ -4,7 +4,6 @@ width: 15; height: 15; border-radius: 20px; - pointer-events: all; user-select: none; .linkAnchorBox-linkCloser { diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index ba8389fda..6676d8cd2 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -10,7 +10,6 @@ letter-spacing: 2px; overflow: hidden; transition: 0.7s opacity ease; - pointer-events: all; .presBox-buttons { padding: 10px; diff --git a/src/client/views/nodes/QueryBox.scss b/src/client/views/nodes/QueryBox.scss index 82f64054c..b5f90aa1e 100644 --- a/src/client/views/nodes/QueryBox.scss +++ b/src/client/views/nodes/QueryBox.scss @@ -2,5 +2,4 @@ width: 100%; height: 100%; position: absolute; - pointer-events: all; } \ No newline at end of file diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss index 6cc184948..141960f60 100644 --- a/src/client/views/nodes/ScreenshotBox.scss +++ b/src/client/views/nodes/ScreenshotBox.scss @@ -1,5 +1,4 @@ .screenshotBox { - pointer-events: all; transform-origin: top left; background: white; color: black; @@ -21,10 +20,6 @@ height: Auto; } -.screenshotBox-content-interactive, .screenshotBox-content-fullScreen { - pointer-events: all; -} - .screenshotBox-uiButtons { background:dimgray; border: orange solid 1px; diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index 678a1a22d..43695f00d 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -3,7 +3,6 @@ height: 100%; display: flex; flex-direction: column; - pointer-events: all; background-color: rgb(241, 239, 235); padding: 10px; .scriptingBox-inputDiv { diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss index 4ef277d8c..78015bd70 100644 --- a/src/client/views/nodes/SliderBox.scss +++ b/src/client/views/nodes/SliderBox.scss @@ -1,7 +1,6 @@ .sliderBox-outerDiv { width: 100%; height: 100%; - pointer-events: all; border-radius: inherit; display: flex; flex-direction: column; diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index fabbf5196..0c0854ac2 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,5 +1,4 @@ .videoBox { - pointer-events: all; transform-origin: top left; .videoBox-viewer { opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger @@ -24,9 +23,9 @@ height: 100%; } -.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen { - pointer-events: all; -} +// .videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen { +// pointer-events: all; +// } .videoBox-time{ color : white; diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 3014b9425..cdeac4bd1 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -18,8 +18,7 @@ display:none; } } -.webBox-cont, -.webBox-cont-dragging { +.webBox-cont { padding: 0vw; position: absolute; top: 0; @@ -32,8 +31,6 @@ } .webBox-cont-interactive { - pointer-events: all; - span { user-select: text !important; } @@ -49,7 +46,6 @@ width: 100%; height: 100%; position: absolute; - pointer-events: all; } .webBox-button { @@ -59,7 +55,7 @@ height: 100%; } -.webView-urlEditor { +.webBox-urlEditor { position: relative; opacity: 0.9; z-index: 9001; @@ -74,6 +70,12 @@ padding-bottom: 10px; overflow: hidden; + .webBox-freeze { + display: flex; + align-items: center; + justify-content: center; + } + .editorBase { display: flex; @@ -115,7 +117,6 @@ width: 100%; height: 100%; position: absolute; - pointer-events: all; .indicator { position: absolute; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index b48437464..26e947f5b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { library } from "@fortawesome/fontawesome-svg-core"; -import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { faStickyNote, faLock, faUnlock } from '@fortawesome/free-solid-svg-icons'; import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, FieldResult } from "../../../new_fields/Doc"; @@ -23,6 +23,7 @@ import React = require("react"); import * as WebRequest from 'web-request'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { DocumentView } from "./DocumentView"; const htmlToText = require("html-to-text"); library.add(faStickyNote); @@ -116,31 +117,21 @@ export class WebBox extends ViewBoxAnnotatableComponent { - let url: string = ""; - const field = Cast(this.rootDoc[this.props.fieldKey], WebField); - if (field) url = field.url.href; - - const newBox = Docs.Create.TextDocument(url, { - x: NumCast(this.rootDoc.x), - y: NumCast(this.rootDoc.y), - title: url, - _width: 200, - _height: 70, - }); - - SelectionManager.SelectedDocuments().map(dv => { - dv.props.addDocument?.(newBox); - dv.props.removeDocument?.(dv.rootDoc); - }); - - Doc.BrushDoc(newBox); + toggleNativeDimensions = () => { + if (this.Document._nativeWidth || this.Document._nativeHeight) { + DocumentView.unfreezeNativeDimensions(this.layoutDoc); + this.layoutDoc.lockedTransform = false; + } + else { + Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight()); + this.layoutDoc.lockedTransform = true; + } } urlEditor() { + const frozen = this.layoutDoc._nativeWidth && this.layoutDoc._nativeHeight; return ( -
+
-
- +
+
@@ -331,7 +322,8 @@ export class WebBox extends ViewBoxAnnotatableComponent {!frozen ? (null) : -
+
@@ -350,7 +342,7 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.content}
e.stopPropagation()} onScroll={e => { if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) { -- cgit v1.2.3-70-g09d2 From d3aebc8404685496673a8611a6d72ff7695ad191 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 18 Apr 2020 13:42:03 -0400 Subject: several pointerevent fixes broken by last change. added scroll to target for webBox links. --- src/client/util/DocumentManager.ts | 13 ++- .../views/collections/CollectionTreeView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 - src/client/views/nodes/DocumentView.scss | 10 -- src/client/views/nodes/DocumentView.tsx | 118 ++++++++++----------- src/client/views/nodes/FormattedTextBoxComment.tsx | 4 +- src/client/views/nodes/LinkAnchorBox.scss | 1 + src/client/views/nodes/LinkAnchorBox.tsx | 2 + src/client/views/nodes/LinkBox.tsx | 1 - src/client/views/nodes/WebBox.tsx | 22 ++-- src/client/views/pdf/Annotation.tsx | 4 +- src/client/views/pdf/PDFViewer.tsx | 8 +- 13 files changed, 100 insertions(+), 91 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 2d6078cf3..e66e67723 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -156,7 +156,12 @@ export class DocumentManager { let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); if (annotatedDoc) { const first = getFirstDocView(annotatedDoc); - if (first) annotatedDoc = first.props.Document; + if (first) { + annotatedDoc = first.props.Document; + if (docView) { + docView.props.focus(annotatedDoc, false); + } + } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); @@ -219,9 +224,9 @@ export class DocumentManager { if (linkDoc) { const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 : (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; - const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : - doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number"): - (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number"):Cast(linkDoc.anchor1_timecode, "number"))); + const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : + doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); if (target) { const containerDoc = (await Cast(target.annotationOn, Doc)) || target; containerDoc.currentTimecode = targetTimecode; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 510c9924b..5ff8c11ee 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -145,7 +145,6 @@ class TreeView extends React.Component { ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this))); } - onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { this.props.active(true) && Doc.BrushDoc(this.dataDoc); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 02f9bd487..b3409bd57 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -804,7 +804,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u if (!annotOn) { this.props.focus(doc); } else { - const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height); + const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height); const offset = annotOn && (contextHgt / 2 * 96 / 72); this.props.Document.scrollY = NumCast(doc.y) - offset; } @@ -820,7 +820,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document.scale, pt: this.Document.panTransformType }; - if (!willZoom) { + if (!willZoom && DocumentView._focusHack.length) { Doc.BrushDoc(this.props.Document); !doc.z && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1); } else { @@ -995,7 +995,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u {...this.getChildDocumentViewProps(pair.layout, pair.data)} dataProvider={this.childDataProvider} LayoutDoc={this.childLayoutDocFunc} - pointerEvents={this.props.layoutEngine?.() !== undefined ? "none" : undefined} + pointerEvents={this.props.layoutEngine?.() !== undefined ? false : undefined} jitterRotation={NumCast(this.props.Document.jitterRotation)} fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} FreezeDimensions={BoolCast(this.props.freezeChildDimensions)} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 05ad98c43..e325a70c0 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -22,7 +22,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { width?: number; height?: number; jitterRotation: number; - pointerEvents?: "none"; transition?: string; fitToBox?: boolean; } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index fc9ee1201..81ae36cc0 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -38,16 +38,6 @@ display:flex; overflow: hidden; } - .documentView-linkAnchorBoxWrapper { - pointer-events: none; - position: absolute; - transform-origin: top left; - width: 100%; - height: 100%; - top:0; - left:0; - z-index: 1; - } .documentView-lock { width: 20; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2f22488e4..2bae2fa96 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -80,6 +80,7 @@ export interface DocumentViewProps { ContentScaling: () => number; PanelWidth: () => number; PanelHeight: () => number; + pointerEvents?: boolean; focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void; parentActive: (outsideReaction: boolean) => boolean; whenActiveChanged: (isActive: boolean) => void; @@ -994,38 +995,42 @@ export class DocumentView extends DocComponent(Docu screenToLocalTransform = () => this.props.ScreenToLocalTransform(); @computed get contents() { TraceMobx(); - return (); + return (<> + + {this.anchors} + + ); } linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document); @@ -1049,20 +1054,19 @@ export class DocumentView extends DocComponent(Docu @computed get anchors() { TraceMobx(); return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => -
- -
); + ); } @computed get innards() { TraceMobx(); @@ -1101,22 +1105,18 @@ export class DocumentView extends DocComponent(Docu SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)} />
); - return <> - {this.anchors} - {!showTitle && !showCaption ? - this.contents : -
-
- {this.contents} -
- {titleView} - {captionView} + return !showTitle && !showCaption ? + this.contents : +
+
+ {this.contents}
- } - ; + {titleView} + {captionView} +
; } @computed get ignorePointerEvents() { - return (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); + return this.props.pointerEvents === false || (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @observable _animate = 0; diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx index 35304033f..41df5b3c1 100644 --- a/src/client/views/nodes/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/FormattedTextBoxComment.tsx @@ -16,6 +16,7 @@ import React = require("react"); import { Docs } from "../../documents/Documents"; import wiki from "wikijs"; import { DocumentType } from "../../documents/DocumentTypes"; +import { DocumentView } from "./DocumentView"; export let formattedTextBoxCommentPlugin = new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } @@ -85,8 +86,9 @@ export class FormattedTextBoxComment { const textBox = FormattedTextBoxComment.textBox; if (FormattedTextBoxComment.linkDoc && !keep && textBox) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab":"onRight"); + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); } else { + DocumentView._focusHack = []; DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); } diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss index 24f9c1ea0..710f2178b 100644 --- a/src/client/views/nodes/LinkAnchorBox.scss +++ b/src/client/views/nodes/LinkAnchorBox.scss @@ -5,6 +5,7 @@ height: 15; border-radius: 20px; user-select: none; + pointer-events: all; .linkAnchorBox-linkCloser { position: absolute; diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 13ffc6956..3b1ced815 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -17,6 +17,7 @@ import { LinkEditor } from "../linking/LinkEditor"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SelectionManager } from "../../util/SelectionManager"; import { TraceMobx } from "../../../new_fields/util"; +import { DocumentView } from "./DocumentView"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -74,6 +75,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { + DocumentView._focusHack = []; DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false); this._editing = false; }), 300 - (Date.now() - this._lastTap)); diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index af4bf420f..740f2ef04 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -17,7 +17,6 @@ export class LinkBox extends ViewBoxBaseComponent( public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); } render() { return
e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ background: this.props.backgroundColor?.(this.props.Document) }} > (); private _iframeRef = React.createRef(); private _iframeIndicatorRef = React.createRef(); private _iframeDragRef = React.createRef(); - @observable private _pressX: number = 0; - @observable private _pressY: number = 0; - private _scrollTop = 0; + private _reactionDisposer?: IReactionDisposer; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); iframeLoaded = action((e: any) => { @@ -53,6 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.scrollY, + (scrollY) => { + if (scrollY !== undefined) { + this._outerRef.current!.scrollTop = scrollY; + this.layoutDoc.scrollY = undefined; + } + }, + { fireImmediately: true } + ); }); setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; iframedown = (e: PointerEvent) => { @@ -60,7 +69,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { const scroll = (e.target as any)?.children?.[0].scrollTop; - this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = this._scrollTop = scroll; + this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll; } async componentDidMount() { @@ -87,6 +96,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { else if (e.button === 0) { const annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { - DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined); + DocumentView._focusHack = []; + DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined); e.stopPropagation(); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 948d2300d..75be08e9f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List"; import { makeInterface, createSchema } from "../../../new_fields/Schema"; import { ScriptField, ComputedField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero } from "../../../Utils"; +import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero, emptyPath } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -607,7 +607,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { - if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { + if (!this.props.Document[HeightSym]() || !this.props.Document._nativeHeight) { setTimeout((() => { this.Document._height = this.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; this.Document._nativeHeight = (this.Document._nativeWidth || 0) * this._coverPath.height / this._coverPath.width; @@ -632,7 +632,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent + return
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )}
; @@ -643,7 +643,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent Date: Sat, 18 Apr 2020 17:42:09 -0400 Subject: cleaned up webbox - fit width by default. native dimensions by default. toggle buttons for annotate/interact. --- src/client/documents/Documents.ts | 4 +- .../views/collections/CollectionDockingView.tsx | 30 ++++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/PDFBox.scss | 3 - src/client/views/nodes/WebBox.scss | 21 +++--- src/client/views/nodes/WebBox.tsx | 78 ++++++++++++---------- src/client/views/pdf/PDFViewer.scss | 4 -- src/client/views/pdf/PDFViewer.tsx | 7 +- 8 files changed, 77 insertions(+), 72 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 968e3d4dc..baaee09ea 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -590,7 +590,7 @@ export namespace Docs { } export function WebDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(new URL(url)), { _fitWidth: true, ...options }); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { @@ -914,7 +914,7 @@ export namespace Docs { }); } ctor = Docs.Create.WebDocument; - options = { _height: options._width, ...options, title: path, _nativeWidth: undefined }; + options = { ...options, _nativeWidth: 850, _nativeHeight: 962, _width: 500, _height: 566, title: path, }; } return ctor ? ctor(path, options) : undefined; } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d77ef812f..c74f5555b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -739,19 +739,27 @@ export class DockedFrameRenderer extends React.Component { nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; contentScaling = () => { - if (this.layoutDoc!.type === DocumentType.PDF) { - if ((this.layoutDoc && this.layoutDoc._fitWidth) || - this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) { - return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); - } else { - return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight); - } - } const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); - if (!nativeW || !nativeH) return 1; - const wscale = this.panelWidth() / nativeW; - return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; + let scaling = 1; + if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) { + scaling = 1; + } else if ((this.layoutDoc?._fitWidth) || + this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) { + scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); + } else { + // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) { + // if ((this.layoutDoc?._fitWidth) || + // this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) { + // return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); + // } else { + // return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight); + // } + // } + const wscale = this.panelWidth() / nativeW; + scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale; + } + return scaling; } ScreenToLocalTransform = () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b3409bd57..87cf716e5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -822,7 +822,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u if (!willZoom && DocumentView._focusHack.length) { Doc.BrushDoc(this.props.Document); - !doc.z && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1); + !doc.z && NumCast(this.layoutDoc.scale) < 1 && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1); } else { if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) { if (!doc.z) this.setPan(newPanX, newPanY, "Ease", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 7a3d2e92b..bccf0f291 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -199,9 +199,6 @@ .pdfBox { pointer-events: none; - .collectionFreeFormView-none { - pointer-events: none; - } .pdfViewer-text { .textLayer { span { diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index cdeac4bd1..af84a7d95 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -48,11 +48,17 @@ position: absolute; } -.webBox-button { - padding: 0vw; - border: none; +.webBox-buttons { + margin-left: 44; + background:lightGray; width: 100%; - height: 100%; +} +.webBox-freeze { + display: flex; + align-items: center; + justify-content: center; + margin-right: 5px; + width: 30px; } .webBox-urlEditor { @@ -60,7 +66,6 @@ opacity: 0.9; z-index: 9001; transition: top .5s; - background: lightgrey; padding: 10px; @@ -70,12 +75,6 @@ padding-bottom: 10px; overflow: hidden; - .webBox-freeze { - display: flex; - align-items: center; - justify-content: center; - } - .editorBase { display: flex; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 510e6be0c..0a7772044 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { library } from "@fortawesome/fontawesome-svg-core"; -import { faStickyNote, faLock, faUnlock } from '@fortawesome/free-solid-svg-icons'; +import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons'; import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, FieldResult } from "../../../new_fields/Doc"; @@ -34,8 +34,8 @@ const WebDocument = makeInterface(documentSchema); export class WebBox extends ViewBoxAnnotatableComponent(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } - @observable private collapsed: boolean = true; - @observable private url: string = "hello"; + @observable private _collapsed: boolean = true; + @observable private _url: string = "hello"; @observable private _pressX: number = 0; @observable private _pressY: number = 0; @@ -105,19 +105,19 @@ export class WebBox extends ViewBoxAnnotatableComponent) => { - this.url = e.target.value; + this._url = e.target.value; } @action submitURL = () => { - this.dataDoc[this.props.fieldKey] = new WebField(new URL(this.url)); + this.dataDoc[this.props.fieldKey] = new WebField(new URL(this._url)); } @action setURL() { const urlField: FieldResult = Cast(this.dataDoc[this.props.fieldKey], WebField); - if (urlField) this.url = urlField.url.toString(); - else this.url = ""; + if (urlField) this._url = urlField.url.toString(); + else this._url = ""; } onValueKeyDown = async (e: React.KeyboardEvent) => { @@ -128,36 +128,44 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (this.Document._nativeWidth || this.Document._nativeHeight) { - DocumentView.unfreezeNativeDimensions(this.layoutDoc); + if (!this.layoutDoc.isAnnotating) { + //DocumentView.unfreezeNativeDimensions(this.layoutDoc); this.layoutDoc.lockedTransform = false; + this.layoutDoc.isAnnotating = true; } else { - Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight()); + //Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight()); this.layoutDoc.lockedTransform = true; + this.layoutDoc.isAnnotating = false; } } urlEditor() { - const frozen = this.layoutDoc._nativeWidth && this.layoutDoc._nativeHeight; + const frozen = this.layoutDoc._nativeWidth && this.layoutDoc.isAnnotating; return ( -
+
-
+
+
+ +
+
+ +
@@ -170,9 +178,6 @@ export class WebBox extends ViewBoxAnnotatableComponent SUBMIT -
- -
@@ -183,7 +188,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - this.collapsed = !this.collapsed; + this._collapsed = !this._collapsed; } _ignore = 0; @@ -326,20 +331,19 @@ export class WebBox extends ViewBoxAnnotatableComponent -
- {content} -
- {!frozen ? (null) : -
-
-
-
-
-
} - ); + return (<> +
+ {content} +
+ {!frozen ? (null) : +
+
+
+
+
+
} + ); } scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop)) render() { @@ -352,7 +356,7 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.content}
e.stopPropagation()} onScroll={e => { if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) { diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 5cd2c4fe4..26c31a80e 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -30,10 +30,6 @@ .page { position: relative; } - .collectionfreeformview-container { - pointer-events: none; - } - .pdfViewer-text-selected { .textLayer{ pointer-events: all; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 75be08e9f..d54390236 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -282,7 +282,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { // creates annotation documents for current highlights const annotationDoc = this.makeAnnotationDocument(color); - annotationDoc && this.props.addDocument && this.props.addDocument(annotationDoc); + annotationDoc && this.props?.addDocument(annotationDoc); return annotationDoc; } @@ -585,6 +584,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) { const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); + annotationDoc.isLinkButton = true; if (link) link.followLinkLocation = "onRight"; } } @@ -641,7 +641,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent (this.Document.scrollHeight || this.Document._nativeHeight || 0); panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0); @computed get overlayLayer() { - return
+ return
Date: Sat, 18 Apr 2020 23:25:15 -0400 Subject: changed structure of template docs in sidebar. got rid of special context menu in tree view in favor of pushing everything to document context menus which ca now be extended by the local document or through props. --- src/client/documents/Documents.ts | 1 + src/client/util/DocumentManager.ts | 2 +- src/client/views/MainView.scss | 2 + src/client/views/MainView.tsx | 15 +- .../views/collections/CollectionStackingView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 46 ++---- src/client/views/nodes/DocumentView.scss | 1 - src/client/views/nodes/DocumentView.tsx | 26 ++-- .../authentication/models/current_user_utils.ts | 159 ++++++++++++--------- 9 files changed, 136 insertions(+), 118 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3ac823bb0..3073707a6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -79,6 +79,7 @@ export interface DocumentOptions { x?: number; y?: number; z?: number; + author?: string; dropAction?: dropActionType; childDropAction?: dropActionType; layoutKey?: string; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index e66e67723..4683e77a8 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -241,4 +241,4 @@ export class DocumentManager { } } } -Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file +Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index e9f2248ad..81d427f64 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -5,6 +5,7 @@ .mainView-tabButtons { position: relative; width: 100%; + margin-top: 10px; } .mainContent-div { @@ -72,6 +73,7 @@ flex-direction: column; position: relative; height: 100%; + background: dimgray; .documentView-node-topmost { background: lightgrey; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index aec1f960a..40cabcf83 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faTerminal, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; +import { faTerminal, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -41,11 +41,12 @@ import { RadialMenu } from './nodes/RadialMenu'; import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; +import { ScriptField } from '../../new_fields/ScriptField'; @observer export class MainView extends React.Component { public static Instance: MainView; - private _buttonBarHeight = 35; + private _buttonBarHeight = 26; private _flyoutSizeOnDown = 0; private _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); @@ -102,7 +103,10 @@ export class MainView extends React.Component { } library.add(faTerminal); + library.add(faWindowMaximize); library.add(faFileAlt); + library.add(faAddressCard); + library.add(faQuestionCircle); library.add(faStickyNote); library.add(faFont); library.add(faExclamation); @@ -212,6 +216,11 @@ export class MainView extends React.Component { const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc); const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + + const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + mainDoc.contextMenuScripts = new List([toggleTheme!]); + mainDoc.contextMenuLabels = new List(["Toggle Theme Colors"]); + Doc.AddDocToList(workspaces, "data", mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => this.openWorkspace(mainDoc), 0); @@ -459,7 +468,7 @@ export class MainView extends React.Component { } @computed get mainContent() { - const sidebar = this.userDoc && this.userDoc.sidebarContainer; + const sidebar = this.userDoc?.sidebarContainer; return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index dd84c4d6e..b15649d83 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -63,7 +63,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const dxf = () => this.getDocTransform(d, dref.current!); this._docXfs.push({ dxf: dxf, width: width, height: height }); const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); - const style = this.isStackingView ? { width: width(), marginTop: this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` }; + const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` }; return
{this.getDisplayDoc(d, (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc, dxf, width)}
; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 5ff8c11ee..2df1614de 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,7 +32,7 @@ import { Templates } from '../Templates'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { CollectionViewType } from './CollectionView'; +import { CollectionViewType, CollectionView } from './CollectionView'; import { RichTextField } from '../../../new_fields/RichTextField'; import { DocumentView } from '../nodes/DocumentView'; @@ -210,33 +210,6 @@ class TreeView extends React.Component { })} />) - onWorkspaceContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view - const sort = this.props.document[`${this.fieldKey}-sortAscending`]; - if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) { - ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List(), icon: "plus" }); - } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) { - ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" }); - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" }); - if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" }); - } - ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); - } else { - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); - ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); - } - ContextMenu.Instance.addItem({ description: (sort ? "Sort Descending" : (sort === false ? "Unsort" : "Sort Ascending")), event: () => this.props.document[`${this.fieldKey}-sortAscending`] = (sort ? false : (sort === false ? undefined : true)), icon: "minus" }); - ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" }); - ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" }); - ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); - e.stopPropagation(); - e.preventDefault(); - } - } - @undoBatch treeDrop = (e: Event, de: DragManager.DropEvent) => { const pt = [de.x, de.y]; @@ -356,7 +329,11 @@ class TreeView extends React.Component { const remDoc = (doc: Doc) => this.remove(doc, expandKey); const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true); const docs = expandKey === "links" ? this.childLinks : this.childDocs; - return
    + const sortKey = `${this.fieldKey}-sortAscending`; + return
      { + this.props.document[sortKey] = (this.props.document[sortKey] ? false : (this.props.document[sortKey] === false ? undefined : true)); + e.stopPropagation(); + }}> {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document), this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, @@ -421,6 +398,12 @@ class TreeView extends React.Component { {}
; } + + focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true); + contextMenuItems = () => { + const focusScript = ScriptField.MakeFunction(`DocFocus(self)`); + return [{ script: focusScript!, label: "Focus" }]; + } /** * Renders the EditableView title element for placement into the tree. */ @@ -442,7 +425,7 @@ class TreeView extends React.Component { })}> {this.treeViewExpandedView} ); - const openRight = (
+ const openRight = (
); return <> @@ -474,6 +457,7 @@ class TreeView extends React.Component { PanelHeight={returnZero} NativeHeight={returnZero} NativeWidth={returnZero} + contextMenuItems={this.contextMenuItems} renderDepth={1} focus={emptyFunction} parentActive={returnTrue} @@ -491,7 +475,7 @@ class TreeView extends React.Component { render() { setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); - return
+ return
  • {this.renderBullet} diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 81ae36cc0..692493414 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -71,7 +71,6 @@ display: inline-block; width: 100%; height: 100%; - pointer-events: none; .documentView-styleContentWrapper { width: 100%; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e3013698d..12f5b27e1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,21 +1,24 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; -import { action, computed, runInAction, trace, observable } from "mobx"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; -import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; import { Document, PositionDocument } from '../../../new_fields/documentSchemas'; import { Id } from '../../../new_fields/FieldSymbols'; import { InkTool } from '../../../new_fields/InkField'; import { RichTextField } from '../../../new_fields/RichTextField'; import { listSpec } from "../../../new_fields/Schema"; +import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField'; import { ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField'; import { TraceMobx } from '../../../new_fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; -import { emptyFunction, returnOne, returnTransparent, returnTrue, Utils, OmitKeys, returnZero } from "../../../Utils"; +import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; +import { ClientRecommender } from '../../ClientRecommender'; import { DocServer } from "../../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; @@ -24,6 +27,7 @@ import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { Scripting } from '../../util/Scripting'; +import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; import { Transform } from "../../util/Transform"; @@ -35,19 +39,13 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { InkingControl } from '../InkingControl'; +import { KeyphraseQueryView } from '../KeyphraseQueryView'; import { OverlayView } from '../OverlayView'; -import { ScriptBox } from '../ScriptBox'; import { ScriptingRepl } from '../ScriptingRepl'; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; -import React = require("react"); -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField'; -import { ClientRecommender } from '../../ClientRecommender'; -import { SearchUtil } from '../../util/SearchUtil'; import { RadialMenu } from './RadialMenu'; -import { KeyphraseQueryView } from '../KeyphraseQueryView'; -import { undo } from 'prosemirror-history'; +import React = require("react"); library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -65,6 +63,7 @@ export interface DocumentViewProps { LayoutDoc?: () => Opt; LibraryPath: Doc[]; fitToBox?: boolean; + contextMenuItems?: () => { script: ScriptField, label: string }[]; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected onClick?: ScriptField; onPointerDown?: ScriptField; @@ -720,6 +719,11 @@ export class DocumentView extends DocComponent(Docu const cm = ContextMenu.Instance; const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); + const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []); + Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) => + cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" })); + this.props.contextMenuItems?.().forEach(item => + cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" })); const existing = cm.findByDescription("Layout..."); const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: (e) => this.toggleBackground(false), icon: this.Document.lockedPosition ? "unlock" : "lock" }); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 32cce9608..e7a163a3e 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -18,6 +18,7 @@ import { makeTemplate } from "../../../client/util/DropConverter"; import { RichTextField } from "../../../new_fields/RichTextField"; import { PrefetchProxy } from "../../../new_fields/Proxy"; import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox"; +import { MainView } from "../../../client/views/MainView"; export class CurrentUserUtils { private static curr_id: string; @@ -51,7 +52,17 @@ export class CurrentUserUtils { ]; doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" }); Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues); - doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 })); + doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 })) + doc.templateButtons = Docs.Create.MasonryDocument(CurrentUserUtils.setupTemplateButtons(doc), { + title: "template buttons", + _pivotField: "author", _xMargin: 0, + _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + }); + doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as any as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], { + title: "template layouts", _xPadding: 0, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) + })); } static setupDefaultIconTypes(doc: Doc, buttons?: string[]) { doc.iconView = new PrefetchProxy(Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") })); @@ -86,7 +97,7 @@ export class CurrentUserUtils { // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, - { title: "Drag a search box", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, + { title: "Drag a search box", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a document previewer", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" }, ]; @@ -94,6 +105,7 @@ export class CurrentUserUtils { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, icon: data.icon, title: data.title, + author: "Draggable Items", ignoreClick: data.ignoreClick, dropAction: data.click ? "copy" : undefined, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, @@ -105,22 +117,40 @@ export class CurrentUserUtils { })); } + static setupTemplateButtons(doc: Doc) { + const queryTemplate = Docs.Create.MulticolumnDocument( + [ + Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }), + Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true }) + ], + { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); + queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); + const slideTemplate = Docs.Create.MultirowDocument( + [ + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }), + Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true }) + ], + { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); + slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); + const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" }); + Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description"); + descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); + + doc.slidesBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "address-card" }); + doc.descriptionBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "description view", icon: "window-maximize" }); + doc.queryBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "query view", icon: "question-circle" }); + + return [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc]; + } + + static async updateCreatorButtons(doc: Doc) { - const toolsBtn = await Cast(doc.ToolsBtn, Doc); - if (toolsBtn) { - const stackingDoc = await Cast(toolsBtn.sourcePanel, Doc); - if (stackingDoc) { - const stackdocs = await Cast(stackingDoc.data, listSpec(Doc)); - if (stackdocs) { - const dragset = await Cast(stackdocs[0], Doc); - if (dragset) { - const dragdocs = await Cast(dragset.data, listSpec(Doc)); - if (dragdocs) { - const dragDocs = await Promise.all(dragdocs); - this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb)); - } - } - } + const dragset = await Cast(doc.dragCreators, Doc); + if (dragset) { + const dragdocs = await Cast(dragset.data, listSpec(Doc)); + if (dragdocs) { + const dragDocs = await Promise.all(dragdocs); + this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb)); } } } @@ -203,9 +233,16 @@ export class CurrentUserUtils { // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators - const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), { - _width: 500, _autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _yMargin: 5 + const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc); + doc.dragCreators = Docs.Create.MasonryDocument(creatorBtns, { + title: "drag Creators", + _pivotField: "author", _xMargin: 0, + _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + }); + doc.allCreators = Docs.Create.StackingDocument([doc.dragCreators as any as Doc, doc.templateButtons as any as Doc], { + title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, + _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", }); // setup a color picker const color = Docs.Create.ColorDocument({ @@ -215,7 +252,7 @@ export class CurrentUserUtils { return Docs.Create.ButtonDocument({ _width: 35, _height: 25, title: "Tools", fontSize: 10, targetContainer: sidebarContainer, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.StackingDocument([dragCreators, color], { + sourcePanel: Docs.Create.StackingDocument([doc.allCreators as Doc, color], { _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true }), onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), @@ -228,6 +265,9 @@ export class CurrentUserUtils { doc.workspaces = Docs.Create.TreeDocument([], { title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, }); + const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`); + (doc.workspaces as Doc).contextMenuScripts = new List([newWorkspace!]); + (doc.workspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); doc.documents = Docs.Create.TreeDocument([], { title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, @@ -237,11 +277,14 @@ export class CurrentUserUtils { doc.recentlyClosed = Docs.Create.TreeDocument([], { title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, }); + const clearAll = ScriptField.MakeScript(`self.data = new List([])`); + (doc.recentlyClosed as Doc).contextMenuScripts = new List([clearAll!]); + (doc.recentlyClosed as Doc).contextMenuLabels = new List(["Clear All"]); return Docs.Create.ButtonDocument({ _width: 50, _height: 25, title: "Library", fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, Docs.Prototypes.MainLinkDocument(), doc, doc.recentlyClosed as Doc], { + sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc, doc], { title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true }), targetContainer: sidebarContainer, @@ -279,54 +322,28 @@ export class CurrentUserUtils { })); } - /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window - static setupExpandingButtons(doc: Doc) { - const queryTemplate = Docs.Create.MulticolumnDocument( - [ - Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }), - Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true }) - ], - { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); - queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); - const slideTemplate = Docs.Create.MultirowDocument( - [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true }) - ], - { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); - slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" }); - Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description"); - descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); + static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { + ...opts, + _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true + })) as any as Doc; - const ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, - dropAction: "alias", removeDropProperties: new List(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100 - })) as any as Doc; - const blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { - ...opts, - _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true - })) as any as Doc; + static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ + ...opts, + dropAction: "alias", removeDropProperties: new List(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100 + })) as any as Doc; - doc.penBtn = ficon({ + /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window + static setupExpandingButtons(doc: Doc) { + doc.penBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"), - title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen, this)`), activePen: doc + author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen, this)`), activePen: doc }) - doc.undoBtn = ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); - doc.redoBtn = ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); - doc.slidesBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "sticky-note" }); - doc.descriptionBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, removeDropProperties: new List(["dropAction"]), title: "description view", icon: "sticky-note" }); - doc.queryBtn = ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, removeDropProperties: new List(["dropAction"]), title: "query view", icon: "sticky-note" }); - doc.templateButtons = blist({ title: "template buttons", ignoreClick: true }, [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc]); - doc.expandingButtons = blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc, doc.templateButtons as Doc]); - doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], { - title: "template layouts", _xPadding: 0, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) - })); + doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); + doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); + doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]); } - // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" })); @@ -356,14 +373,15 @@ export class CurrentUserUtils { static updateUserDocument(doc: Doc) { doc.title = Doc.CurrentUserEmail; new InkingControl(); - (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc); - (doc.noteTypes === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc); (doc.childClickFuncs === undefined) && CurrentUserUtils.setupChildClicks(doc); + (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc); + (doc.templateDocs === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc); (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc); (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc); (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc); + doc.linkDb = Docs.Prototypes.MainLinkDocument(); // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); @@ -375,9 +393,9 @@ export class CurrentUserUtils { PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => { stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => { - sidebarButtons && sidebarButtons.map((sidebarBtn, i) => { - sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { - btn && btn.sourcePanel && btn.targetContainer && i === 1 && (btn.onClick as ScriptField).script.run({ this: btn }); + sidebarButtons?.map((sidebarBtn, i) => { + sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { // choose which item to display first in sidebar panel (i === 0,1, or 2) + btn?.sourcePanel && btn.targetContainer && i === 2 && (btn.onClick as ScriptField).script.run({ this: btn }); }); }); }); @@ -427,3 +445,4 @@ export class CurrentUserUtils { Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); +Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 34ee553af7b2133632b66f966c07fd1bb1085e81 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 19 Apr 2020 00:30:23 -0400 Subject: changed tree view to show context menu icon to avoid right-clicking --- src/Utils.ts | 29 +++++++++++++++ src/client/views/DocumentDecorations.tsx | 33 ++--------------- .../views/collections/CollectionTreeView.scss | 31 +++++++++------- .../views/collections/CollectionTreeView.tsx | 42 ++++++++++++---------- src/client/views/nodes/DocumentView.tsx | 6 ++-- 5 files changed, 76 insertions(+), 65 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index 58f272ba5..9acdc8731 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -470,6 +470,35 @@ export function clearStyleSheetRules(sheet: any) { return false; } +export function simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number) { + ["pointerdown", "pointerup"].map(event => element.dispatchEvent( + new PointerEvent(event, { + view: window, + bubbles: true, + cancelable: true, + button: 2, + pointerType: "mouse", + clientX: x, + clientY: y, + screenX: sx, + screenY: sy, + }))); + + element.dispatchEvent( + new MouseEvent("contextmenu", { + view: window, + bubbles: true, + cancelable: true, + button: 2, + clientX: x, + clientY: y, + movementX: 0, + movementY: 0, + screenX: sx, + screenY: sy, + })); +} + export function setupMoveUpEvents( target: object, e: React.PointerEvent, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c49fe157c..9ba418d74 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -8,7 +8,7 @@ import { PositionDocument } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils"; +import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; import { DocUtils } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from "../util/DragManager"; @@ -142,40 +142,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onSettingsDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, () => false, (e) => { }, this.onSettingsClick); } - - simulateMouseClick(element: Element, x: number, y: number, sx: number, sy: number) { - ["pointerdown", "pointerup"].map(event => element.dispatchEvent( - new PointerEvent(event, { - view: window, - bubbles: true, - cancelable: true, - button: 2, - pointerType: "mouse", - clientX: x, - clientY: y, - screenX: sx, - screenY: sy, - }))); - - element.dispatchEvent( - new MouseEvent("contextmenu", { - view: window, - bubbles: true, - cancelable: true, - button: 2, - clientX: x, - clientY: y, - movementX: 0, - movementY: 0, - screenX: sx, - screenY: sy, - })); - } @action onSettingsClick = (e: PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey) { let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0]; while (child.children.length && child.className !== "jsx-parser") child = child.children[0]; - this.simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); } } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 8e95f7fbe..1e59c493f 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -100,10 +100,29 @@ border-left: dashed 1px #00000042; } +.treeViewItem-header { + border: transparent 1px solid; + display: flex; + + .editableView-container-editing-oneLine { + min-width: 15px; + } + .documentView-node-topmost { + width: unset; + } + > svg { + display: none; + } + +} + .treeViewItem-header:hover { .collectionTreeView-keyHeader { display: inherit; } + > svg { + display: inherit; + } .treeViewItem-openRight { display: inline-block; @@ -119,18 +138,6 @@ } } -.treeViewItem-header { - border: transparent 1px solid; - display: flex; - - .editableView-container-editing-oneLine { - min-width: 15px; - } - .documentView-node-topmost { - width: unset; - } -} - .treeViewItem-header-above { border-top: black 1px solid; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2df1614de..c2838a15e 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -10,7 +10,7 @@ import { Document, listSpec, createSchema, makeInterface } from '../../../new_fi import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; -import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue } from '../../../Utils'; +import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; @@ -182,16 +182,12 @@ class TreeView extends React.Component { SetValue={undoBatch((value: string) => { Doc.SetInPlace(this.props.document, key, value, false) || true; Doc.SetInPlace(this.props.document, "editTitle", undefined, false); - //this.props.document.editTitle = undefined; })} OnFillDown={undoBatch((value: string) => { Doc.SetInPlace(this.props.document, key, value, false); const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); - //EditableView.loadId = doc[Id]; Doc.SetInPlace(this.props.document, "editTitle", undefined, false); - // this.props.document.editTitle = undefined; Doc.SetInPlace(this.props.document, "editTitle", true, false); - //doc.editTitle = true; return this.props.addDocument(doc); })} onClick={() => { @@ -399,11 +395,16 @@ class TreeView extends React.Component {
    ; } + showContextMenu = (e: React.MouseEvent) => { + simulateMouseClick(this._docRef.current!.ContentDiv!, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + e.stopPropagation(); + } focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true); contextMenuItems = () => { const focusScript = ScriptField.MakeFunction(`DocFocus(self)`); return [{ script: focusScript!, label: "Focus" }]; } + _docRef = React.createRef(); /** * Renders the EditableView title element for placement into the tree. */ @@ -413,18 +414,21 @@ class TreeView extends React.Component { const editTitle = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)"); const headerElements = ( - { - if (this.treeViewOpen) { - this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" : - this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" : - this.treeViewExpandedView === "layout" && this.props.document.links ? "links" : - this.childDocs ? this.fieldKey : "fields"; - } - this.treeViewOpen = true; - })}> - {this.treeViewExpandedView} - ); + <> + this.showContextMenu(e)}> + { + if (this.treeViewOpen) { + this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" : + this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" : + this.treeViewExpandedView === "layout" && this.props.document.links ? "links" : + this.childDocs ? this.fieldKey : "fields"; + } + this.treeViewOpen = true; + })}> + {this.treeViewExpandedView} + + ); const openRight = (
    ); @@ -440,6 +444,7 @@ class TreeView extends React.Component { {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? this.editableView("title") : { } render() { + const sorting = this.props.document[`${this.fieldKey}-sortAscending`]; setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); return
  • @@ -481,7 +487,7 @@ class TreeView extends React.Component { {this.renderBullet} {this.renderTitle}
  • -
    +
    {!this.treeViewOpen || this.props.renderedIds.indexOf(this.props.document[Id]) !== -1 ? (null) : this.renderContent}
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 12f5b27e1..d114698ea 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -823,11 +823,9 @@ export class DocumentView extends DocComponent(Docu icon: "brain" }); - cm.addItem({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" }); - - - moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" }); moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); + moreItems.push({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" }); + moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" }); moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); runInAction(() => { -- cgit v1.2.3-70-g09d2 From ea359c9d247a9be59b78ed043be6427ab10cb8bd Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 19 Apr 2020 01:40:54 -0400 Subject: added optional labels for font icons. --- src/client/documents/Documents.ts | 1 + .../views/collections/CollectionTreeView.tsx | 8 +++++- src/client/views/nodes/DocumentView.tsx | 4 ++- src/client/views/nodes/FontIconBox.scss | 12 +++++++++ src/client/views/nodes/FontIconBox.tsx | 1 + .../authentication/models/current_user_utils.ts | 29 +++++++++++----------- 6 files changed, 39 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3073707a6..209a72d52 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -85,6 +85,7 @@ export interface DocumentOptions { layoutKey?: string; type?: string; title?: string; + label?: string; // short form of title for use as an icon label style?: string; page?: number; scale?: number; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c2838a15e..cd1e23bbd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -483,7 +483,13 @@ class TreeView extends React.Component { setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); return
  • -
    +
    { + e.stopPropagation(); + e.preventDefault(); + }} onPointerDown={e => { + e.stopPropagation(); + e.preventDefault(); + }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> {this.renderBullet} {this.renderTitle}
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d114698ea..d8178ea77 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1172,7 +1172,9 @@ export class DocumentView extends DocComponent(Docu
    : this.innards} - {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 ?
    this.toggleBackground(true)}>
    : (null)} + {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ? +
    this.toggleBackground(true)}>
    + : (null)}
    ; { this._showKPQuery ? : undefined; } } diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss index f0fe7a54e..68b00a5be 100644 --- a/src/client/views/nodes/FontIconBox.scss +++ b/src/client/views/nodes/FontIconBox.scss @@ -8,6 +8,18 @@ border-radius: 100%; transform-origin: top left; + .fontIconBox-label { + background: gray; + color:white; + margin-left: -10px; + border-radius: 8px; + width:100%; + position: absolute; + text-align: center; + font-size: 8px; + margin-top:4px; + } + svg { width: 95% !important; height: 95%; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 9329cf210..c6ea6a882 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -57,6 +57,7 @@ export class FontIconBox extends DocComponent( boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined }}> + {!this.rootDoc.label ? (null) :
    {StrCast(this.rootDoc.label).substring(0, 5)}
    } ; } } \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index e7a163a3e..7e5f7257a 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -80,31 +80,32 @@ export class CurrentUserUtils { const emptyPresentation = Docs.Create.PresDocument(new List(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" }); doc.activePen = doc; - const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ - { title: "Drag a collection", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection }, - { title: "Drag a web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' }, - { title: "Drag a cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' }, - { title: "Drag a screenshot", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' }, - { title: "Drag a webcam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, - { title: "Drag a audio recorder", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, - { title: "Drag a clickabl button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, - { title: "Drag a presentation view", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation }, - { title: "Drag a scripting box", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, - { title: "Drag an import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, - { title: "Drag a mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, + const docProtoData: { title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection }, + { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' }, + { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' }, + { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' }, + { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, + { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, + { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, + { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation }, + { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, + { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, + { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, - { title: "Drag a search box", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, - { title: "Drag a document previewer", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, + { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, + { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" }, ]; return docProtoData.filter(d => !alreadyCreatedButtons?.includes(d.title)).map(data => Docs.Create.FontIconDocument({ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, icon: data.icon, title: data.title, + label: data.label, author: "Draggable Items", ignoreClick: data.ignoreClick, dropAction: data.click ? "copy" : undefined, -- cgit v1.2.3-70-g09d2 From bdf4ac9601e54bf8e2a3a8f988c97274d84ae8a4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 19 Apr 2020 13:30:20 -0400 Subject: fixed removing docs from tree view with menu. restructured current_user_utils completely --- src/client/views/MainView.tsx | 11 +- .../views/collections/CollectionTreeView.tsx | 6 +- src/client/views/nodes/AudioBox.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/WebBox.tsx | 4 +- src/client/views/pdf/PDFViewer.tsx | 2 +- .../authentication/models/current_user_utils.ts | 562 +++++++++++++-------- src/server/database.ts | 3 - 8 files changed, 369 insertions(+), 230 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 40cabcf83..0877cc5f6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -61,7 +61,7 @@ export class MainView extends React.Component { @computed private get userDoc() { return Doc.UserDoc(); } @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - @computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; } + @computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; } public isPointerDown = false; @@ -399,15 +399,14 @@ export class MainView extends React.Component { mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1); @computed get flyout() { - const sidebarContent = this.userDoc?.sidebarContainer; + const sidebarContent = this.userDoc?.["tabs-panelContainer"]; if (!(sidebarContent instanceof Doc)) { return (null); } - const sidebarButtonsDoc = Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; return
    -
    +
    diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index cd1e23bbd..c243d8f29 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -139,6 +139,9 @@ class TreeView extends React.Component { @undoBatch @action remove = (document: Document, key: string) => { return Doc.RemoveDocFromList(this.dataDoc, key, document); } + @undoBatch @action removeDoc = (document: Document) => { + return Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), document); + } protected createTreeDropTarget = (ele: HTMLDivElement) => { this._treedropDisposer && this._treedropDisposer(); @@ -455,7 +458,7 @@ class TreeView extends React.Component { onClick={this.props.onChildClick || editTitle} dropAction={this.props.dropAction} moveDocument={this.props.moveDocument} - removeDocument={undefined} + removeDocument={this.removeDoc} ScreenToLocalTransform={this.getTransform} ContentScaling={returnOne} PanelWidth={returnZero} @@ -778,6 +781,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as } render() { + if (!(this.props.Document instanceof Doc)) return (null); const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType; const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 7078cc01c..8f40ea2be 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView"; import { Docs } from "../../documents/Documents"; import { ComputedField } from "../../../new_fields/ScriptField"; import { Networking } from "../../Network"; +import { Upload } from "../../../server/SharedMediaTypes"; // testing testing @@ -146,7 +147,9 @@ export class AudioBox extends ViewBoxBaseComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); - this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + if (!(result instanceof Error)) { + this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + } }; this._recordStart = new Date().getTime(); runInAction(() => this.audioState = "recording"); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5c1326b1a..ca4ec7aff 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -560,14 +560,14 @@ export class DocumentView extends DocComponent(Docu } static findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc().iconViews, Doc, null)?.data); + const iconViews = DocListCast(Cast(Doc.UserDoc()["icon-view-all"], Doc, null)?.data); const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data); const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data); const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === type + "_" + templateName && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); return docLayoutTemplate; } @@ -991,7 +991,7 @@ export class DocumentView extends DocComponent(Docu return typeof fallback === "string" ? fallback : "layout"; } rootSelected = (outsideReaction?: boolean) => { - return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)); + return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); panelWidth = () => this.props.PanelWidth(); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 36907c4a7..66ddf64c9 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -68,7 +68,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const scroll = (e.target as any)?.children?.[0].scrollTop; + const scroll = e.target?.children?.[0].scrollTop; this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll; } async componentDidMount() { @@ -345,7 +345,7 @@ export class WebBox extends ViewBoxAnnotatableComponent} ); } - scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop)) + scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop)); render() { return (
    { // creates annotation documents for current highlights const annotationDoc = this.makeAnnotationDocument(color); - annotationDoc && this.props?.addDocument(annotationDoc); + annotationDoc && this.props.addDocument?.(annotationDoc); return annotationDoc; } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 7e5f7257a..3d68d8425 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -19,6 +19,7 @@ import { RichTextField } from "../../../new_fields/RichTextField"; import { PrefetchProxy } from "../../../new_fields/Proxy"; import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox"; import { MainView } from "../../../client/views/MainView"; +import { DocumentType } from "../../../client/documents/DocumentTypes"; export class CurrentUserUtils { private static curr_id: string; @@ -35,60 +36,151 @@ export class CurrentUserUtils { @observable public static GuestWorkspace: Doc | undefined; @observable public static GuestMobile: Doc | undefined; - static setupDefaultDocTemplates(doc: Doc, buttons?: string[]) { - const taskStatusValues = [{ title: "todo", _backgroundColor: "blue", color: "white" }, - { title: "in progress", _backgroundColor: "yellow", color: "black" }, - { title: "completed", _backgroundColor: "green", color: "white" } - ]; - const noteTemplates = [ - Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }), - Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }), - Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }), - Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }), - Docs.Create.TextDocument("", { - title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false, - layout: FormattedTextBox.LayoutString("Todo"), _height: 100, _showCaption: "caption", caption: RichTextField.DashField("taskStatus") - }) - ]; - doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" }); - Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues); - doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 })) - doc.templateButtons = Docs.Create.MasonryDocument(CurrentUserUtils.setupTemplateButtons(doc), { - title: "template buttons", - _pivotField: "author", _xMargin: 0, - _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - }); - doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([doc.noteTypes as any as Doc, doc.templateButtons as Doc, doc.clickFuncs as Doc], { - title: "template layouts", _xPadding: 0, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) - })); + // sets up the default User Templates - slideView, queryView, descriptionView + static setupUserTemplateButtons(doc: Doc) { + if (doc["template-button-query"] === undefined) { + const queryTemplate = Docs.Create.MulticolumnDocument( + [ + Docs.Create.QueryDocument({ title: "query", _height: 200 }), + Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true }) + ], + { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true } + ); + queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); + doc["template-button-query"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "query view", icon: "question-circle" + }); + } + + if (doc["template-button-slides"] === undefined) { + const slideTemplate = Docs.Create.MultirowDocument( + [ + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200 }), + Docs.Create.TextDocument("", { title: "text", _height: 100 }) + ], + { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true } + ); + slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); + doc["template-button-slides"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "address-card" + }); + } + + if (doc["template-button-description"] === undefined) { + const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" }); + Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description"); + descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); + + doc["template-button-description"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "description view", icon: "window-maximize" + }); + } + + if (doc["template-buttons"] === undefined) { + doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], { + title: "template buttons", _xMargin: 0, _showTitle: "title", + _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + })); + } + return doc["template-buttons"] as Doc; } - static setupDefaultIconTypes(doc: Doc, buttons?: string[]) { - doc.iconView = new PrefetchProxy(Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") })); - Doc.GetProto(doc.iconView as any as Doc).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); - doc.isTemplateDoc = makeTemplate(doc.iconView as any as Doc); - doc.iconImageView = new PrefetchProxy(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") })); - doc.isTemplateDoc = makeTemplate(doc.iconImageView as any as Doc, true, "image_icon"); - doc.iconColView = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") })); - doc.isTemplateDoc = makeTemplate(doc.iconColView as any as Doc, true, "collection_icon"); - doc.iconViews = Docs.Create.TreeDocument([doc.iconView as any as Doc, doc.iconImageView as any as Doc, doc.iconColView as any as Doc], { title: "icon types", _height: 75 }); + + // setup the different note type skins + static setupNoteTemplates(doc: Doc) { + if (doc.noteTypes === undefined) { + const taskStatusValues = [ + { title: "todo", _backgroundColor: "blue", color: "white" }, + { title: "in progress", _backgroundColor: "yellow", color: "black" }, + { title: "completed", _backgroundColor: "green", color: "white" } + ]; + const noteTemplates = [ + Docs.Create.TextDocument("", { title: "text", style: "Note", isTemplateDoc: true, backgroundColor: "yellow" }), + Docs.Create.TextDocument("", { title: "text", style: "Idea", isTemplateDoc: true, backgroundColor: "pink" }), + Docs.Create.TextDocument("", { title: "text", style: "Topic", isTemplateDoc: true, backgroundColor: "lightBlue" }), + Docs.Create.TextDocument("", { title: "text", style: "Person", isTemplateDoc: true, backgroundColor: "lightGreen" }), + Docs.Create.TextDocument("", { + title: "text", style: "Todo", isTemplateDoc: true, backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption", + layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus") + }) + ]; + doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations" }); + Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues); + doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), + { title: "Note Layouts", _height: 75 })); + } + // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved. + PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast)); + return doc.noteTypes as Doc; } - // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools - static setupCreatorButtons(doc: Doc, alreadyCreatedButtons?: string[]) { - const emptyPresentation = Docs.Create.PresDocument(new List(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); - const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" }); - doc.activePen = doc; - const docProtoData: { title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ - { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection }, - { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", {_width: 300, _height: 300, title: "New Webpage" })' }, + // creates Note templates, and initial "user" templates + static setupDocTemplates(doc: Doc) { + const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc); + const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc); + if (doc.templateDocs === undefined) { + doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], { + title: "template layouts", _xPadding: 0, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) + })); + } + } + + // setup templates for different document types when they are iconified from Document Decorations + static setupDefaultIconTemplates(doc: Doc) { + if (doc["icon-view"] === undefined) { + const iconView = Docs.Create.TextDocument("", { + title: "icon", _width: 150, _height: 30, isTemplateDoc: true, + onClick: ScriptField.MakeScript("deiconifyView(this)") + }); + Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); + iconView.isTemplateDoc = makeTemplate(iconView); + doc["icon-view"] = new PrefetchProxy(iconView); + } + if (doc["icon-view-img"] === undefined) { + const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }); + iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); + doc["icon-view-img"] = new PrefetchProxy(iconImageView); + } + if (doc["icon-view-col"] === undefined) { + const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") }); + iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); + doc["icon-view-col"] = new PrefetchProxy(iconColView); + } + if (doc["icon-view-all"] === undefined) { + doc["icon-view-all"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["icon-view"] as Doc, doc["icon-view-img"] as Doc, doc["icon-view-col"] as Doc], { title: "icon templates", _height: 75 })); + } + return doc["icon-view-all"] as Doc; + } + + static creatorBtnDescriptors(doc: Doc): { + title: string, label: string, icon: string, drag?: string, ignoreClick?: boolean, + click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc + }[] { + if (doc.emptyPresentation === undefined) { + doc.emptyPresentation = Docs.Create.PresDocument(new List(), + { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); + } + if (doc.emptyCollection === undefined) { + doc.emptyCollection = Docs.Create.FreeformDocument([], + { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" }); + } + return [ + { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc }, + { title: "Drag a web page", label: "Web", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("", { title: "New Webpage" })' }, { title: "Drag a cat image", label: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' }, { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' }, { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, - { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation }, + { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, @@ -101,59 +193,45 @@ export class CurrentUserUtils { { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" }, ]; - return docProtoData.filter(d => !alreadyCreatedButtons?.includes(d.title)).map(data => Docs.Create.FontIconDocument({ - _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, - icon: data.icon, - title: data.title, - label: data.label, - author: "Draggable Items", - ignoreClick: data.ignoreClick, - dropAction: data.click ? "copy" : undefined, - onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, - onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, - ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, - activePen: data.activePen, - backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), - dragFactory: data.dragFactory, - })); - } - - static setupTemplateButtons(doc: Doc) { - const queryTemplate = Docs.Create.MulticolumnDocument( - [ - Docs.Create.QueryDocument({ title: "query", _height: 200, forceActive: true }), - Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true, forceActive: true }) - ], - { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); - queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); - const slideTemplate = Docs.Create.MultirowDocument( - [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, forceActive: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, forceActive: true }) - ], - { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, _autoHeight: false, forceActive: true, hideFilterView: true }); - slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" }); - Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description"); - descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); - - doc.slidesBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "address-card" }); - doc.descriptionBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: descriptionTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "description view", icon: "window-maximize" }); - doc.queryBtn = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: queryTemplate, author: "-Template Items", removeDropProperties: new List(["dropAction"]), title: "query view", icon: "question-circle" }); - - return [doc.slidesBtn as Doc, doc.descriptionBtn as Doc, doc.queryBtn as Doc]; } - - static async updateCreatorButtons(doc: Doc) { - const dragset = await Cast(doc.dragCreators, Doc); - if (dragset) { - const dragdocs = await Cast(dragset.data, listSpec(Doc)); - if (dragdocs) { - const dragDocs = await Promise.all(dragdocs); - this.setupCreatorButtons(doc, dragDocs.map(d => StrCast(d.title))).map(nb => Doc.AddDocToList(dragset, "data", nb)); + // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools + static async setupCreatorButtons(doc: Doc) { + let alreadyCreatedButtons: string[] = []; + const dragCreatorSet = await Cast(doc.dragCreators, Doc, null); + if (dragCreatorSet) { + const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc)); + if (dragCreators) { + const dragDocs = await Promise.all(dragCreators); + alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title)); } } + const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)). + map(data => Docs.Create.FontIconDocument({ + _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, + icon: data.icon, + title: data.title, + label: data.label, + ignoreClick: data.ignoreClick, + dropAction: data.click ? "copy" : undefined, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, + activePen: data.activePen, + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), + dragFactory: data.dragFactory, + })); + + if (dragCreatorSet === undefined) { + doc.dragCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { + title: "Draggable Items", _showTitle: "title", _xMargin: 0, + _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + })); + } else { + creatorBtns.forEach(nb => Doc.AddDocToList(doc.dragCreators as Doc, "data", nb)); + } + return doc.dragCreators as Doc; } static setupMobileButtons(doc: Doc, buttons?: string[]) { @@ -231,96 +309,138 @@ export class CurrentUserUtils { }); } - // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) - static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. + // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) + static async setupToolsBtnPanel(doc: Doc, sidebarContainer: Doc) { // setup a masonry view of all he creators - const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc); - doc.dragCreators = Docs.Create.MasonryDocument(creatorBtns, { - title: "drag Creators", - _pivotField: "author", _xMargin: 0, - _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - }); - doc.allCreators = Docs.Create.StackingDocument([doc.dragCreators as any as Doc, doc.templateButtons as any as Doc], { - title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, - _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", - }); + const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); + const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + + if (doc.allCreators === undefined) { + doc.allCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { + title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, + _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", + })); + } // setup a color picker - const color = Docs.Create.ColorDocument({ - title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) - }); + if (doc.colorPicker === undefined) { + const color = Docs.Create.ColorDocument({ + title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) + }); + doc.colorPicker = new PrefetchProxy(color); + } - return Docs.Create.ButtonDocument({ - _width: 35, _height: 25, title: "Tools", fontSize: 10, targetContainer: sidebarContainer, - letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.StackingDocument([doc.allCreators as Doc, color], { - _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true - }), - onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), - }); + if (doc["tabs-button-tools"] === undefined) { + doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({ + _width: 35, _height: 25, title: "Tools", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", + sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.allCreators as Doc, doc.colorPicker as Doc], { + _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true + })) as any as Doc, + targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), + })); + } + (doc["tabs-button-tools"] as Doc).sourcePanel; // prefetch sourcePanel + return doc["tabs-button-tools"] as Doc; } - // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views - static setupLibraryPanel(sidebarContainer: Doc, doc: Doc) { + static setupWorkspaces(doc: Doc) { // setup workspaces library item - doc.workspaces = Docs.Create.TreeDocument([], { - title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, - }); + if (doc.worspaces === undefined) { + doc.workspaces = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, + })); + } const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`); (doc.workspaces as Doc).contextMenuScripts = new List([newWorkspace!]); (doc.workspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); - doc.documents = Docs.Create.TreeDocument([], { - title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, - }); - + return doc.workspaces as Doc; + } + static setupDocumentCollection(doc: Doc) { + if (doc.documents === undefined) { + doc.documents = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, + })); + } + return doc.documents as Doc; + } + static setupRecentlyClosed(doc: Doc) { // setup Recently Closed library item - doc.recentlyClosed = Docs.Create.TreeDocument([], { - title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, - }); + if (doc.recentlyClosed === undefined) { + doc.recentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, + })); + } + // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready + PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast)); const clearAll = ScriptField.MakeScript(`self.data = new List([])`); (doc.recentlyClosed as Doc).contextMenuScripts = new List([clearAll!]); (doc.recentlyClosed as Doc).contextMenuLabels = new List(["Clear All"]); - return Docs.Create.ButtonDocument({ - _width: 50, _height: 25, title: "Library", fontSize: 10, - letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc, doc], { - title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true - }), - targetContainer: sidebarContainer, - onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") - }); + return doc.recentlyClosed as Doc; + } + // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views + static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) { + const workspaces = CurrentUserUtils.setupWorkspaces(doc); + const documents = CurrentUserUtils.setupDocumentCollection(doc); + const recentlyClosed = CurrentUserUtils.setupRecentlyClosed(doc); + + if (doc["tabs-button-library"] === undefined) { + doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({ + _width: 50, _height: 25, title: "Library", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", + sourcePanel: new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { + title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "place", lockedPosition: true, boxShadow: "0 0", dontRegisterChildren: true + })) as any as Doc, + targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") + })); + } + return doc["tabs-button-library"] as Doc; } // setup the Search button which will display the search panel. - static setupSearchPanel(sidebarContainer: Doc) { - return Docs.Create.ButtonDocument({ - _width: 50, _height: 25, title: "Search", fontSize: 10, - letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.QueryDocument({ title: "search stack", }), - targetContainer: sidebarContainer, - lockedPosition: true, - onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel") - }); + static setupSearchBtnPanel(doc: Doc, sidebarContainer: Doc) { + if (doc["tabs-button-search"] === undefined) { + doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({ + _width: 50, _height: 25, title: "Search", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", + sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc, + targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, + lockedPosition: true, + onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel") + })); + } + return doc["tabs-button-search"] as Doc; } - // setup the list of sidebar mode buttons which determine what is displayed in the sidebar - static setupSidebarButtons(doc: Doc) { - const sidebarContainer = new Doc(); - doc.sidebarContainer = new PrefetchProxy(sidebarContainer); - sidebarContainer._chromeStatus = "disabled"; - sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()"); + static setupSidebarContainer(doc: Doc) { + if (doc["tabs-panelContainer"] === undefined) { + const sidebarContainer = new Doc(); + sidebarContainer._chromeStatus = "disabled"; + sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()"); + doc["tabs-panelContainer"] = new PrefetchProxy(sidebarContainer); + } + return doc["tabs-panelContainer"] as Doc; + } - doc.ToolsBtn = new PrefetchProxy(this.setupToolsPanel(sidebarContainer, doc)); - doc.LibraryBtn = new PrefetchProxy(this.setupLibraryPanel(sidebarContainer, doc)); - doc.SearchBtn = new PrefetchProxy(this.setupSearchPanel(sidebarContainer)); + // setup the list of sidebar mode buttons which determine what is displayed in the sidebar + static async setupSidebarButtons(doc: Doc) { + const sidebarContainer = CurrentUserUtils.setupSidebarContainer(doc); + const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer); + const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer); + const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer); // Finally, setup the list of buttons to display in the sidebar - doc.sidebarButtons = new PrefetchProxy(Docs.Create.StackingDocument([doc.SearchBtn as any as Doc, doc.LibraryBtn as any as Doc, doc.ToolsBtn as any as Doc], { - _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode", - title: "sidebar btn row stack", backgroundColor: "dimGray", - })); + if (doc["tabs-buttons"] === undefined) { + doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], { + _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "view-mode", + title: "sidebar btn row stack", backgroundColor: "dimGray", + })); + (toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn }); + } } static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { @@ -328,85 +448,101 @@ export class CurrentUserUtils { _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), backgroundColor: "black", treeViewPreventOpen: true, lockedPosition: true, _chromeStatus: "disabled", linearViewIsExpanded: true - })) as any as Doc; + })) as any as Doc static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ ...opts, dropAction: "alias", removeDropProperties: new List(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100 - })) as any as Doc; + })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupExpandingButtons(doc: Doc) { - doc.penBtn = CurrentUserUtils.ficon({ - onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"), - author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen, this)`), activePen: doc - }) - doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); - doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); - doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]); + if (doc.penBtn === undefined) { + doc.penBtn = CurrentUserUtils.ficon({ + onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"), + author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen, this)`), activePen: doc + }); + } + if (doc.undoBtn === undefined) { + doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); + } + if (doc.redoBtn === undefined) { + doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); + } + if (doc.expandingButtons === undefined) { + doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]); + } } // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { - doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" })); + if (doc.overlays === undefined) { + doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" })); + } } // the initial presentation Doc to use static setupDefaultPresentation(doc: Doc) { - doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" })); - doc.curPresentation = Docs.Create.PresDocument(new List(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); + if (doc.presentationTemplate === undefined) { + doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ + title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" + })); + } + if (doc.curPresentation === undefined) { + doc.curPresentation = Docs.Create.PresDocument(new List(), { + title: "Presentation", _viewType: CollectionViewType.Stacking, + _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" + }); + } } static setupMobileUploads(doc: Doc) { - doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" })); + if (doc.optionalRightCollection === undefined) { + doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" })); + } } - static setupChildClicks(doc: Doc) { - const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )", - { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 }); - const onClick = Docs.Create.ScriptingDocument(undefined, { title: "onClick", "onClick-rawScript": "console.log('click')", isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 }, "onClick"); - const onCheckedClick = Docs.Create.ScriptingDocument(undefined, - { title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 }, "onCheckedClick"); - doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" }); - doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" }); - } - - static updateUserDocument(doc: Doc) { - doc.title = Doc.CurrentUserEmail; - new InkingControl(); - (doc.childClickFuncs === undefined) && CurrentUserUtils.setupChildClicks(doc); - (doc.iconTypes === undefined) && CurrentUserUtils.setupDefaultIconTypes(doc); - (doc.templateDocs === undefined) && CurrentUserUtils.setupDefaultDocTemplates(doc); - (doc.optionalRightCollection === undefined) && CurrentUserUtils.setupMobileUploads(doc); - (doc.overlays === undefined) && CurrentUserUtils.setupOverlays(doc); - (doc.expandingButtons === undefined) && CurrentUserUtils.setupExpandingButtons(doc); - (doc.curPresentation === undefined) && CurrentUserUtils.setupDefaultPresentation(doc); - (doc.sidebarButtons === undefined) && CurrentUserUtils.setupSidebarButtons(doc); - doc.linkDb = Docs.Prototypes.MainLinkDocument(); + static setupClickEditorTemplates(doc: Doc) { + if (doc.childClickFuncs === undefined) { + const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( + "docCast(thisContainer.target).then((target) => { target && docCast(this.source).then((source) => { target.proto.data = new List([source || this]); } ); } )", + { target: Doc.name }), { title: "On Child Clicked (open in target)", _width: 300, _height: 200 }); + doc.childClickFuncs = Docs.Create.TreeDocument([openInTarget], { title: "on Child Click function templates" }); + } // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); - // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready - PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast)); - // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved. - PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast)); + + if (doc.clickFuncs === undefined) { + const onClick = Docs.Create.ScriptingDocument(undefined, { + title: "onClick", "onClick-rawScript": "console.log('click')", + isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200 + }, "onClick"); + const onCheckedClick = Docs.Create.ScriptingDocument(undefined, { + title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", "onCheckedClick-params": new List(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, isTemplateForField: "onCheckedClick", _width: 300, _height: 200 + }, "onCheckedClick"); + doc.clickFuncs = Docs.Create.TreeDocument([onClick, onCheckedClick], { title: "onClick funcs" }); + } PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); - PromiseValue(Cast(doc.childClickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); - PromiseValue(Cast(doc.sidebarButtons, Doc)).then(stackingDoc => { - stackingDoc && PromiseValue(Cast(stackingDoc.data, listSpec(Doc))).then(sidebarButtons => { - sidebarButtons?.map((sidebarBtn, i) => { - sidebarBtn && PromiseValue(Cast(sidebarBtn, Doc)).then(async btn => { // choose which item to display first in sidebar panel (i === 0,1, or 2) - btn?.sourcePanel && btn.targetContainer && i === 2 && (btn.onClick as ScriptField).script.run({ this: btn }); - }); - }); - }); - }); + + return doc.clickFuncs as Doc; + } + + static async updateUserDocument(doc: Doc) { + new InkingControl(); + doc.title = Doc.CurrentUserEmail; + doc.activePen = doc; + CurrentUserUtils.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon + CurrentUserUtils.setupMobileUploads(doc); // sets up the optional right collection of documents uploaded from mobile + CurrentUserUtils.setupOverlays(doc); // documents in overlay layer + CurrentUserUtils.setupExpandingButtons(doc); // the bottom bar of font icons + CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered + await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels + doc.linkDb = Docs.Prototypes.MainLinkDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); - this.updateCreatorButtons(doc); return doc; } diff --git a/src/server/database.ts b/src/server/database.ts index 1da31c5ff..d74bd7321 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -52,9 +52,6 @@ export namespace Database { private currentWrites: { [id: string]: Promise } = {}; private db?: mongodb.Db; private onConnect: (() => void)[] = []; - constructor() { - } - doConnect() { console.error(`\nConnecting to Mongo with URL : ${url}\n`); this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => { -- cgit v1.2.3-70-g09d2 From 26949f548619c93fc13bc0f3cfcec6cf9e72f3d6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 19 Apr 2020 18:50:21 -0400 Subject: final structural cleanp up of UserDocument for a while, hopefully --- src/client/documents/Documents.ts | 7 +- src/client/util/SelectionManager.ts | 6 +- src/client/util/SharingManager.tsx | 2 +- src/client/views/DocumentButtonBar.tsx | 5 +- src/client/views/DocumentDecorations.tsx | 9 +- src/client/views/GlobalKeyHandler.ts | 7 +- src/client/views/InkingControl.tsx | 18 +-- src/client/views/MainView.tsx | 20 +-- src/client/views/OverlayView.tsx | 23 ++- .../views/collections/CollectionDockingView.tsx | 16 +- .../views/collections/CollectionTimeView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 15 +- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/PresBox.tsx | 8 +- src/mobile/ImageUpload.tsx | 2 +- src/mobile/MobileInterface.tsx | 53 +++---- src/new_fields/Doc.ts | 18 ++- .../authentication/models/current_user_utils.ts | 171 ++++++++++----------- 18 files changed, 184 insertions(+), 202 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 209a72d52..d231f42e8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -8,7 +8,6 @@ import { PDFBox } from "../views/nodes/PDFBox"; import { ScriptingBox } from "../views/nodes/ScriptingBox"; import { VideoBox } from "../views/nodes/VideoBox"; import { WebBox } from "../views/nodes/WebBox"; -import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { OmitKeys, JSONUtils, Utils } from "../../Utils"; import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc"; import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; @@ -239,7 +238,7 @@ export namespace Docs { [DocumentType.LINKDB, { data: new List(), layout: { view: EmptyBox, dataField: data }, - options: { childDropAction: "alias", title: "LINK DB" } + options: { childDropAction: "alias", title: "Global Link Database" } }], [DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: data } @@ -960,7 +959,7 @@ export namespace DocUtils { export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; - if (target.doc === CurrentUserUtils.UserDocument) return undefined; + if (target.doc === Doc.UserDoc()) return undefined; const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id); Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1.title +" (" + (self.linkRelationship||"to") +") " + self.anchor2.title'); @@ -990,7 +989,7 @@ export namespace DocUtils { }); ContextMenu.Instance.addItem({ description: "Add Template Doc ...", - subitems: DocListCast(Cast(Doc.UserDoc().expandingButtons, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({ + subitems: DocListCast(Cast(Doc.UserDoc().dockedBtns, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title), event: (args: { x: number, y: number }) => { const newDoc = Doc.ApplyTemplate(dragDoc); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 6c386d684..a49977c42 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -28,21 +28,21 @@ export namespace SelectionManager { manager.SelectedDocuments.clear(); manager.SelectedDocuments.set(docView, true); } - Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); + Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); } @action DeselectDoc(docView: DocumentView): void { if (manager.SelectedDocuments.get(docView)) { manager.SelectedDocuments.delete(docView); docView.props.whenActiveChanged(false); - Doc.UserDoc().SelectedDocs = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); + Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); } } @action DeselectAll(): void { Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments.clear(); - Doc.UserDoc().SelectedDocs = new List([]); + Doc.UserDoc().activeSelection = new List([]); } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 7496ac73c..3ce6de80d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -109,7 +109,7 @@ export default class SharingManager extends React.Component<{}> { if (isCandidate) { const userDocument = await DocServer.GetRefField(user.userDocumentId); if (userDocument instanceof Doc) { - const notificationDoc = await Cast(userDocument.optionalRightCollection, Doc); + const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); runInAction(() => { if (notificationDoc instanceof Doc) { this.users.push({ user, notificationDoc }); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 5b78008ab..93987b751 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -21,7 +21,6 @@ import { Template, Templates } from "./Templates"; import React = require("react"); import { DragManager } from '../util/DragManager'; import { MetadataEntryMenu } from './MetadataEntryMenu'; -import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -202,9 +201,9 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | @computed get pinButton() { const targetDoc = this.view0?.props.Document; - const isPinned = targetDoc && CurrentUserUtils.IsDocPinned(targetDoc); + const isPinned = targetDoc && Doc.isDocPinned(targetDoc); return !targetDoc ? (null) :
    { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9ba418d74..c8766a6b3 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -7,7 +7,6 @@ import { Doc, DataSym } from "../../new_fields/Doc"; import { PositionDocument } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; -import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; import { DocUtils } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; @@ -69,7 +68,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> get Bounds(): { x: number, y: number, b: number, r: number } { return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { if (documentView.props.renderDepth === 0 || - Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) { + Doc.AreProtosEqual(documentView.props.Document, Doc.UserDoc())) { return bounds; } const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse(); @@ -164,7 +163,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dragData.isSelectionMove = true; this.Interacting = true; this._hidden = true; - DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(documentView => documentView.ContentDiv!), dragData, e.x, e.y, { + DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(dv => dv.ContentDiv!), dragData, e.x, e.y, { dragComplete: action(e => this._hidden = this.Interacting = false), hideSource: true }); @@ -178,14 +177,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onCloseClick = async (e: PointerEvent) => { if (e.button === 0) { - const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc; + const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc; const selected = SelectionManager.SelectedDocuments().slice(); SelectionManager.DeselectAll(); this._addedCloseCalls.forEach(handler => handler(selected)); selected.map(dv => { recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true); - dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); + dv.props.removeDocument?.(dv.props.Document); }); } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index d01f3f1e5..185222541 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -7,7 +7,6 @@ import { action, runInAction } from "mobx"; import { Doc } from "../../new_fields/Doc"; import { DictationManager } from "../util/DictationManager"; import SharingManager from "../util/SharingManager"; -import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; import { ScriptField } from "../../new_fields/ScriptField"; import { InkingControl } from "./InkingControl"; @@ -194,7 +193,7 @@ export default class KeyManager { } break; case "t": - PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); if (MainView.Instance.flyoutWidth === 240) { MainView.Instance.flyoutWidth = 0; } else { @@ -202,7 +201,7 @@ export default class KeyManager { } break; case "l": - PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + PromiseValue(Cast(Doc.UserDoc()["tabs-button-library"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); if (MainView.Instance.flyoutWidth === 250) { MainView.Instance.flyoutWidth = 0; } else { @@ -210,7 +209,7 @@ export default class KeyManager { } break; case "f": - PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); + PromiseValue(Cast(Doc.UserDoc()["tabs-button-search"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); if (MainView.Instance.flyoutWidth === 400) { MainView.Instance.flyoutWidth = 0; } else { diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 645c7fa54..172c1864a 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -12,9 +12,9 @@ import { FormattedTextBox } from "./nodes/FormattedTextBox"; export class InkingControl { @observable static Instance: InkingControl; - @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; } - @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } - @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } + @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; } + @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(Doc.UserDoc().inkColor)) ?? "rgb(244, 67, 54)"; } + @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "5"; } @observable public _open: boolean = false; constructor() { @@ -23,7 +23,7 @@ export class InkingControl { switchTool = action((tool: InkTool): void => { // this._selectedTool = tool; - CurrentUserUtils.UserDocument.inkTool = tool; + Doc.UserDoc().inkTool = tool; }); decimalToHexString(number: number) { if (number < 0) { @@ -34,7 +34,7 @@ export class InkingControl { @undoBatch switchColor = action((color: ColorState): void => { - CurrentUserUtils.UserDocument.inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); + Doc.UserDoc().inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { const selected = SelectionManager.SelectedDocuments(); @@ -44,9 +44,9 @@ export class InkingControl { view.props.Document.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document); if (targetDoc) { if (StrCast(Doc.Layout(view.props.Document).layout).indexOf("FormattedTextBox") !== -1 && FormattedTextBox.HadSelection) { - Doc.Layout(view.props.Document).color = CurrentUserUtils.UserDocument.inkColor; + Doc.Layout(view.props.Document).color = Doc.UserDoc().inkColor; } else { - Doc.Layout(view.props.Document)._backgroundColor = CurrentUserUtils.UserDocument.inkColor; // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment + Doc.Layout(view.props.Document)._backgroundColor = Doc.UserDoc().inkColor; // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment } } }); @@ -57,7 +57,7 @@ export class InkingControl { @action switchWidth = (width: string): void => { // this._selectedWidth = width; - CurrentUserUtils.UserDocument.inkWidth = width; + Doc.UserDoc().inkWidth = width; } @computed @@ -73,7 +73,7 @@ export class InkingControl { @action updateSelectedColor(value: string) { // this._selectedColor = value; - CurrentUserUtils.UserDocument.inkColor = value; + Doc.UserDoc().inkColor = value; } @computed diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0877cc5f6..f8e246315 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -204,7 +204,7 @@ export class MainView extends React.Component { @action createNewWorkspace = async (id?: string) => { - const workspaces = Cast(this.userDoc.workspaces, Doc) as Doc; + const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; const workspaceCount = DocListCast(workspaces.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, @@ -214,8 +214,8 @@ export class MainView extends React.Component { title: "Collection " + workspaceCount, }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc); - const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().documents as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + Doc.AddDocToList(Doc.GetProto(Doc.UserDoc().myDocuments as Doc), "data", freeformDoc); + const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myDocuments as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); mainDoc.contextMenuScripts = new List([toggleTheme!]); @@ -261,7 +261,7 @@ export class MainView extends React.Component { } // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) setTimeout(async () => { - const col = this.userDoc && await Cast(this.userDoc.optionalRightCollection, Doc); + const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc); col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col); }, 100); return true; @@ -504,8 +504,8 @@ export class MainView extends React.Component { return !this._flyoutTranslate ? (
    ) : (null); } - addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); - remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); + addButtonDoc = (doc: Doc) => Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc); + remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc); moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc); buttonBarXf = () => { @@ -514,13 +514,13 @@ export class MainView extends React.Component { return new Transform(-translateX, -translateY, 1 / scale); } @computed get docButtons() { - const expandingBtns = Doc.UserDoc()?.expandingButtons; - if (expandingBtns instanceof Doc) { + const dockedBtns = Doc.UserDoc()?.dockedBtns; + if (dockedBtns instanceof Doc) { return
    + style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} > void; @@ -140,10 +138,11 @@ export class OverlayView extends React.Component { } @computed get overlayDocs() { - if (!CurrentUserUtils.UserDocument) { + const userDocOverlays = Doc.UserDoc().myOverlayDocuments; + if (!userDocOverlays) { return (null); } - return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => { + return userDocOverlays instanceof Doc && DocListCast(userDocOverlays.data).map(d => { setTimeout(() => d.inOverlay = true, 0); let offsetx = 0, offsety = 0; const onPointerMove = action((e: PointerEvent) => { @@ -195,7 +194,7 @@ export class OverlayView extends React.Component { addDocTab={returnFalse} pinToPres={emptyFunction} ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined}/> + ContainingCollectionDoc={undefined} />
    ; }); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c74f5555b..5e77bc0bb 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -14,11 +14,9 @@ import { List } from '../../../new_fields/List'; import { FieldId } from "../../../new_fields/RefField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from '../../../new_fields/util'; -import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; -import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { Scripting } from '../../util/Scripting'; @@ -376,8 +374,7 @@ export class CollectionDockingView extends React.Component this.setupGoldenLayout(), 1); - const userDoc = CurrentUserUtils.UserDocument; - userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false); + DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false); this.props.Document.workspaceBrush = true; } this._ignoreStateChange = ""; @@ -544,9 +541,8 @@ export class CollectionDockingView extends React.Component { @action public static PinDoc(doc: Doc) { //add this new doc to props.Document - const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; + const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; if (curPres) { const pinDoc = Doc.MakeAlias(doc); pinDoc.presentationTargetDoc = doc; @@ -698,7 +694,7 @@ export class DockedFrameRenderer extends React.Component { @action public static UnpinDoc(doc: Doc) { //add this new doc to props.Document - const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; + const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; if (curPres) { const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)); ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 1d1949c99..742a818bc 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -91,7 +91,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { public static SyncTimelineToPresentation(doc: Doc) { const fieldKey = Doc.LayoutFieldKey(doc); - doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(curPresentationItem()[this._pivotField || 'year'] || 0)"); + doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(activePresentationItem()[this._pivotField || 'year'] || 0)"); } specificMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c243d8f29..83e536167 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -6,10 +6,9 @@ import { observer } from "mobx-react"; import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; -import { Document, listSpec, createSchema, makeInterface } from '../../../new_fields/Schema'; +import { Document, listSpec } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; -import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; @@ -195,7 +194,7 @@ class TreeView extends React.Component { })} onClick={() => { SelectionManager.DeselectAll(); - Doc.UserDoc().SelectedDocs = new List([this.props.document]); + Doc.UserDoc().activeSelection = new List([this.props.document]); return false; }} OnTab={undoBatch((shift?: boolean) => { @@ -691,14 +690,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout - if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) { + if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myWorkspaces) { ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - } else if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.recentlyClosed) { - ContextMenu.Instance.addItem({ description: "Clear All", event: () => CurrentUserUtils.UserDocument.recentlyClosed = new List(), icon: "plus" }); + } else if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myRecentlyClosed) { + ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List(), icon: "plus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); @@ -739,14 +738,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as heroView._showTitle = "title"; heroView._showTitleHover = "titlehover"; - Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data", + Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", Docs.Create.FontIconDocument({ title: "hero view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", dragFactory: heroView, removeDropProperties: new List(["dropAction"]), icon: "portrait", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), })); - Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data", + Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", Docs.Create.FontIconDocument({ title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", dragFactory: detailView, removeDropProperties: new List(["dropAction"]), icon: "file-alt", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ca4ec7aff..196104fe0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -305,7 +305,7 @@ export class DocumentView extends DocComponent(Docu self: this.rootDoc, thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log); - if (this.props.Document !== Doc.UserDoc().undoBtn && this.props.Document !== Doc.UserDoc().redoBtn) { + if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) { UndoManager.RunInBatch(func, "on click"); } else func(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself @@ -560,7 +560,7 @@ export class DocumentView extends DocComponent(Docu } static findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc()["icon-view-all"], Doc, null)?.data); + const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data); const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data); const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index e428e16da..80d043db1 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -49,7 +49,7 @@ export class PresBox extends ViewBoxBaseComponent @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } @computed get currentIndex() { return NumCast(this.layoutDoc._itemIndex); } - updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.rootDoc); + updateCurrentPresentation = action(() => Doc.UserDoc().activePresentation = this.rootDoc); next = () => { this.updateCurrentPresentation(); @@ -253,14 +253,14 @@ export class PresBox extends ViewBoxBaseComponent updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: CollectionViewType) => { if (BoolCast(this.layoutDoc.inOverlay) !== (mode === CollectionViewType.Invalid)) { if (this.layoutDoc.inOverlay) { - Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc); + Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); CollectionDockingView.AddRightSplit(this.rootDoc); this.layoutDoc.inOverlay = false; } else { this.layoutDoc.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25; this.layoutDoc.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25; this.props.addDocTab?.(this.rootDoc, "close"); - Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.rootDoc); + Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); } } })); @@ -292,7 +292,7 @@ export class PresBox extends ViewBoxBaseComponent this.updateMinimize(e, StrCast(this.layoutDoc._viewType)); }); - childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined; + childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined; render() { const mode = StrCast(this.layoutDoc._viewType) as CollectionViewType; this.initializeViewAliases(this.childDocs, mode); diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 5903a2ce9..295e82142 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -67,7 +67,7 @@ class Uploader extends React.Component { const field = await DocServer.GetRefField(res); let pending: Opt; if (field instanceof Doc) { - pending = await Cast(field.optionalRightCollection, Doc); + pending = await Cast(field.rightSidebarCollection, Doc); } if (pending) { this.status = "has pending docs"; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index c87ac719c..73ebbb303 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,51 +1,38 @@ import React = require('react'); +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEraser, faHighlighter, faLongArrowAltLeft, faMousePointer, faPenNib } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { computed, action, observable } from 'mobx'; -import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; -import { FieldValue, Cast, StrCast } from '../new_fields/Types'; -import { Doc, DocListCast } from '../new_fields/Doc'; +import { DocServer } from '../client/DocServer'; import { Docs } from '../client/documents/Documents'; -import { CollectionView } from '../client/views/collections/CollectionView'; -import { DocumentView } from '../client/views/nodes/DocumentView'; -import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, returnZero } from '../Utils'; -import { Transform } from '../client/util/Transform'; -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faPenNib, faHighlighter, faEraser, faMousePointer, faBreadSlice, faTrash, faCheck, faLongArrowAltLeft } from '@fortawesome/free-solid-svg-icons'; +import { DocumentManager } from '../client/util/DocumentManager'; +import RichTextMenu from '../client/util/RichTextMenu'; import { Scripting } from '../client/util/Scripting'; -import { CollectionFreeFormView } from '../client/views/collections/collectionFreeForm/CollectionFreeFormView'; +import { Transform } from '../client/util/Transform'; +import { CollectionView } from '../client/views/collections/CollectionView'; +import { DocumentDecorations } from '../client/views/DocumentDecorations'; import GestureOverlay from '../client/views/GestureOverlay'; import { InkingControl } from '../client/views/InkingControl'; -import { InkTool } from '../new_fields/InkField'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import "./MobileInterface.scss"; -import { SelectionManager } from '../client/util/SelectionManager'; -import { DateField } from '../new_fields/DateField'; -import { GestureUtils } from '../pen-gestures/GestureUtils'; -import { DocServer } from '../client/DocServer'; -import { DocumentDecorations } from '../client/views/DocumentDecorations'; -import { OverlayView } from '../client/views/OverlayView'; -import { DictationOverlay } from '../client/views/DictationOverlay'; -import SharingManager from '../client/util/SharingManager'; -import { PreviewCursor } from '../client/views/PreviewCursor'; -import { ContextMenu } from '../client/views/ContextMenu'; +import { DocumentView } from '../client/views/nodes/DocumentView'; import { RadialMenu } from '../client/views/nodes/RadialMenu'; -import PDFMenu from '../client/views/pdf/PDFMenu'; -import MarqueeOptionsMenu from '../client/views/collections/collectionFreeForm/MarqueeOptionsMenu'; -import GoogleAuthenticationManager from '../client/apis/GoogleAuthenticationManager'; -import { listSpec } from '../new_fields/Schema'; +import { PreviewCursor } from '../client/views/PreviewCursor'; +import { Doc, DocListCast, FieldResult } from '../new_fields/Doc'; import { Id } from '../new_fields/FieldSymbols'; -import { DocumentManager } from '../client/util/DocumentManager'; -import RichTextMenu from '../client/util/RichTextMenu'; +import { InkTool } from '../new_fields/InkField'; +import { listSpec } from '../new_fields/Schema'; +import { Cast, FieldValue } from '../new_fields/Types'; import { WebField } from "../new_fields/URLField"; -import { FieldResult } from "../new_fields/Doc"; -import { List } from '../new_fields/List'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils'; +import "./MobileInterface.scss"; library.add(faLongArrowAltLeft); @observer export default class MobileInterface extends React.Component { @observable static Instance: MobileInterface; - @computed private get userDoc() { return CurrentUserUtils.UserDocument; } + @computed private get userDoc() { return Doc.UserDoc(); } @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } // @observable private currentView: "main" | "ink" | "upload" = "main"; private mainDoc: any = CurrentUserUtils.setupMobileDoc(this.userDoc); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index bcf0d1aec..8e8f0928c 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -865,6 +865,16 @@ export namespace Doc { return id; } + export function isDocPinned(doc: Doc) { + //add this new doc to props.Document + const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + if (curPres) { + return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; + } + return false; + } + + export async function addFieldEnumerations(doc: Opt, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) { let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey); if (!(optionsCollection instanceof Doc)) { @@ -906,13 +916,13 @@ Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; }); Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); }); Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); }); -Scripting.addGlobal(function curPresentationItem() { - const curPres = Doc.UserDoc().curPresentation as Doc; +Scripting.addGlobal(function activePresentationItem() { + const curPres = Doc.UserDoc().activePresentation as Doc; return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; }); -Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().SelectedDocs = new List([doc]); }); +Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); }); Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = DocListCast(Doc.UserDoc().SelectedDocs). + const docs = DocListCast(Doc.UserDoc().activeSelection). filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 3d68d8425..a2f016f94 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -30,7 +30,7 @@ export class CurrentUserUtils { public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } @computed public static get UserDocument() { return Doc.UserDoc(); } - @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).pen as Doc; } + @computed public static get ActivePen() { return Doc.UserDoc().activePen instanceof Doc && (Doc.UserDoc().activePen as Doc).inkPen as Doc; } @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; @@ -84,10 +84,12 @@ export class CurrentUserUtils { if (doc["template-buttons"] === undefined) { doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], { - title: "template buttons", _xMargin: 0, _showTitle: "title", + title: "Template Item Creators", _xMargin: 0, _showTitle: "title", _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); + } else { + DocListCast(Cast(doc["template-buttons"], Doc, null)?.data); // prefetch templates } return doc["template-buttons"] as Doc; } @@ -114,9 +116,10 @@ export class CurrentUserUtils { Doc.addFieldEnumerations(Doc.GetProto(noteTemplates[4]), "taskStatus", taskStatusValues); doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt, true, StrCast(nt.style)) ? nt : nt), { title: "Note Layouts", _height: 75 })); + } else { + DocListCast(Cast(doc.noteTypes, Doc, null)?.data); // prefetch templates } - // this is equivalent to using PrefetchProxies to make sure all the sidebarButtons and noteType internal Doc's have been retrieved. - PromiseValue(Cast(doc.noteTypes, Doc)).then(noteTypes => noteTypes && PromiseValue(noteTypes.data).then(DocListCast)); + return doc.noteTypes as Doc; } @@ -135,29 +138,31 @@ export class CurrentUserUtils { // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc) { - if (doc["icon-view"] === undefined) { + if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(this)") }); Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); iconView.isTemplateDoc = makeTemplate(iconView); - doc["icon-view"] = new PrefetchProxy(iconView); + doc["template-icon-view"] = new PrefetchProxy(iconView); } - if (doc["icon-view-img"] === undefined) { + if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); - doc["icon-view-img"] = new PrefetchProxy(iconImageView); + doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } - if (doc["icon-view-col"] === undefined) { + if (doc["template-icon-view-col"] === undefined) { const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onClick: ScriptField.MakeScript("deiconifyView(self)") }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); - doc["icon-view-col"] = new PrefetchProxy(iconColView); + doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } - if (doc["icon-view-all"] === undefined) { - doc["icon-view-all"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["icon-view"] as Doc, doc["icon-view-img"] as Doc, doc["icon-view-col"] as Doc], { title: "icon templates", _height: 75 })); + if (doc["template-icons"] === undefined) { + doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-col"] as Doc], { title: "icon templates", _height: 75 })); + } else { + DocListCast(Cast(doc["template-icons"], Doc, null)?.data); // prefetch templates } - return doc["icon-view-all"] as Doc; + return doc["template-icons"] as Doc; } static creatorBtnDescriptors(doc: Doc): { @@ -180,15 +185,15 @@ export class CurrentUserUtils { { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, - { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc }, + { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, - // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, - // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, + // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc }, + // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc }, { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" }, @@ -198,7 +203,7 @@ export class CurrentUserUtils { // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static async setupCreatorButtons(doc: Doc) { let alreadyCreatedButtons: string[] = []; - const dragCreatorSet = await Cast(doc.dragCreators, Doc, null); + const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null); if (dragCreatorSet) { const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc)); if (dragCreators) { @@ -223,25 +228,25 @@ export class CurrentUserUtils { })); if (dragCreatorSet === undefined) { - doc.dragCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { - title: "Draggable Items", _showTitle: "title", _xMargin: 0, + doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { + title: "Standard Item Creators", _showTitle: "title", _xMargin: 0, _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); } else { - creatorBtns.forEach(nb => Doc.AddDocToList(doc.dragCreators as Doc, "data", nb)); + creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb)); } - return doc.dragCreators as Doc; + return doc.myItemCreators as Doc; } static setupMobileButtons(doc: Doc, buttons?: string[]) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" }, - { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, - { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, - // { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "red", activePen: doc }, + { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc }, + { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc }, + // { title: "draw", icon: "pen-nib", click: 'switchMobileView(setupMobileInkingDoc, renderMobileInking, onSwitchMobileInking);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "red", activePen: doc }, { title: "upload", icon: "upload", click: 'switchMobileView(setupMobileUploadDoc, renderMobileUpload, onSwitchMobileUpload);', backgroundColor: "orange" }, // { title: "upload", icon: "upload", click: 'uploadImageMobile();', backgroundColor: "cyan" }, ]; @@ -255,11 +260,11 @@ export class CurrentUserUtils { static setupThumbButtons(doc: Doc) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ - { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, + { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon, @@ -316,25 +321,25 @@ export class CurrentUserUtils { const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); - if (doc.allCreators === undefined) { - doc.allCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { + if (doc.myCreators === undefined) { + doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _width: 500, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", })); } // setup a color picker - if (doc.colorPicker === undefined) { + if (doc.myColorPicker === undefined) { const color = Docs.Create.ColorDocument({ title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) }); - doc.colorPicker = new PrefetchProxy(color); + doc.myColorPicker = new PrefetchProxy(color); } if (doc["tabs-button-tools"] === undefined) { doc["tabs-button-tools"] = new PrefetchProxy(Docs.Create.ButtonDocument({ _width: 35, _height: 25, title: "Tools", fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.allCreators as Doc, doc.colorPicker as Doc], { + sourcePanel: new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack", forceActive: true })) as any as Doc, targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc, @@ -347,39 +352,39 @@ export class CurrentUserUtils { static setupWorkspaces(doc: Doc) { // setup workspaces library item - if (doc.worspaces === undefined) { - doc.workspaces = new PrefetchProxy(Docs.Create.TreeDocument([], { + if (doc.myWorkspaces === undefined) { + doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, })); } const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`); - (doc.workspaces as Doc).contextMenuScripts = new List([newWorkspace!]); - (doc.workspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); + (doc.myWorkspaces as Doc).contextMenuScripts = new List([newWorkspace!]); + (doc.myWorkspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); - return doc.workspaces as Doc; + return doc.myWorkspaces as Doc; } static setupDocumentCollection(doc: Doc) { - if (doc.documents === undefined) { - doc.documents = new PrefetchProxy(Docs.Create.TreeDocument([], { + if (doc.myDocuments === undefined) { + doc.myDocuments = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, })); } - return doc.documents as Doc; + return doc.myDocuments as Doc; } static setupRecentlyClosed(doc: Doc) { // setup Recently Closed library item - if (doc.recentlyClosed === undefined) { - doc.recentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { + if (doc.myRecentlyClosed === undefined) { + doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, })); } // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready - PromiseValue(Cast(doc.recentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast)); + PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast)); const clearAll = ScriptField.MakeScript(`self.data = new List([])`); - (doc.recentlyClosed as Doc).contextMenuScripts = new List([clearAll!]); - (doc.recentlyClosed as Doc).contextMenuLabels = new List(["Clear All"]); + (doc.myRecentlyClosed as Doc).contextMenuScripts = new List([clearAll!]); + (doc.myRecentlyClosed as Doc).contextMenuLabels = new List(["Clear All"]); - return doc.recentlyClosed as Doc; + return doc.myRecentlyClosed as Doc; } // setup the Library button which will display the library panel. This panel includes a collection of workspaces, documents, and recently closed views static setupLibraryPanel(doc: Doc, sidebarContainer: Doc) { @@ -456,48 +461,48 @@ export class CurrentUserUtils { })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window - static setupExpandingButtons(doc: Doc) { - if (doc.penBtn === undefined) { - doc.penBtn = CurrentUserUtils.ficon({ - onClick: ScriptField.MakeScript("activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)"), - author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.pen, this)`), activePen: doc + static setupDockedButtons(doc: Doc) { + if (doc["dockedBtn-pen"] === undefined) { + doc["dockedBtn-pen"] = CurrentUserUtils.ficon({ + onClick: ScriptField.MakeScript("activatePen(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this,2, this.backgroundColor)"), + author: "systemTemplates", title: "ink mode", icon: "pen-nib", ischecked: ComputedField.MakeFunction(`sameDocs(this.activePen.inkPen, this)`), activePen: doc }); } - if (doc.undoBtn === undefined) { - doc.undoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); + if (doc["dockedBtn-undo"] === undefined) { + doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), title: "undo button", icon: "undo-alt" }); } - if (doc.redoBtn === undefined) { - doc.redoBtn = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); + if (doc["dockedBtn-redo"] === undefined) { + doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), title: "redo button", icon: "redo-alt" }); } - if (doc.expandingButtons === undefined) { - doc.expandingButtons = CurrentUserUtils.blist({ title: "expanding buttons", ignoreClick: true }, [doc.undoBtn as Doc, doc.redoBtn as Doc, doc.penBtn as Doc]); + if (doc.dockedBtns === undefined) { + doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc, doc["dockedBtn-pen"] as Doc]); } } // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { - if (doc.overlays === undefined) { - doc.overlays = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" })); + if (doc.myOverlayDocuments === undefined) { + doc.myOverlayDocuments = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6" })); } } // the initial presentation Doc to use static setupDefaultPresentation(doc: Doc) { - if (doc.presentationTemplate === undefined) { - doc.presentationTemplate = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ + if (doc["template-presentation"] === undefined) { + doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" })); } - if (doc.curPresentation === undefined) { - doc.curPresentation = Docs.Create.PresDocument(new List(), { + if (doc.activePresentation === undefined) { + doc.activePresentation = Docs.Create.PresDocument(new List(), { title: "Presentation", _viewType: CollectionViewType.Stacking, _LODdisable: true, _chromeStatus: "replaced", _showTitle: "title", boxShadow: "0 0" }); } } - static setupMobileUploads(doc: Doc) { - if (doc.optionalRightCollection === undefined) { - doc.optionalRightCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "New mobile uploads" })); + static setupRightSidebar(doc: Doc) { + if (doc.rightSidebarCollection === undefined) { + doc.rightSidebarCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Right Sidebar" })); } } @@ -532,29 +537,19 @@ export class CurrentUserUtils { doc.title = Doc.CurrentUserEmail; doc.activePen = doc; CurrentUserUtils.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon - CurrentUserUtils.setupMobileUploads(doc); // sets up the optional right collection of documents uploaded from mobile + CurrentUserUtils.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing CurrentUserUtils.setupOverlays(doc); // documents in overlay layer - CurrentUserUtils.setupExpandingButtons(doc); // the bottom bar of font icons + CurrentUserUtils.setupDockedButtons(doc); // the bottom bar of font icons CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels - doc.linkDb = Docs.Prototypes.MainLinkDocument(); + doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet - doc.undoBtn && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc.undoBtn as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); - doc.redoBtn && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc.redoBtn as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); + doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); + doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); return doc; } - - public static IsDocPinned(doc: Doc) { - //add this new doc to props.Document - const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; - if (curPres) { - return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; - } - return false; - } - public static async loadCurrentUser() { return rp.get(Utils.prepend("/getCurrentUser")).then(response => { if (response) { -- cgit v1.2.3-70-g09d2 From 821bd7bb1b2a59459c63ecfbe9c513e5f36a7e43 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Apr 2020 01:39:15 -0400 Subject: fixed runtime errors for AntimodeMenu and from current_user_utils not initializing properly --- src/client/util/RichTextMenu.tsx | 8 ++++---- src/client/views/AntimodeMenu.tsx | 21 +++++++++++++-------- src/client/views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../authentication/models/current_user_utils.ts | 13 +++++++------ 5 files changed, 26 insertions(+), 20 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx index 3f0ec7aa5..4a9a4c10f 100644 --- a/src/client/util/RichTextMenu.tsx +++ b/src/client/util/RichTextMenu.tsx @@ -445,8 +445,8 @@ export default class RichTextMenu extends AntimodeMenu { } const button = - ; const dropdownContent = @@ -790,11 +790,11 @@ export default class RichTextMenu extends AntimodeMenu {
    {this.getDragger()}
    diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index fba2fb5c6..f810361c6 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -16,7 +16,8 @@ export default abstract class AntimodeMenu extends React.Component { @observable protected _top: number = -300; @observable protected _left: number = -300; @observable protected _opacity: number = 1; - @observable protected _transition: string = "opacity 0.5s"; + @observable protected _transitionProperty: string = "opacity"; + @observable protected _transitionDuration: string = "0.5s"; @observable protected _transitionDelay: string = ""; @observable protected _canFade: boolean = true; @@ -34,7 +35,7 @@ export default abstract class AntimodeMenu extends React.Component { */ public jumpTo = (x: number, y: number, forceJump: boolean = false) => { if (!this.Pinned || forceJump) { - this._transition = this._transitionDelay = ""; + this._transitionProperty = this._transitionDuration = this._transitionDelay = ""; this._opacity = 1; this._left = x; this._top = y; @@ -49,14 +50,16 @@ export default abstract class AntimodeMenu extends React.Component { public fadeOut = (forceOut: boolean) => { if (!this.Pinned) { if (this._opacity === 0.2) { - this._transition = "opacity 0.1s"; + this._transitionProperty = "opacity"; + this._transitionDuration = "0.1s"; this._transitionDelay = ""; this._opacity = 0; this._left = this._top = -300; } if (forceOut) { - this._transition = ""; + this._transitionProperty = ""; + this._transitionDuration = ""; this._transitionDelay = ""; this._opacity = 0; this._left = this._top = -300; @@ -67,7 +70,8 @@ export default abstract class AntimodeMenu extends React.Component { @action protected pointerLeave = (e: React.PointerEvent) => { if (!this.Pinned && this._canFade) { - this._transition = "opacity 0.5s"; + this._transitionProperty = "opacity"; + this._transitionDuration = "0.5s"; this._transitionDelay = "1s"; this._opacity = 0.2; setTimeout(() => this.fadeOut(false), 3000); @@ -76,7 +80,8 @@ export default abstract class AntimodeMenu extends React.Component { @action protected pointerEntered = (e: React.PointerEvent) => { - this._transition = "opacity 0.1s"; + this._transitionProperty = "opacity"; + this._transitionDuration = "0.1s"; this._transitionDelay = ""; this._opacity = 1; } @@ -133,7 +138,7 @@ export default abstract class AntimodeMenu extends React.Component { protected getElement(buttons: JSX.Element[]) { return (
    + style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay }}> {buttons}
    @@ -143,7 +148,7 @@ export default abstract class AntimodeMenu extends React.Component { protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) { return (
    + style={{ left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, height: "auto" }}> {rows} {hasDragger ?
    : <>}
    diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 83e536167..011e07287 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -650,7 +650,7 @@ export type collectionTreeViewProps = { }; @observer -export class CollectionTreeView extends CollectionSubView(Document, undefined as any as collectionTreeViewProps) { +export class CollectionTreeView extends CollectionSubView>(Document) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 87cf716e5..d2106808e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -75,7 +75,7 @@ export type collectionFreeformViewProps = { }; @observer -export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, undefined as any as collectionFreeformViewProps) { +export class CollectionFreeFormView extends CollectionSubView>(PanZoomDocument) { private _lastX: number = 0; private _lastY: number = 0; private _downX: number = 0; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index a2f016f94..85938e026 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -536,12 +536,13 @@ export class CurrentUserUtils { new InkingControl(); doc.title = Doc.CurrentUserEmail; doc.activePen = doc; - CurrentUserUtils.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon - CurrentUserUtils.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing - CurrentUserUtils.setupOverlays(doc); // documents in overlay layer - CurrentUserUtils.setupDockedButtons(doc); // the bottom bar of font icons - CurrentUserUtils.setupDefaultPresentation(doc); // presentation that's initially triggered - await CurrentUserUtils.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels + this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon + this.setupDocTemplates(doc); // sets up the template menu of templates + this.setupRightSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing + this.setupOverlays(doc); // documents in overlay layer + this.setupDockedButtons(doc); // the bottom bar of font icons + this.setupDefaultPresentation(doc); // presentation that's initially triggered + await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet -- cgit v1.2.3-70-g09d2 From abbe0168ae934eb12e3f2f9180e4e7667891ffc9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Apr 2020 11:11:18 -0400 Subject: fixed deiconify & clipping LOD collections & Paths --- .../views/collections/collectionFreeForm/CollectionFreeFormView.scss | 1 + src/client/views/nodes/DocumentView.tsx | 2 +- src/server/authentication/models/current_user_utils.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index e1516b468..a00311a9c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -36,6 +36,7 @@ height: 100%; display: flex; align-items: center; + overflow: hidden; .collectionfreeformview-placeholderSpan { font-size: 32; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 196104fe0..8a05cfb0d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -867,7 +867,7 @@ export class DocumentView extends DocComponent(Docu SelectionManager.SelectDoc(this, false); } }); - const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), ""); + const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), ""); cm.addItem({ description: `path: ${path}`, event: () => { this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 85938e026..e99e12a2a 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -141,7 +141,7 @@ export class CurrentUserUtils { if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, - onClick: ScriptField.MakeScript("deiconifyView(this)") + onClick: ScriptField.MakeScript("deiconifyView(self)") }); Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); iconView.isTemplateDoc = makeTemplate(iconView); -- cgit v1.2.3-70-g09d2 From feb43b4bf769575766825593269b97f46536dd72 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Apr 2020 11:48:57 -0400 Subject: fixed ctrl-enter editing of treeviews. --- src/client/views/collections/CollectionTreeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 011e07287..34f0f2313 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -189,7 +189,7 @@ class TreeView extends React.Component { Doc.SetInPlace(this.props.document, key, value, false); const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); Doc.SetInPlace(this.props.document, "editTitle", undefined, false); - Doc.SetInPlace(this.props.document, "editTitle", true, false); + Doc.SetInPlace(doc, "editTitle", true, false); return this.props.addDocument(doc); })} onClick={() => { -- cgit v1.2.3-70-g09d2 From 49ebcb56488669c1f416c583a9b8c7914e5b6d34 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Apr 2020 16:23:39 -0400 Subject: fixed moving documents in tree views --- src/client/util/DragManager.ts | 2 +- src/client/views/collections/CollectionTreeView.tsx | 13 +++++++------ src/server/authentication/models/current_user_utils.ts | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 3e9a5b63a..3ea030171 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -19,7 +19,7 @@ import { DateField } from "../../new_fields/DateField"; import { DocumentView } from "../views/nodes/DocumentView"; import { UndoManager } from "./UndoManager"; -export type dropActionType = "place" | "alias" | "copy" | undefined; +export type dropActionType = "alias" | "copy" | "move" | undefined; // undefined = move export function SetupDrag( _reference: React.RefObject, docFunc: () => Doc | Promise | undefined, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 34f0f2313..d8e2c8841 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -228,7 +228,8 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc); } const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId[Id] ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments); - return ((de.complete.docDragData.dropAction && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ? + const move = de.complete.docDragData.dropAction === "move" || de.complete.docDragData.dropAction; + return ((!move && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ? de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d) || added, false) : de.complete.docDragData.moveDocument ? movedDocs.reduce((added, d) => de.complete.docDragData?.moveDocument?.(d, undefined, addDoc) || added, false) @@ -335,7 +336,7 @@ class TreeView extends React.Component { {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document), this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, + StrCast(this.props.document.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)} ; @@ -456,7 +457,7 @@ class TreeView extends React.Component { pinToPres={emptyFunction} onClick={this.props.onChildClick || editTitle} dropAction={this.props.dropAction} - moveDocument={this.props.moveDocument} + moveDocument={this.move} removeDocument={this.removeDoc} ScreenToLocalTransform={this.getTransform} ContentScaling={returnOne} @@ -472,7 +473,7 @@ class TreeView extends React.Component { bringToFront={emptyFunction} dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)} ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} + ContainingCollectionDoc={this.props.containingCollection} />}
    {this.props.treeViewHideHeaderFields() ? (null) : headerElements} @@ -657,7 +658,7 @@ export class CollectionTreeView extends CollectionSubView { - this.treedropDisposer && this.treedropDisposer(); + this.treedropDisposer?.(); if (this._mainEle = ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); } @@ -665,7 +666,7 @@ export class CollectionTreeView extends CollectionSubView Date: Mon, 20 Apr 2020 16:37:13 -0400 Subject: fixed ctrl-enter on text box title. fixed min-width of tree view titles. --- src/client/views/collections/CollectionTreeView.scss | 1 + src/client/views/collections/CollectionTreeView.tsx | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 1e59c493f..a00bb6bfb 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -82,6 +82,7 @@ text-overflow: ellipsis; white-space: pre-wrap; overflow: hidden; + min-width: 10px; // width:100%;//width: max-content; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d8e2c8841..f589c2c76 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -487,11 +487,15 @@ class TreeView extends React.Component { return
  • { - e.stopPropagation(); - e.preventDefault(); + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } }} onPointerDown={e => { - e.stopPropagation(); - e.preventDefault(); + if (this.props.active(true)) { + e.stopPropagation(); + e.preventDefault(); + } }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> {this.renderBullet} {this.renderTitle} @@ -810,6 +814,7 @@ export class CollectionTreeView extends CollectionSubView([Templates.Title.Layout]) }); EditableView.loadId = doc[Id]; + Doc.SetInPlace(doc, "editTitle", true, false); this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); })} />)} {this.props.Document.allowClear ? this.renderClearButton : (null)} -- cgit v1.2.3-70-g09d2 From 0bc20bc8c1f2b6b77624d37047d63a31fa0cf299 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Apr 2020 11:25:30 -0400 Subject: added additional rendition menu item --- src/client/views/collections/CollectionView.tsx | 67 ++++++++++++++----------- 1 file changed, 37 insertions(+), 30 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 877e4355f..178c3189b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -192,38 +192,45 @@ export class CollectionView extends Touchable { } + setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) { + const existingVm = ContextMenu.Instance.findByDescription(category); + const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; + + subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" }); + if (addExtras && CollectionView._safeMode) { + ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" }); + } + subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" }); + subItems.push({ description: "Treeview", event: () => func(CollectionViewType.Tree), icon: "tree" }); + subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" }); + subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" }); + subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" }); + subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" }); + subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" }); + subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" }); + subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" }); + subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); + subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); + if (addExtras) switch (this.props.Document._viewType) { + case CollectionViewType.Freeform: { + subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); + break; + } + } + addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); + !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" }); + } + onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - const existingVm = ContextMenu.Instance.findByDescription("View Modes..."); - const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; - subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" }); - if (CollectionView._safeMode) { - ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" }); - } - subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" }); - subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" }); - subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" }); - subItems.push({ - description: "Stacking (AutoHeight)", event: () => { - this.props.Document._viewType = CollectionViewType.Stacking; - this.props.Document._autoHeight = true; - }, icon: "ellipsis-v" - }); - subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" }); - subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" }); - subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" }); - subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" }); - subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" }); - subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" }); - subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" }); - switch (this.props.Document._viewType) { - case CollectionViewType.Freeform: { - subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); - break; - } - } - subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); - !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); + + this.setupViewTypes("View Modes ...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); + this.setupViewTypes("Additional Rendition ...", vtype => { + const newRendition = Doc.MakeAlias(this.props.Document); + newRendition._viewType = vtype; + this.props.addDocTab(newRendition, "onRight"); + return newRendition; + }, false); const existing = ContextMenu.Instance.findByDescription("Layout..."); const layoutItems = existing && "subitems" in existing ? existing.subitems : []; -- cgit v1.2.3-70-g09d2 From 3d629495644631e6b614b1b48f1d32d6c7aeefc6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Apr 2020 17:25:17 -0400 Subject: added parameterization capability to layout strings. added Div's that can take Dash field values --- src/client/util/RichTextSchema.tsx | 6 ++-- src/client/views/DocumentDecorations.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 7 ++-- src/client/views/nodes/DocumentContentsView.tsx | 45 +++++++++++++++++++++---- 4 files changed, 46 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 1522f5e21..d23962d5c 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -852,9 +852,9 @@ export class DashDocView { }; this._renderDisposer?.(); this._renderDisposer = reaction(() => { - if (!Doc.AreProtosEqual(finalLayout, dashDoc)) { - finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here? - } + // if (!Doc.AreProtosEqual(finalLayout, dashDoc)) { + // finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here? + // } const layoutKey = StrCast(finalLayout.layoutKey); const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1]; if (finalLayout !== dashDoc && finalKey) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c8766a6b3..bd41788d4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,7 +3,7 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym } from "../../new_fields/Doc"; +import { Doc, DataSym, Field } from "../../new_fields/Doc"; import { PositionDocument } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; @@ -366,7 +366,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || ""; } if (this._titleControlString.startsWith("#")) { - return selected.props.Document[this._titleControlString.substring(1)]?.toString() || "-unset-"; + return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-"; } return this._accumulatedTitle; } else if (SelectionManager.SelectedDocuments().length > 1) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 178c3189b..4448fcf21 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -211,11 +211,8 @@ export class CollectionView extends Touchable { subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" }); subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); - if (addExtras) switch (this.props.Document._viewType) { - case CollectionViewType.Freeform: { - subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); - break; - } + if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) { + subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); } addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 7522af3a3..765416658 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,8 +1,8 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt } from "../../../new_fields/Doc"; -import { Cast, StrCast } from "../../../new_fields/Types"; -import { OmitKeys, Without } from "../../../Utils"; +import { Doc, Opt, Field } from "../../../new_fields/Doc"; +import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; +import { OmitKeys, Without, emptyPath } from "../../../Utils"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -53,6 +53,31 @@ class ObserverJsxParser1 extends JsxParser { const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; + +interface DivProps { + Document: Doc; +} + +@observer +export class Div extends React.Component { + render() { + const style: { [key: string]: any } = {}; + const divKeys = OmitKeys(this.props, ["children", "Document", "key", "__proto__"]).omit; + Object.keys(divKeys).map((prop: string) => { + let p = (this.props as any)[prop] as string; + if (p?.startsWith("@")) { // bcz: extend this to support expression -- is this really a script? + const key = p.substring(1); + const n = Cast(this.props.Document[key], "number", null); + p = n ? n.toString() : StrCast(this.props.Document[key], p); + } + style[prop] = p; + }); + return
    + {this.props.children} +
    ; + } +} + @observer export class DocumentContentsView extends React.Component boolean, @@ -102,21 +127,29 @@ export class DocumentContentsView extends React.Component 12 || !this.layout || !this.layoutDoc) ? (null) : + let layoutFrame = this.layout; + const replacer = (match: any, p1: string, offset: any, string: any) => { + const n = Cast(this.props.Document[p1], "number", null); + return n !== undefined ? n.toString() : StrCast(this.props.Document[p1], p1); + }; + layoutFrame = layoutFrame.replace(/\{([a-zA-Z0-9_-]+)\}/g, replacer); + return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) : this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ? : { console.log(test); }} -- cgit v1.2.3-70-g09d2 From 682782a1337003de1694d1625d262a1efddcb02d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Apr 2020 23:50:20 -0400 Subject: tweaked HTMLtag syntax slightly for onClick functions to be specified without quotes --- package-lock.json | 27 ++++++++++++++++++ package.json | 2 ++ .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - src/client/views/nodes/DocumentContentsView.tsx | 32 ++++++++++++++++------ src/client/views/search/SearchBox.tsx | 4 +-- 5 files changed, 54 insertions(+), 12 deletions(-) (limited to 'src/client/views/collections') diff --git a/package-lock.json b/package-lock.json index 940321132..176d1f27e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,15 @@ "regenerator-runtime": "^0.13.4" } }, + "@babel/runtime-corejs3": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz", + "integrity": "sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, "@babel/types": { "version": "7.9.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", @@ -1194,6 +1203,11 @@ "resolved": "https://registry.npmjs.org/@types/word-extractor/-/word-extractor-0.3.0.tgz", "integrity": "sha512-XcVsLXMpPT6Lv4Qvsc2yPoWg3zPVKEIyQ21eQ1ka58zZjtd7vUZ2kx0KydRbsNRNGopEIiwlemGzgo46dytR2Q==" }, + "@types/xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-3gJTS9gt27pS7U9q5IVqo4YvKSlkf2ck8ish6etuDj6LIRxkL/2Y8RMUtK/QzvE1Yv2zwWV5yemI2BS0GGGFnA==" + }, "@types/yargs": { "version": "13.0.8", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", @@ -3456,6 +3470,11 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -18092,6 +18111,14 @@ "resolved": "https://registry.npmjs.org/xoauth2/-/xoauth2-1.2.0.tgz", "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU=" }, + "xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", + "requires": { + "@babel/runtime-corejs3": "^7.8.3" + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index ed846fcff..f1b4be520 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,7 @@ "@types/uuid": "^3.4.6", "@types/webpack": "^4.41.3", "@types/word-extractor": "^0.3.0", + "@types/xregexp": "^4.3.0", "@types/youtube": "0.0.38", "adm-zip": "^0.4.13", "archiver": "^3.1.1", @@ -258,6 +259,7 @@ "word-extractor": "^0.3.0", "words-to-numbers": "^1.5.1", "xoauth2": "^1.2.0", + "xregexp": "^4.3.0", "youtube": "^0.1.0" } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d2106808e..3b5101a4d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,7 +44,6 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { CollectionViewType } from "../CollectionView"; -import { Script } from "vm"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index d577068b7..b5af68ba1 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -35,10 +35,10 @@ import { WebBox } from "./WebBox"; import { InkingStroke } from "../InkingStroke"; import React = require("react"); import { RecommendationsBox } from "../RecommendationsBox"; - import { TraceMobx } from "../../../new_fields/util"; import { ScriptField } from "../../../new_fields/ScriptField"; -import ReactTable from "react-table"; +import XRegExp = require("xregexp"); + const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; @@ -59,13 +59,13 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; interface HTMLtagProps { Document: Doc; htmltag: string; + onClick?: ScriptField; } -//" this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)' backgroundColor='{this.bannerColor}'>{this.title}" -@observer +//"{this.title}"@observer export class HTMLtag extends React.Component { click = (e: React.MouseEvent) => { - const clickScript = (this.props as any).onClick; - ScriptField.MakeScript(clickScript, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }); + const clickScript = (this.props as any).onClick as Opt; + clickScript?.script.run({ this: this.props.Document }); } render() { const style: { [key: string]: any } = {}; @@ -129,11 +129,12 @@ export class DocumentContentsView extends React.Component): JsxBindings { const list = { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc, + onClick: onClick }; return { props: list }; } @@ -141,6 +142,7 @@ export class DocumentContentsView extends React.Component{content}< as in {this.title} const replacer = (match: any, expr: string, offset: any, string: any) => { return ">" + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + "<"; @@ -159,9 +161,21 @@ export class DocumentContentsView extends React.Component; + if (splits.length > 1) { + const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] }); + layoutFrame = splits[0] + " onClick={props.onClick} " + splits[1].substring(code[1].end + 1); + onClick = ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name }); + } + + const bindings = this.CreateBindings(onClick); + // layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns + return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) : this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ? - + : {}; searchFileTypes: string[]; - setSearchFileTypes: (types: string[]) => {} + setSearchFileTypes: (types: string[]) => {}; } export enum Keys { @@ -86,7 +86,7 @@ export class SearchBox extends React.Component { this._searchString = this.props.searchQuery; this.submitSearch(); } - }) + }); @action -- cgit v1.2.3-70-g09d2 From 30db3b71d3661d7cd41ed32f4004069cb10ec2b4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Apr 2020 12:31:40 -0400 Subject: added raw text field for PDFs. fixed search scrolling in text boxes. --- src/client/views/collections/CollectionSubView.tsx | 1 + src/client/views/nodes/FormattedTextBox.tsx | 13 +++++-------- src/server/DashUploadUtils.ts | 7 ++++--- src/server/SharedMediaTypes.ts | 1 + 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 37cf942c6..ca38a21b8 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -388,6 +388,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: continue; } const proto = Doc.GetProto(doc); + proto.text = result.rawText; proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); if (Upload.isImageInformation(result)) { proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 62911dc5c..c8eb4c23c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -596,7 +596,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.setupEditor(this.config, this.props.fieldKey); - this._searchReactionDisposer = reaction(() => this.layoutDoc.searchMatch, + this._searchReactionDisposer = reaction(() => this.rootDoc.searchMatch, search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(), { fireImmediately: true }); @@ -821,13 +821,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._editorView = new EditorView(this.ProseRef, { state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config), handleScrollToSelection: (editorView) => { - const ref = editorView.domAtPos(editorView.state.selection.from); - let refNode = ref.node as any; - while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement; - const r1 = refNode?.getBoundingClientRect(); - const r3 = self._ref.current!.getBoundingClientRect(); - if (r1.top < r3.top || r1.top > r3.bottom) { - r1 && (self._scrollRef.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale); + const docPos = editorView.coordsAtPos(editorView.state.selection.from); + const viewRect = self._ref.current!.getBoundingClientRect(); + if (docPos.top < viewRect.top || docPos.top > viewRect.bottom) { + docPos && (self._scrollRef.current!.scrollTop += (docPos.top - viewRect.top) * self.props.ScreenToLocalTransform().Scale); } return true; }, diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 9b518749c..3f903a861 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -100,7 +100,7 @@ export namespace DashUploadUtils { const writeStream = createWriteStream(serverPathToFile(Directory.text, textFilename)); writeStream.write(result.text, error => error ? reject(error) : resolve()); }); - return MoveParsedFile(file, Directory.pdfs); + return MoveParsedFile(file, Directory.pdfs, undefined, result.text); } const manualSuffixes = [".webm"]; @@ -237,7 +237,7 @@ export namespace DashUploadUtils { * @param suffix If the file doesn't have a suffix and you want to provide it one * to appear in the new location */ - export async function MoveParsedFile(file: File, destination: Directory, suffix: string | undefined = undefined): Promise { + export async function MoveParsedFile(file: File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise { const { path: sourcePath } = file; let name = path.basename(sourcePath); suffix && (name += suffix); @@ -249,7 +249,8 @@ export namespace DashUploadUtils { result: error ? error : { accessPaths: { agnostic: getAccessPaths(destination, name) - } + }, + rawText: text } }); }); diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts index 2495123b7..0f788f6c5 100644 --- a/src/server/SharedMediaTypes.ts +++ b/src/server/SharedMediaTypes.ts @@ -21,6 +21,7 @@ export namespace Upload { export interface FileInformation { accessPaths: AccessPathInfo; + rawText?: string; } export type FileResponse = { source: File, result: T | Error }; -- cgit v1.2.3-70-g09d2 From 04bc902ad31eaf07e3a5edc34854744a88ffb3bc Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Apr 2020 14:08:19 -0400 Subject: fixed clicking gear icon on doc decorations. moved repl to Drag items. Removed Open... menu. Added Open FIelds & Full SCreen to Open New Persepectives. --- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/MainView.tsx | 3 ++- src/client/views/OverlayView.tsx | 6 +++++- src/client/views/collections/CollectionView.tsx | 8 ++------ src/client/views/nodes/DocumentView.tsx | 11 ++--------- src/server/authentication/models/current_user_utils.ts | 9 +++++---- 6 files changed, 18 insertions(+), 23 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bd41788d4..5d6242201 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -144,8 +144,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onSettingsClick = (e: PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey) { let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0]; - while (child.children.length && child.className !== "jsx-parser") child = child.children[0]; - simulateMouseClick(child.children[0], e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + while (child.children.length) child = child.children[0]; + simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f8e246315..8fb67c435 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faTerminal, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; +import { faTerminal, faCalculator, faWindowMaximize, faAddressCard, faQuestionCircle, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -103,6 +103,7 @@ export class MainView extends React.Component { } library.add(faTerminal); + library.add(faCalculator); library.add(faWindowMaximize); library.add(faFileAlt); library.add(faAddressCard); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 614217bc8..20aa14f84 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -9,6 +9,8 @@ import { Transform } from "../util/Transform"; import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView"; import { DocumentView } from "./nodes/DocumentView"; import './OverlayView.scss'; +import { Scripting } from "../util/Scripting"; +import { ScriptingRepl } from './ScriptingRepl'; export type OverlayDisposer = () => void; @@ -210,4 +212,6 @@ export class OverlayView extends React.Component {
    ); } -} \ No newline at end of file +} +// bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error +Scripting.addGlobal(function addOverlayWindow(Tag: string, options: OverlayElementOptions) { const x = ; OverlayView.Instance.addWindow(x, options); }); \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 4448fcf21..ade54f2c9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -221,8 +221,8 @@ export class CollectionView extends Touchable { onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - this.setupViewTypes("View Modes ...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); - this.setupViewTypes("Additional Rendition ...", vtype => { + this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); + this.setupViewTypes("Open New Perspective...", vtype => { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; this.props.addDocTab(newRendition, "onRight"); @@ -242,10 +242,6 @@ export class CollectionView extends Touchable { !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); - const open = ContextMenu.Instance.findByDescription("Open..."); - const openItems = open && "subitems" in open ? open.subitems : []; - !open && ContextMenu.Instance.addItem({ description: "Open...", subitems: openItems, icon: "hand-point-right" }); - const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; const funcs = [{ key: "onChildClick", name: "On Child Clicked", script: undefined as any as ScriptField }]; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 286044ab2..d8783c458 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,8 +40,6 @@ import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { InkingControl } from '../InkingControl'; import { KeyphraseQueryView } from '../KeyphraseQueryView'; -import { OverlayView } from '../OverlayView'; -import { ScriptingRepl } from '../ScriptingRepl'; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { RadialMenu } from './RadialMenu'; @@ -737,17 +735,12 @@ export class DocumentView extends DocComponent(Docu layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); - const open = ContextMenu.Instance.findByDescription("Open..."); + const open = cm.findByDescription("Open New Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" }); - openItems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, "inTab", this.props.LibraryPath), icon: "folder" }); - openItems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, "onRight", this.props.LibraryPath), icon: "caret-square-right" }); - openItems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" }); - openItems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); openItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); templateDoc && openItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - openItems.push({ description: "Open Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); - !open && cm.addItem({ description: "Open...", subitems: openItems, icon: "external-link-alt" }); + !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); const existingOnClick = cm.findByDescription("OnClick..."); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 3955f64fc..b4f9ead53 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -84,7 +84,7 @@ export class CurrentUserUtils { if (doc["template-buttons"] === undefined) { doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], { - title: "Template Item Creators", _xMargin: 0, _showTitle: "title", + title: "Compound Item Creators", _xMargin: 0, _showTitle: "title", _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); @@ -186,6 +186,7 @@ export class CurrentUserUtils { { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc }, + { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, @@ -194,10 +195,10 @@ export class CurrentUserUtils { // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.inkPen, this)`, activePen: doc }, // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc }, // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc }, - { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a document previewer", label: "Prev", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, - // { title: "buxton", icon: "cloud-upload-alt", ignoreClick: true, drag: "Docs.Create.Buxton()" }, + { title: "Drag a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, ]; + } // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools @@ -229,7 +230,7 @@ export class CurrentUserUtils { if (dragCreatorSet === undefined) { doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { - title: "Standard Item Creators", _showTitle: "title", _xMargin: 0, + title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), })); -- cgit v1.2.3-70-g09d2 From 2d44b804d3029e5f99bfe37310651aa12e69f3fc Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Apr 2020 17:19:34 -0400 Subject: extended links to support a draw all the time flag. fixed parentdocseletor to update when inputs change. --- src/client/views/collections/ParentDocumentSelector.tsx | 13 +++++++------ .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 5 +++-- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 4 ++-- src/client/views/nodes/LinkAnchorBox.tsx | 3 ++- 4 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 5c9d2b0dd..10c6ead1a 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import './ParentDocumentSelector.scss'; import { Doc } from "../../../new_fields/Doc"; import { observer } from "mobx-react"; -import { observable, action, runInAction, trace, computed } from "mobx"; +import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx"; import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; @@ -31,13 +31,14 @@ type SelectorProps = { export class SelectorContextMenu extends React.Component { @observable private _docs: { col: Doc, target: Doc }[] = []; @observable private _otherDocs: { col: Doc, target: Doc }[] = []; + _reaction: IReactionDisposer | undefined; - constructor(props: SelectorProps) { - super(props); - - this.fetchDocuments(); + componentDidMount() { + this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true }); + } + componentWillUnmount() { + this._reaction?.(); } - async fetchDocuments() { const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document); const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.Document[Id]}"` }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 09fc5148e..b2ea7290d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -25,7 +25,7 @@ export class CollectionFreeFormLinkView extends React.Component [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)], action(() => { setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() - setTimeout(action(() => this._opacity = 0.05), 750); // this will unhighlight the link line. + setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : []; const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : []; const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!); @@ -81,6 +81,7 @@ export class CollectionFreeFormLinkView extends React.Component + return !a.width || !b.width || ((!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> {text !== "-ungrouped-" ? text : ""} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index d12f93f15..702b02a20 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -31,14 +31,14 @@ export class CollectionFreeFormLinksView extends React.Component { }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]); return connections.filter(c => c.a.props.layoutKey && c.b.props.layoutKey && c.a.props.Document.type === DocumentType.LINK && - c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed + c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed ).map(c => ); } render() { return
    - {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections} + {this.uniqueConnections} {this.props.children}
    ; diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 3b1ced815..8e3c15908 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -40,7 +40,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - setupMoveUpEvents(this, e, this.onPointerMove, () => { }, this.onClick); + setupMoveUpEvents(this, e, this.onPointerMove, () => { }, this.onClick, false); } onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => { const cdiv = this._ref && this._ref.current && this._ref.current.parentElement; @@ -107,6 +107,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent this.openLinkTargetOnRight(e), icon: "eye" }); funcs.push({ description: "Open Link on Right", event: () => this.openLinkDocOnRight(e), icon: "eye" }); funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" }); + funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" }); ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" }); } -- cgit v1.2.3-70-g09d2 From da2be3c1d53584cb4b0ea2920a20fd524dcba89d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Apr 2020 18:03:49 -0400 Subject: made hyperlink lines curved --- .../collectionFreeForm/CollectionFreeFormLinkView.scss | 1 + .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 75af11537..05111adb4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -4,6 +4,7 @@ pointer-events: all; stroke-width: 3px; transition: opacity 0.5s ease-in; + fill: transparent; } .collectionfreeformlinkview-linkCircle { stroke: rgb(0,0,0); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index b2ea7290d..cf12ef382 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -94,6 +94,13 @@ export class CollectionFreeFormLinkView extends React.Component {text !== "-ungrouped-" ? text : ""} - + {/* + x2={`${pt2[0]}`} y2={`${pt2[1]}`} /> */} ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 72f614f023a4bac6a2f0b63a1b1a16e6f4545c7a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 23 Apr 2020 00:53:08 -0400 Subject: fixed browser back after link navigation. simplified menus more. fixed jitter mode to be seeded. fixed menu gear for collections. . --- src/client/util/History.ts | 6 +++- src/client/util/ProsemirrorExampleTransfer.ts | 4 +-- src/client/views/DocumentDecorations.tsx | 6 +++- .../views/collections/CollectionDockingView.tsx | 6 +++- .../views/collections/CollectionStackingView.tsx | 2 +- .../views/collections/CollectionTimeView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 21 +++++------ src/client/views/nodes/AudioBox.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 9 +++-- src/client/views/nodes/DocumentBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 41 ++++++++++++++++++++-- src/client/views/nodes/KeyValueBox.tsx | 23 ++++++++---- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/ScreenshotBox.tsx | 2 +- src/client/views/nodes/SliderBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- 20 files changed, 103 insertions(+), 39 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 545e8acb4..2c53d7e52 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -40,8 +40,12 @@ export namespace HistoryUtil { // } } + let _lastStatePush = 0; export function pushState(state: ParsedUrl) { - history.pushState(state, "", createUrl(state)); + if (Date.now() - _lastStatePush > 1000) { + history.pushState(state, "", createUrl(state)); + } + _lastStatePush = Date.now(); } export function replaceState(state: ParsedUrl) { diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 42247f177..680f48f70 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -154,7 +154,7 @@ export default function buildKeymap>(schema: S, props: any const originalDoc = layoutDoc.rootDocument || layoutDoc; if (originalDoc instanceof Doc) { const newDoc = Docs.Create.TextDocument("", { - title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), + layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), x: NumCast(originalDoc.x), y: NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10, _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height) }); FormattedTextBox.SelectOnLoad = newDoc[Id]; @@ -172,7 +172,7 @@ export default function buildKeymap>(schema: S, props: any const originalDoc = layoutDoc.rootDocument || layoutDoc; if (force || props.Document._singleLine) { const newDoc = Docs.Create.TextDocument("", { - title: "", layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), + layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), x: NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10, y: NumCast(originalDoc.y), _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height) }); FormattedTextBox.SelectOnLoad = newDoc[Id]; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 43c7751fa..6e699bc68 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -134,7 +134,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onSettingsClick = (e: PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey) { let child = SelectionManager.SelectedDocuments()[0].ContentDiv!.children[0]; - while (child.children.length) child = child.children[0]; + while (child.children.length) { + const next = Array.from(child.children).find(c => !c.className.includes("collectionViewChrome")); + if (next) child = next; + else break; + } simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); } } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 5e77bc0bb..0d859c3f1 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -29,6 +29,7 @@ import "./CollectionDockingView.scss"; import { SubCollectionViewProps } from "./CollectionSubView"; import { DockingViewButtonSelector } from './ParentDocumentSelector'; import React = require("react"); +import { CollectionViewType } from './CollectionView'; library.add(faFile); const _global = (window /* browser */ || global /* node */) as any; @@ -93,6 +94,9 @@ export class CollectionDockingView extends React.Component { addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); - if (doc.dockingConfig) { + if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { return MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { return CollectionDockingView.AddRightSplit(doc, libraryPath); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b15649d83..24a3119cc 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -397,7 +397,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (!e.isPropagationStopped()) { const subItems: ContextMenuProps[] = []; subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" }); - ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" }); } } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 742a818bc..06ebf6d2d 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -102,7 +102,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { layoutItems.push({ description: "Auto Time/Pivot layout", event: () => { doc._forceRenderEngine = undefined; }, icon: "compress-arrows-alt" }); layoutItems.push({ description: "Sync with presentation", event: () => CollectionTimeView.SyncTimelineToPresentation(doc), icon: "compress-arrows-alt" }); - ContextMenu.Instance.addItem({ description: "Pivot/Time Options ...", subitems: layoutItems, icon: "eye" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); } @computed get _allFacets() { const facets = new Set(); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f589c2c76..a052d045c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -711,7 +711,7 @@ export class CollectionTreeView extends CollectionSubView this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" }); layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" }); layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" }); - ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ade54f2c9..4f1242c33 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -229,7 +229,7 @@ export class CollectionView extends Touchable { return newRendition; }, false); - const existing = ContextMenu.Instance.findByDescription("Layout..."); + const existing = ContextMenu.Instance.findByDescription("Options..."); const layoutItems = existing && "subitems" in existing ? existing.subitems : []; layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); if (this.props.Document.childLayout instanceof Doc) { @@ -240,7 +240,7 @@ export class CollectionView extends Touchable { } layoutItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); - !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); + !existing && ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3b5101a4d..55e55e56c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1048,16 +1048,17 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.props.children && this.props.annotationsKey) return; - const layoutItems: ContextMenuProps[] = []; - - layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); - layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); - layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); - layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); - layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); + const options = ContextMenu.Instance.findByDescription("Options..."); + const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; + + optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); + optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); + optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); + optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); + optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); - layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" }); - layoutItems.push({ + optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" }); + optionItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => { const input = document.createElement("input"); input.type = "file"; @@ -1085,7 +1086,7 @@ export class CollectionFreeFormView extends CollectionSubView { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 8f40ea2be..6ff6d1b42 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -162,7 +162,7 @@ export class AudioBox extends ViewBoxBaseComponent this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" }); - ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } stopRecording = action(() => { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3a7e005ac..615b05e43 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,3 @@ -import anime from "animejs"; import { computed, IReactionDisposer, observable, reaction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; @@ -29,8 +28,14 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent(PositionDocument) { @observable _animPos: number[] | undefined = undefined; + random(min: number, max: number) { // min should not be equal to max + const mseed = Math.abs(this.X * this.Y); + const seed = (mseed * 9301 + 49297) % 233280; + var rnd = seed / 233280; + return min + rnd * (max - min); + } get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive - get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${anime.random(-1, 1) * this.props.jitterRotation}deg)`; } + get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; } get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); } get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); } get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); } diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index ac562f19a..7583aa070 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -45,7 +45,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent Doc.GetProto(this.props.Document).excludeCollections = !this.props.Document.excludeCollections, icon: "expand-arrows-alt" }); funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); - ContextMenu.Instance.addItem({ description: "DocumentBox Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } @computed get contentDoc() { return (this.props.Document.isTemplateDoc || this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document)); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a19ba6506..cab7b965a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -741,7 +741,7 @@ export class DocumentView extends DocComponent(Docu const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); - onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" }); + onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 2c89d53d8..1329f7dcf 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from " import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; -import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; +import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from '../../../new_fields/documentSchemas'; import { Id } from '../../../new_fields/FieldSymbols'; import { InkTool } from '../../../new_fields/InkField'; @@ -48,6 +48,7 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './Format import React = require("react"); import { PrefetchProxy } from '../../../new_fields/Proxy'; import { makeTemplate } from '../../util/DropConverter'; +import { DocumentView } from './DocumentView'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -396,6 +397,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null); } specificContextMenu = (e: React.MouseEvent): void => { + const cm = ContextMenu.Instance; + const funcs: ContextMenuProps[] = []; this.props.Document.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" }); funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); @@ -407,10 +410,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" }); - funcs.push({ description: "Toggle Audio", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" }); + funcs.push({ description: "Toggle Dictation Icon", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" }); + + const highlighting: ContextMenuProps[] = []; ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option => - funcs.push({ + highlighting.push({ description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => { e.stopPropagation(); if (FormattedTextBox._highlights.indexOf(option) === -1) { @@ -421,8 +426,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.updateHighlights(); }, icon: "expand-arrows-alt" })); + funcs.push({ description: "highlighting...", subitems: highlighting, icon: "hand-point-right" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); + + const change = cm.findByDescription("Change Perspective..."); + const changeItems: ContextMenuProps[] = change && "subitems" in change ? change.subitems : []; + + const noteTypesDoc = Cast(Doc.UserDoc().noteTypes, Doc, null); + const noteTypes = DocListCast(noteTypesDoc?.data); + noteTypes.forEach(note => { + changeItems.push({ + description: StrCast(note.title), event: () => { + Doc.setNativeView(this.props.Document); + DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); + }, icon: "eye" + }) + }); + changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }) + !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); + + const open = cm.findByDescription("Open New Perspective..."); + const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; + + openItems.push({ + description: "FreeForm", event: () => { + const alias = Doc.MakeAlias(this.rootDoc); + DocumentView.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform"); + this.props.addDocTab(alias, "onRight"); + }, icon: "eye" + }) + !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); + } recordDictation = () => { diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 7aad6f90e..2970674a2 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -16,6 +16,8 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); +import { ContextMenu } from "../ContextMenu"; +import { ContextMenuProps } from "../ContextMenuItem"; export type KVPScript = { script: CompiledScript; @@ -34,11 +36,7 @@ export class KeyValueBox extends React.Component { @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } - get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; } - - constructor(props: FieldViewProps) { - super(props); - } + get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; } @action onEnterKey = (e: React.KeyboardEvent): void => { @@ -234,13 +232,26 @@ export class KeyValueBox extends React.Component { return new Doc; } + specificContextMenu = (e: React.MouseEvent): void => { + const cm = ContextMenu.Instance; + const open = cm.findByDescription("Change Perspective..."); + const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; + openItems.push({ + description: "Default Perspective", event: () => { + this.props.addDocTab(this.fieldDocToLayout, "inTab"); + this.props.addDocTab(this.props.Document, "close"); + }, icon: "image" + }); + !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" }); + } + render() { const dividerDragger = this.splitPercentage === 0 ? (null) :
    ; - return (
    + return (
    diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 8e3c15908..eb647d0e4 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -109,7 +109,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent this.openLinkEditor(e), icon: "eye" }); funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" }); - ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6db36e43c..3712c648e 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -210,7 +210,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" }); - ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } @computed get contentScaling() { return this.props.ContentScaling(); } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 11b24b059..125690dc7 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -120,7 +120,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent { const funcs: ContextMenuProps[] = []; funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) }); - ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" }); + ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } onChange = (values: readonly number[]) => runInAction(() => { this.dataDoc[this.minThumbKey] = values[0]; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 588068334..613929bca 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -209,7 +209,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent Date: Thu, 23 Apr 2020 02:09:10 -0400 Subject: fixed performance of panning with something selected by making TemplateMenu ot render unless it has explicitly been activated. --- src/client/views/DocumentButtonBar.tsx | 62 ++++++---------------- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/TemplateMenu.tsx | 9 ++-- src/client/views/Templates.tsx | 45 ++-------------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 8 +-- src/client/views/nodes/FormattedTextBox.tsx | 6 +-- src/client/views/webcam/DashWebRTCVideo.tsx | 2 +- 9 files changed, 37 insertions(+), 101 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 93987b751..5936bbfb8 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { RichTextField } from '../../new_fields/RichTextField'; import { NumCast, StrCast } from "../../new_fields/Types"; -import { emptyFunction } from "../../Utils"; +import { emptyFunction, setupMoveUpEvents } from "../../Utils"; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; import { UndoManager } from "../util/UndoManager"; import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView'; @@ -108,10 +108,8 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | get view0() { return this.props.views?.[0]; } @action - onLinkButtonMoved = (e: PointerEvent): void => { - if (this._linkButton.current !== null && (Math.abs(e.clientX - this._downX) > 3 || Math.abs(e.clientY - this._downY) > 3)) { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); + onLinkButtonMoved = (e: PointerEvent) => { + if (this._linkButton.current !== null) { const linkDrag = UndoManager.StartBatch("Drag Link"); this.view0 && DragManager.StartLinkDrag(this._linkButton.current, this.view0.props.Document, e.pageX, e.pageY, { dragComplete: dropEv => { @@ -131,26 +129,16 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | }, hideSource: false }); + return true; } - e.stopPropagation(); + return false; } onLinkButtonDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.addEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - document.addEventListener("pointerup", this.onLinkButtonUp); - e.stopPropagation(); + setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, emptyFunction); } - onLinkButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - e.stopPropagation(); - } @computed get considerGoogleDocsPush() { @@ -257,29 +245,12 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | }} />; } - private _downx = 0; - private _downy = 0; - onAliasButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onAliasButtonMoved); - document.removeEventListener("pointerup", this.onAliasButtonUp); - e.stopPropagation(); - } - + @observable _aliasDown = false; onAliasButtonDown = (e: React.PointerEvent): void => { - this._downx = e.clientX; - this._downy = e.clientY; - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onAliasButtonMoved); - document.addEventListener("pointermove", this.onAliasButtonMoved); - document.removeEventListener("pointerup", this.onAliasButtonUp); - document.addEventListener("pointerup", this.onAliasButtonUp); - } - onAliasButtonMoved = (e: PointerEvent): void => { - if (this._dragRef.current !== null && (Math.abs(e.clientX - this._downx) > 4 || Math.abs(e.clientY - this._downy) > 4)) { - document.removeEventListener("pointermove", this.onAliasButtonMoved); - document.removeEventListener("pointerup", this.onAliasButtonUp); - + setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); + }; + onAliasButtonMoved = () => { + if (this._dragRef.current) { const dragDocView = this.props.views[0]!; const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]); const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); @@ -290,9 +261,10 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | offsetY: dragData.offset[1], hideSource: false }); + return true; } - e.stopPropagation(); - } + return false; + }; @computed get templateButton() { @@ -301,9 +273,9 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | Array.from(Object.values(Templates.TemplateList)).map(template => templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); return !view0 ? (null) :
    - v).map(v => v as DocumentView)} templates={templates} />}> -
    + this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} + content={!this._aliasDown ? (null) : v).map(v => v as DocumentView)} templates={templates} />}> +
    {}
    diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6e699bc68..e1348a317 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -191,7 +191,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (e.button === 0) { const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { - CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0], selectedDocs[0].props.LibraryPath) + CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0], selectedDocs[0].props.LibraryPath); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index b76137f06..6894500dd 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -124,7 +124,7 @@ export class TemplateMenu extends React.Component { templateMenu.push(); templateMenu.push(); if (noteTypesDoc) { - addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(self)`)); + addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(self,firstDoc)`, {}, { firstDoc })); this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push( this.toggleLayout(e, template)} />)); } @@ -175,11 +175,10 @@ Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) { return templateTitle && DocumentView.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template); }); -Scripting.addGlobal(function templateIsUsed(templateDoc: Doc) { - const firstDoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0].props.Document : undefined; - if (firstDoc) { +Scripting.addGlobal(function templateIsUsed(templateDoc: Doc, selDoc: Doc) { + if (selDoc) { const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title); - return StrCast(firstDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked'; + return StrCast(selDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked'; } return false; }); \ No newline at end of file diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index 8c60f1c36..a6dbaa650 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -1,45 +1,23 @@ -import React = require("react"); - -export enum TemplatePosition { - InnerTop, - InnerBottom, - InnerRight, - InnerLeft, - TopRight, - OutterTop, - OutterBottom, - OutterRight, - OutterLeft, -} - export class Template { - constructor(name: string, position: TemplatePosition, layout: string) { + constructor(name: string, layout: string) { this._name = name; - this._position = position; this._layout = layout; } private _name: string; - private _position: TemplatePosition; private _layout: string; get Name(): string { return this._name; } - get Position(): TemplatePosition { - return this._position; - } - get Layout(): string { return this._layout; } } export namespace Templates { - // export const BasicLayout = new Template("Basic layout", "{layout}"); - - export const Caption = new Template("Caption", TemplatePosition.OutterBottom, + export const Caption = new Template("Caption", `
    {layout}
    @@ -47,16 +25,7 @@ export namespace Templates {
    ` ); - export const Title = new Template("Title", TemplatePosition.InnerTop, - `
    -
    - {props.Document.title} -
    -
    -
    {layout}
    -
    -
    ` ); - export const TitleHover = new Template("TitleHover", TemplatePosition.InnerTop, + export const Title = new Template("Title", `
    {props.Document.title} @@ -65,14 +34,8 @@ export namespace Templates {
    {layout}
    ` ); + export const TitleHover = new Template("TitleHover", Title.Layout); export const TemplateList: Template[] = [Title, TitleHover, Caption]; - - export function sortTemplates(a: Template, b: Template) { - if (a.Position < b.Position) { return -1; } - if (a.Position > b.Position) { return 1; } - return 0; - } - } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 55e55e56c..8ead1c300 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1057,7 +1057,7 @@ export class CollectionFreeFormView extends CollectionSubView this.updateClusters(!this.Document.useClusters), icon: "braille" }); optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); - optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" }); + optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" }); optionItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => { const input = document.createElement("input"); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 615b05e43..4b282b0c9 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -31,7 +31,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } else { - DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0]; - DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)]; + if (this.props.Document.type === DocumentType.RTF) { + DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0]; + DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)]; - this.props.focus(this.props.Document, false); + this.props.focus(this.props.Document, false); + } SelectionManager.SelectDoc(this, e.ctrlKey); } preventDefault = false; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 1329f7dcf..fd7462a10 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -441,9 +441,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp Doc.setNativeView(this.props.Document); DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); }, icon: "eye" - }) + }); }); - changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }) + changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }); !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); const open = cm.findByDescription("Open New Perspective..."); @@ -455,7 +455,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp DocumentView.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform"); this.props.addDocTab(alias, "onRight"); }, icon: "eye" - }) + }); !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); } diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index 1d52ba38f..2ea011316 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -3,7 +3,7 @@ import React = require("react"); import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView"; import { FieldViewProps, FieldView } from "../nodes/FieldView"; import { observable, action } from "mobx"; -import { DocumentDecorations, CloseCall } from "../DocumentDecorations"; +import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import "../../views/nodes/WebBox.scss"; import "./DashWebRTCVideo.scss"; -- cgit v1.2.3-70-g09d2