From 7b1a4b93be9d01ab5613de09b8f308291f709b01 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 9 Apr 2020 23:27:41 -0400 Subject: fix presbox to work with linked documents. more cleanup to use dataDoc/layoutDoc/rootDoc. changed ## to >> for inline comment to open up #### heading markdown --- .../views/collections/ParentDocumentSelector.tsx | 8 ++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 41 ---------------------- 2 files changed, 7 insertions(+), 42 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index afc1c6754..5c9d2b0dd 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -114,7 +114,13 @@ export class DockingViewButtonSelector extends React.Component<{ views: Document } render() { - return { if (getComputedStyle(this._ref.current!).width !== "100%") {e.stopPropagation();e.preventDefault();} this.props.views[0].select(false); }} className="buttonSelector"> + return { + if (getComputedStyle(this._ref.current!).width !== "100%") { + e.stopPropagation(); e.preventDefault(); + } + this.props.views[0]?.select(false); + }} className="buttonSelector"> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 146ec9f7d..b5bcc0cc2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1017,47 +1017,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u private thumbIdentifier?: number; - // @action - // handleHandDown = (e: React.TouchEvent) => { - // const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); - // const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); - // this.thumbIdentifier = thumb?.identifier; - // const others = fingers.filter(f => f !== thumb); - // const minX = Math.min(...others.map(f => f.clientX)); - // const minY = Math.min(...others.map(f => f.clientY)); - // const t = this.getTransform().transformPoint(minX, minY); - // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); - - // const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); - // if (thumbDoc) { - // this._palette = ; - // } - - // document.removeEventListener("touchmove", this.onTouch); - // document.removeEventListener("touchmove", this.handleHandMove); - // document.addEventListener("touchmove", this.handleHandMove); - // document.removeEventListener("touchend", this.handleHandUp); - // document.addEventListener("touchend", this.handleHandUp); - // } - - // @action - // handleHandMove = (e: TouchEvent) => { - // for (let i = 0; i < e.changedTouches.length; i++) { - // const pt = e.changedTouches.item(i); - // if (pt?.identifier === this.thumbIdentifier) { - // } - // } - // } - - // @action - // handleHandUp = (e: TouchEvent) => { - // this.onTouchEnd(e); - // if (this.prevPoints.size < 3) { - // this._palette = undefined; - // document.removeEventListener("touchend", this.handleHandUp); - // } - // } - onContextMenu = (e: React.MouseEvent) => { if (this.props.children && this.props.annotationsKey) return; const layoutItems: ContextMenuProps[] = []; -- cgit v1.2.3-70-g09d2 From e4b22138e39244728144fbdd4c06e439be1b519a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 10 Apr 2020 13:24:07 -0400 Subject: more cleanup for layoutDoc/dataDoc/rootDoc updates. fix to doc filtering checkboxes getting filtered. fix to template captions. fixes to generalize LabelDoc's buttonParms --- src/client/documents/Documents.ts | 10 ++--- src/client/util/DragManager.ts | 2 +- src/client/util/DropConverter.ts | 2 +- src/client/util/RichTextRules.ts | 2 +- src/client/views/DocComponent.tsx | 16 +++---- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/InkingStroke.tsx | 4 +- src/client/views/TemplateMenu.tsx | 6 +-- src/client/views/collections/CollectionSubView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 16 ++++--- src/client/views/collections/CollectionView.tsx | 17 ++++---- .../views/collections/CollectionViewChromes.tsx | 23 ++-------- src/client/views/nodes/AudioBox.tsx | 8 ++-- src/client/views/nodes/ColorBox.tsx | 4 +- src/client/views/nodes/DocumentBox.tsx | 6 +-- src/client/views/nodes/DocumentView.tsx | 6 +-- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 48 +++++++++----------- src/client/views/nodes/LinkAnchorBox.tsx | 4 +- src/client/views/nodes/LinkBox.tsx | 4 +- src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/nodes/PresBox.tsx | 4 +- src/client/views/nodes/QueryBox.tsx | 4 +- src/client/views/nodes/ScreenshotBox.tsx | 6 +-- src/client/views/nodes/ScriptingBox.tsx | 4 +- src/client/views/nodes/SliderBox.tsx | 51 ++++++++++------------ src/client/views/nodes/VideoBox.tsx | 4 +- src/client/views/nodes/WebBox.tsx | 4 +- src/client/views/pdf/PDFViewer.tsx | 4 +- .../views/presentationview/PresElementBox.tsx | 4 +- src/new_fields/Doc.ts | 9 ++-- src/new_fields/RichTextField.ts | 4 ++ 34 files changed, 137 insertions(+), 159 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c823c3233..22f22cbfd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -566,8 +566,8 @@ export namespace Docs { LinkManager.Instance.addLink(doc); - Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)"); return doc; } @@ -908,10 +908,10 @@ export namespace DocUtils { if (target.doc === CurrentUserUtils.UserDocument) return undefined; const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship }, id); - Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" (" + (this.linkRelationship||"to") +") " + this.anchor2.title'); + Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('self.anchor1.title +" (" + (self.linkRelationship||"to") +") " + self.anchor2.title'); - Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)"); return linkDoc; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c856bbb1e..346eb2fb4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -222,7 +222,7 @@ export namespace DragManager { const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) }); params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); initialize?.(bd); - bd.buttonParams = new List(params); + bd["onclick-params"] = new List(params); e.docDragData && (e.docDragData.droppedDocuments = [bd]); }; StartDrag(eles, new DragManager.DocumentDragData([]), downX, downY, options, finishDrag); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index eb4d3a9b0..8cea120cd 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -35,7 +35,7 @@ export function makeTemplate(doc: Doc, first: boolean = true, rename: Opt Opt; @@ -22,15 +22,15 @@ export function DocComponent

(schemaCtor: (doc: D // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info @computed get layoutDoc() { return Doc.Layout(this.props.Document); } // This is the data part of a document -- ie, the data that is constant across all views of the document - @computed get dataDoc() { return this.props.Document[DataSym]; } + @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; } -/// DocStaticProps return a base class for React document views that have data extensions but aren't annotatable (e.g. AudioBox, FormattedTextBox) -interface DocExtendableProps { +/// FieldViewBoxProps - a generic base class for field views that are not annotatable (e.g. AudioBox, FormattedTextBox) +interface ViewBoxBaseProps { Document: Doc; DataDoc?: Doc; fieldKey: string; @@ -38,7 +38,7 @@ interface DocExtendableProps { renderDepth: number; rootSelected: (outsideReaction?: boolean) => boolean; } -export function DocExtendableComponent

(schemaCtor: (doc: Doc) => T) { +export function ViewBoxBaseComponent

(schemaCtor: (doc: Doc) => T) { class Component extends Touchable

{ //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then //@computed get Document(): T { return schemaCtor(this.props.Document); } @@ -60,8 +60,8 @@ export function DocExtendableComponent

(schemaCt } -/// DocAnnotatbleComponent return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image) -export interface DocAnnotatableProps { +/// DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image) +export interface ViewBoxAnnotatableProps { Document: Doc; DataDoc?: Doc; fieldKey: string; @@ -71,7 +71,7 @@ export interface DocAnnotatableProps { rootSelected: (outsideReaction?: boolean) => boolean; renderDepth: number; } -export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { +export function ViewBoxAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { class Component extends Touchable

{ @observable _isChildActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bd72385ef..c49fe157c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -393,7 +393,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (SelectionManager.SelectedDocuments().length === 1) { const selected = SelectionManager.SelectedDocuments()[0]; if (this._titleControlString.startsWith("=")) { - return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ this: selected.props.Document }, console.log).result?.toString() || ""; + 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-"; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index ff63a7c33..f66c04e1f 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -4,7 +4,7 @@ import { documentSchema } from "../../new_fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../new_fields/InkField"; import { makeInterface } from "../../new_fields/Schema"; import { Cast, StrCast, NumCast } from "../../new_fields/Types"; -import { DocExtendableComponent } from "./DocComponent"; +import { ViewBoxBaseComponent } from "./DocComponent"; import { InkingControl } from "./InkingControl"; import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; @@ -22,7 +22,7 @@ type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @observer -export class InkingStroke extends DocExtendableComponent(InkDocument) { +export class InkingStroke extends ViewBoxBaseComponent(InkDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } private analyzeStrokes = () => { diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 8fb8e7516..4d7f1e443 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -76,7 +76,7 @@ export class TemplateMenu extends React.Component { @undoBatch @action toggleTemplate = (event: React.ChangeEvent, template: Template): void => { - this.props.docViews.forEach(d => Doc.Layout(d.Document)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : ""); + this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : ""); } @action @@ -87,7 +87,7 @@ export class TemplateMenu extends React.Component { @undoBatch @action toggleChrome = (): void => { - this.props.docViews.map(dv => Doc.Layout(dv.Document)).forEach(layout => + this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout => layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : StrCast(layout._replacedChrome, "enabled"))); } @@ -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(this)`)); + addedTypes.concat(noteTypes).map(template => template.treeViewChecked = ComputedField.MakeFunction(`templateIsUsed(self)`)); this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push( this.toggleLayout(e, template)} />)); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 41f4fb3f0..0c00a1f22 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -120,8 +120,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: return Cast(this.dataField, listSpec(Doc)); } @computed get childDocs() { - const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []); - const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []); + const docFilters = this.props.ignoreFields?.includes("_docFilters") ? [] : Cast(this.props.Document._docFilters, listSpec("string"), []); + const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields for (let i = 0; i < docFilters.length; i += 3) { const [key, value, modifiers] = docFilters.slice(i, i + 3); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7edda5a4f..fd84c6498 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -735,16 +735,18 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as heroView._showTitle = "title"; heroView._showTitleHover = "titlehover"; - Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", + Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data", Docs.Create.FontIconDocument({ - _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - dragFactory: heroView, removeDropProperties: new List(["dropAction"]), title: "hero view", icon: "portrait" + 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(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", + Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data", Docs.Create.FontIconDocument({ - _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - dragFactory: detailView, removeDropProperties: new List(["dropAction"]), title: "detail view", icon: "file-alt" + title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", + dragFactory: detailView, removeDropProperties: new List(["dropAction"]), icon: "file-alt", + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), })); Document.childLayout = heroView; @@ -837,7 +839,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey Docs.Create.TextDocument("", { title: facetValue.toString(), treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", - { layoutDoc: Doc.name, facetHeader: "string", facetValue: "string" }, + {}, { layoutDoc, facetHeader, facetValue }) })); return new List(facetValueDocSet); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8192e6751..821840e0b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -354,14 +354,15 @@ export class CollectionView extends Touchable { let newFacet: Opt; if (nonNumbers / allCollectionDocs.length < .1) { newFacet = Docs.Create.SliderDocument({ title: facetHeader }); + const newFacetField = Doc.LayoutFieldKey(newFacet); const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox newFacet.treeViewExpandedView = "layout"; newFacet.treeViewOpen = true; - newFacet._sliderMin = ranged === undefined ? minVal : ranged[0]; - newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1]; - newFacet._sliderMinThumb = minVal; - newFacet._sliderMaxThumb = maxVal; + 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; newFacet.target = this.props.Document; const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); @@ -370,14 +371,12 @@ export class CollectionView extends Touchable { } else { newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; - const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; - newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); } newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); } } - 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); @@ -385,11 +384,11 @@ export class CollectionView extends Touchable { }), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0)); } filterBackground = () => "dimGray"; + 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)"; return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); } - @computed get treeIgnoreFields() { return ["_facetCollection", "_docFilters"]; } @computed get filterView() { const facetCollection = this.props.Document; const flyout = ( @@ -419,7 +418,7 @@ export class CollectionView extends Touchable { NativeWidth={returnZero} treeViewHideHeaderFields={true} onCheckedClick={this.scriptField!} - ignoreFields={this.treeIgnoreFields} + ignoreFields={this.ignoreFields} annotationsKey={""} dontRegisterView={true} PanelWidth={this.facetWidth} diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index b2ca3c19f..ba95dce00 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -361,28 +361,13 @@ export class CollectionViewBaseChrome extends React.Component { - this._startDragPosition = { x: e.clientX, y: e.clientY }; - document.addEventListener("pointermove", this.dragPointerMove); - document.addEventListener("pointerup", this.dragPointerUp); - e.stopPropagation(); - e.preventDefault(); - } - - dragPointerMove = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - const [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y]; - if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { + setupMoveUpEvents(this, e, (e, down, delta) => { this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY)); - document.removeEventListener("pointermove", this.dragPointerMove); - document.removeEventListener("pointerup", this.dragPointerUp); - } - } - dragPointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.dragPointerMove); - document.removeEventListener("pointerup", this.dragPointerUp); + return true; + }, emptyFunction, emptyFunction); + this._startDragPosition = { x: e.clientX, y: e.clientY }; } render() { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index c08a2b808..7078cc01c 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import "./AudioBox.scss"; import { Cast, DateCast, NumCast } from "../../../new_fields/Types"; import { AudioField, nullAudio } from "../../../new_fields/URLField"; -import { DocExtendableComponent } from "../DocComponent"; +import { ViewBoxBaseComponent } from "../DocComponent"; import { makeInterface, createSchema } from "../../../new_fields/Schema"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils"; @@ -39,7 +39,7 @@ type AudioDocument = makeInterface<[typeof documentSchema, typeof audioSchema]>; const AudioDocument = makeInterface(documentSchema, audioSchema); @observer -export class AudioBox extends DocExtendableComponent(AudioDocument) { +export class AudioBox extends ViewBoxBaseComponent(AudioDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } public static Enabled = false; @@ -194,8 +194,8 @@ export class AudioBox extends DocExtendableComponent; const ColorDocument = makeInterface(documentSchema); @observer -export class ColorBox extends DocExtendableComponent(ColorDocument) { +export class ColorBox extends ViewBoxBaseComponent(ColorDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); } render() { diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index 4f2b3b656..ac562f19a 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -9,7 +9,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyPath } from "../../../Utils"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { DocAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; import "./DocumentBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; @@ -22,7 +22,7 @@ type DocHolderBoxSchema = makeInterface<[typeof documentSchema]>; const DocHolderBoxDocument = makeInterface(documentSchema); @observer -export class DocHolderBox extends DocAnnotatableComponent(DocHolderBoxDocument) { +export class DocHolderBox extends ViewBoxAnnotatableComponent(DocHolderBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); } _prevSelectionDisposer: IReactionDisposer | undefined; _selections: Doc[] = []; @@ -54,7 +54,7 @@ export class DocHolderBox extends DocAnnotatableComponent { - this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(this,this.excludeCollections,[_last_])?.[0]`); + this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); } isSelectionLocked = () => { const kvpstring = Field.toKeyValueString(this.contentDoc, this.props.fieldKey); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1bccce054..1e22dbc0b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -298,7 +298,7 @@ export class DocumentView extends DocComponent(Docu } else if (this.onClickHandler?.script) { SelectionManager.DeselectAll(); const func = () => this.onClickHandler.script.run({ - this: this.props.Document, + this: this.layoutDoc, self: this.rootDoc, thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log); @@ -512,8 +512,8 @@ export class DocumentView extends DocComponent(Docu onPointerUp = (e: PointerEvent): void => { this.cleanUpInteractions(); - if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - this.onPointerUpHandler.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log); document.removeEventListener("pointerup", this.onPointerUp); return; } diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index bd7ea4c92..9329cf210 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -56,7 +56,7 @@ export class FontIconBox extends DocComponent( background: StrCast(referenceLayout.backgroundColor), boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined }}> - + ; } } \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index fb919a716..d641dc791 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -38,7 +38,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { DocAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentButtonBar } from '../DocumentButtonBar'; import { InkingControl } from "../InkingControl"; import { AudioBox } from './AudioBox'; @@ -69,7 +69,7 @@ const RichTextDocument = makeInterface(richTextSchema, documentSchema); type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @observer -export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { +export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index bb7d78ace..5b0edf34f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -24,7 +24,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenuProps } from '../ContextMenuItem'; -import { DocAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; @@ -65,7 +65,7 @@ const uploadIcons = { }; @observer -export class ImageBox extends DocAnnotatableComponent(ImageDocument) { +export class ImageBox extends ViewBoxAnnotatableComponent(ImageDocument) { protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject = React.createRef(); diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 0ec6af93a..b9701f923 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -7,10 +7,10 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema'; import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, StrCast, Cast, FieldValue } from '../../../new_fields/Types'; +import { BoolCast, StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; -import { DocComponent } from '../DocComponent'; +import { ViewBoxBaseComponent } from '../DocComponent'; import './LabelBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; import { ContextMenuProps } from '../ContextMenuItem'; @@ -20,27 +20,16 @@ import { documentSchema } from '../../../new_fields/documentSchemas'; library.add(faEdit as any); -const LabelSchema = createSchema({ - onClick: ScriptField, - buttonParams: listSpec("string"), - text: "string" -}); +const LabelSchema = createSchema({}); type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>; const LabelDocument = makeInterface(LabelSchema, documentSchema); @observer -export class LabelBox extends DocComponent(LabelDocument) { +export class LabelBox extends ViewBoxBaseComponent(LabelDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } private dropDisposer?: DragManager.DragDropDisposer; - @computed get dataDoc() { - return this.props.DataDoc && - (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) || - this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); - } - - protected createDropTarget = (ele: HTMLDivElement) => { this.dropDisposer?.(); if (ele) { @@ -52,8 +41,8 @@ export class LabelBox extends DocComponent(LabelD const funcs: ContextMenuProps[] = []; funcs.push({ description: "Clear Script Params", event: () => { - const params = FieldValue(this.Document.buttonParams); - params?.map(p => this.props.Document[p] = undefined); + const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []); + params?.map(p => this.dataDoc[p] = undefined); }, icon: "trash" }); @@ -64,32 +53,35 @@ export class LabelBox extends DocComponent(LabelD @action drop = (e: Event, de: DragManager.DropEvent) => { const docDragData = de.complete.docDragData; - const params = this.Document.buttonParams; - const missingParams = params?.filter(p => this.props.Document[p] === undefined); + const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []); + const missingParams = params?.filter(p => this.dataDoc[p] === undefined); if (docDragData && missingParams?.includes((e.target as any).textContent)) { - this.props.Document[(e.target as any).textContent] = new List(docDragData.droppedDocuments.map((d, i) => + this.dataDoc[(e.target as any).textContent] = new List(docDragData.droppedDocuments.map((d, i) => d.onDragStart ? docDragData.draggedDocuments[i] : d)); e.stopPropagation(); } } // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { - const params = this.Document.buttonParams; - const missingParams = params?.filter(p => this.props.Document[p] === undefined); - params?.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... + const params = Cast(this.dataDoc[this.fieldKey + "-params"], listSpec("string"), []); + const missingParams = params?.filter(p => this.dataDoc[p] === undefined); + params?.map(p => DocListCast(this.dataDoc[p])); // bcz: really hacky form of prefetching ... return (

+ style={{ boxShadow: this.layoutDoc.opacity ? StrCast(this.layoutDoc.boxShadow) : "" }}>
- {(this.Document.text || this.Document.title)} + {StrCast(this.layoutDoc.text, StrCast(this.layoutDoc.title))}
- {!missingParams || !missingParams.length ? (null) : missingParams.map(m =>
{m}
)} + {!missingParams?.length ? (null) : missingParams.map(m =>
{m}
)}
); diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index f3e099dd6..13ffc6956 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -7,7 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Utils, setupMoveUpEvents } from '../../../Utils'; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager } from "../../util/DragManager"; -import { DocExtendableComponent } from "../DocComponent"; +import { ViewBoxBaseComponent } from "../DocComponent"; import "./LinkAnchorBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); @@ -25,7 +25,7 @@ type LinkAnchorSchema = makeInterface<[typeof documentSchema]>; const LinkAnchorDocument = makeInterface(documentSchema); @observer -export class LinkAnchorBox extends DocExtendableComponent(LinkAnchorDocument) { +export class LinkAnchorBox extends ViewBoxBaseComponent(LinkAnchorDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); } _doubleTap = false; _lastTap: number = 0; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 542c86049..af4bf420f 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -4,7 +4,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas"; import { makeInterface, listSpec } from "../../../new_fields/Schema"; import { returnFalse, returnZero } from "../../../Utils"; import { CollectionTreeView } from "../collections/CollectionTreeView"; -import { DocExtendableComponent } from "../DocComponent"; +import { ViewBoxBaseComponent } from "../DocComponent"; import { FieldView, FieldViewProps } from './FieldView'; import "./LinkBox.scss"; import { Cast } from "../../../new_fields/Types"; @@ -13,7 +13,7 @@ type LinkDocument = makeInterface<[typeof documentSchema]>; const LinkDocument = makeInterface(documentSchema); @observer -export class LinkBox extends DocExtendableComponent(LinkDocument) { +export class LinkBox extends ViewBoxBaseComponent(LinkDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); } render() { return
(PdfDocument) { +export class PDFBox extends ViewBoxAnnotatableComponent(PdfDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } private _keyValue: string = ""; private _valueValue: string = ""; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index bea3170ac..e428e16da 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -16,7 +16,7 @@ import { CollectionView, CollectionViewType } from "../collections/CollectionVie import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; import "./PresBox.scss"; -import { DocExtendableComponent } from "../DocComponent"; +import { ViewBoxBaseComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; library.add(faArrowLeft); @@ -33,7 +33,7 @@ type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @observer -export class PresBox extends DocExtendableComponent(PresBoxDocument) { +export class PresBox extends ViewBoxBaseComponent(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } _childReaction: IReactionDisposer | undefined; @observable _isChildActive = false; diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index 7016b4f04..947167200 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -6,7 +6,7 @@ import { Id } from '../../../new_fields/FieldSymbols'; import { makeInterface } from "../../../new_fields/Schema"; import { StrCast } from "../../../new_fields/Types"; import { SelectionManager } from "../../util/SelectionManager"; -import { DocAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./QueryBox.scss"; @@ -15,7 +15,7 @@ type QueryDocument = makeInterface<[typeof documentSchema]>; const QueryDocument = makeInterface(documentSchema); @observer -export class QueryBox extends DocAnnotatableComponent(QueryDocument) { +export class QueryBox extends ViewBoxAnnotatableComponent(QueryDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); } _docListChangedReaction: IReactionDisposer | undefined; componentDidMount() { diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 1a991f1eb..11b24b059 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -14,7 +14,7 @@ import { Docs, DocUtils } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { DocComponent } from "../DocComponent"; +import { ViewBoxBaseComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; import "./ScreenshotBox.scss"; @@ -26,7 +26,7 @@ const ScreenshotDocument = makeInterface(documentSchema, positionSchema); library.add(faVideo); @observer -export class ScreenshotBox extends DocComponent(ScreenshotDocument) { +export class ScreenshotBox extends ViewBoxBaseComponent(ScreenshotDocument) { private _reactionDisposer?: IReactionDisposer; private _videoRef: HTMLVideoElement | null = null; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); } @@ -109,7 +109,7 @@ export class ScreenshotBox extends DocComponent { - const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); + const field = Cast(this.dataDoc[this.fieldKey], VideoField); if (field) { const url = field.url.href; const subitems: ContextMenuProps[] = []; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 309ee3620..70f29e1dc 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -7,7 +7,7 @@ import { ScriptField } from "../../../new_fields/ScriptField"; import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types"; import { InteractionUtils } from "../../util/InteractionUtils"; import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting"; -import { DocAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./ScriptingBox.scss"; @@ -20,7 +20,7 @@ type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentS const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); @observer -export class ScriptingBox extends DocAnnotatableComponent(ScriptingDocument) { +export class ScriptingBox extends ViewBoxAnnotatableComponent(ScriptingDocument) { protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx index 844d95d11..746ea0b64 100644 --- a/src/client/views/nodes/SliderBox.tsx +++ b/src/client/views/nodes/SliderBox.tsx @@ -1,22 +1,20 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEdit } from '@fortawesome/free-regular-svg-icons'; -import { computed, runInAction } from 'mobx'; +import { runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Handles, Rail, Slider, Tracks, Ticks } from 'react-compound-slider'; -import { Doc } from '../../../new_fields/Doc'; +import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider'; import { documentSchema } from '../../../new_fields/documentSchemas'; -import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; +import { createSchema, makeInterface } from '../../../new_fields/Schema'; import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, FieldValue, StrCast, NumCast, Cast } from '../../../new_fields/Types'; -import { DragManager } from '../../util/DragManager'; +import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { DocComponent } from '../DocComponent'; -import './SliderBox.scss'; -import { Handle, TooltipRail, Track, Tick } from './SliderBox-components'; -import { FieldView, FieldViewProps } from './FieldView'; +import { ViewBoxBaseComponent } from '../DocComponent'; import { ScriptBox } from '../ScriptBox'; +import { FieldView, FieldViewProps } from './FieldView'; +import { Handle, Tick, TooltipRail, Track } from './SliderBox-components'; +import './SliderBox.scss'; library.add(faEdit as any); @@ -32,36 +30,33 @@ type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema] const SliderDocument = makeInterface(SliderSchema, documentSchema); @observer -export class SliderBox extends DocComponent(SliderDocument) { +export class SliderBox extends ViewBoxBaseComponent(SliderDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); } - private dropDisposer?: DragManager.DragDropDisposer; - - @computed get dataDoc() { - return this.props.DataDoc && - (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) || - this.props.DataDoc.layout === this.Document) ? this.props.DataDoc : Doc.GetProto(this.Document); - } + get minThumbKey() { return this.fieldKey + "-minThumb"; } + get maxThumbKey() { return this.fieldKey + "-maxThumb"; } + get minKey() { return this.fieldKey + "-min"; } + get maxKey() { return this.fieldKey + "-max"; } specificContextMenu = (e: React.MouseEvent): void => { 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" }); } onChange = (values: readonly number[]) => runInAction(() => { - this.Document._sliderMinThumb = values[0]; - this.Document._sliderMaxThumb = values[1]; - Cast(this.Document.onThumbChanged, ScriptField, null)?.script.run({ range: values, this: this.props.Document }); + this.dataDoc[this.minThumbKey] = values[0]; + this.dataDoc[this.maxThumbKey] = values[1]; + Cast(this.layoutDoc.onThumbChanged, ScriptField, null)?.script.run({ self: this.rootDoc, range: values, this: this.layoutDoc }); }) render() { - const domain = [NumCast(this.props.Document._sliderMin), NumCast(this.props.Document._sliderMax)]; - const defaultValues = [NumCast(this.props.Document._sliderMinThumb), NumCast(this.props.Document._sliderMaxThumb)]; - return ( + const domain = [NumCast(this.layoutDoc[this.minKey]), NumCast(this.layoutDoc[this.maxKey])]; + const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])]; + return domain[1] <= domain[0] ? (null) : (
e.stopPropagation()} - style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}> + style={{ boxShadow: this.layoutDoc.opacity === 0 ? undefined : StrCast(this.layoutDoc.boxShadow, "") }}>
(Slid {({ handles, activeHandleID, getHandleProps }) => (
{handles.map((handle, i) => { - const value = i === 0 ? this.Document._sliderMinThumb : this.Document._sliderMaxThumb; + const value = i === 0 ? defaultValues[0] : defaultValues[1]; return (
(VideoDocument) { +export class VideoBox extends ViewBoxAnnotatableComponent(VideoDocument) { static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index ea5d601ec..55ad7eb0f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -14,7 +14,7 @@ import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { SelectionManager } from "../../util/SelectionManager"; -import { DocAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; @@ -33,7 +33,7 @@ type WebDocument = makeInterface<[typeof documentSchema]>; const WebDocument = makeInterface(documentSchema); @observer -export class WebBox extends DocAnnotatableComponent(WebDocument) { +export class WebBox extends ViewBoxAnnotatableComponent(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } @observable private collapsed: boolean = true; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index f93c1fa97..c49e6512a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,7 +23,7 @@ import Annotation from "./Annotation"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch } from "../../util/UndoManager"; -import { DocAnnotatableComponent } from "../DocComponent"; +import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentType } from "../../documents/DocumentTypes"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { DocumentDecorations } from "../DocumentDecorations"; @@ -79,7 +79,7 @@ interface IViewerProps { * Handles rendering and virtualization of the pdf */ @observer -export class PDFViewer extends DocAnnotatableComponent(PdfDocument) { +export class PDFViewer extends ViewBoxAnnotatableComponent(PdfDocument) { static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index bed181ab9..dd0cbf929 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -12,7 +12,7 @@ import { Cast, NumCast } from "../../../new_fields/Types"; import { emptyFunction, emptyPath, returnFalse, returnTrue } from "../../../Utils"; import { Transform } from "../../util/Transform"; import { CollectionViewType } from '../collections/CollectionView'; -import { DocExtendableComponent } from '../DocComponent'; +import { ViewBoxBaseComponent } from '../DocComponent'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./PresElementBox.scss"; @@ -44,7 +44,7 @@ const PresDocument = makeInterface(presSchema, documentSchema); * It involves some functionality for its buttons and options. */ @observer -export class PresElementBox extends DocExtendableComponent(PresDocument) { +export class PresElementBox extends ViewBoxBaseComponent(PresDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); } _heightDisposer: IReactionDisposer | undefined; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index a9c97fc19..934ba0f60 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -645,11 +645,12 @@ export namespace Doc { Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc)); (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue)); } - if (templateCaptionValue instanceof RichTextField && (templateCaptionValue.Text || templateCaptionValue.Data.toString().includes("dashField"))) { - templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`, { this: Doc.name }); + // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc + if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) { + templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`); } - if (templateFieldValue instanceof RichTextField && (templateFieldValue.Text || templateFieldValue.Data.toString().includes("dashField"))) { - templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`, { this: Doc.name }); + if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) { + templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`); } // get the layout string that the template uses to specify its layout diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index a5a81f4a4..5cf0e0cc3 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -19,6 +19,10 @@ export class RichTextField extends ObjectField { this.Text = text; } + Empty() { + return !(this.Text || this.Data.toString().includes("dashField")); + } + [Copy]() { return new RichTextField(this.Data, this.Text); } -- cgit v1.2.3-70-g09d2 From 5e93a7535b1d210bc20c57ea656257800bd4690b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 10 Apr 2020 22:16:01 -0400 Subject: fixed focus on text boxes inside a template --- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 503df10c2..ec2b2749c 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -249,9 +249,10 @@ export class MarqueeView extends React.Component { - if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && + if ( + Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - this.setPreviewCursor(e.clientX, e.clientY, false); + !(e.nativeEvent as any).formattedHandled && this.setPreviewCursor(e.clientX, e.clientY, false); // let the DocumentView stopPropagation of this event when it selects this document } else { // why do we get a click event when the cursor have moved a big distance? // let's cut it off here so no one else has to deal with it. -- cgit v1.2.3-70-g09d2 From 7afce5c0ba803b66f3b8c6722f25604290abc1c1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 11 Apr 2020 15:59:14 -0400 Subject: fixed deiconifying things. fixed interactions with backgrounds to allow their content to be active. added overflow flag for collection freeforms. --- src/client/views/DocComponent.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++--- .../views/collections/collectionFreeForm/MarqueeView.tsx | 10 ++++++++-- src/new_fields/documentSchemas.ts | 1 + src/server/authentication/models/current_user_utils.ts | 4 ++-- 6 files changed, 16 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index c97e46f91..b1bd4191c 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -115,7 +115,7 @@ export function ViewBoxAnnotatableComponent

this.props.whenActiveChanged(this._isChildActive = isActive)); active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) && ((this.props.Document.forceActive && this.props.rootSelected(outsideReaction)) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) - annotationsActive = (outsideReaction?: boolean) => (InkingControl.Instance.selectedTool !== InkTool.None || + annotationsActive = (outsideReaction?: boolean) => (InkingControl.Instance.selectedTool !== InkTool.None || (this.props.Document.isBackground && this.props.active()) || (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) } return Component; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 28aaf0c57..d77ef812f 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -773,7 +773,7 @@ export class DockedFrameRenderer extends React.Component { return CollectionDockingView.AddRightSplit(doc, libraryPath); } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); - } else { + } else {// if (location === "inPlace") { return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b5bcc0cc2..e4e1a543a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -67,7 +67,7 @@ export const panZoomSchema = createSchema({ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); export type collectionFreeformViewProps = { - forceScaling?:boolean; // whether to force scaling of content (needed by ImageBox) + forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) }; @observer @@ -138,7 +138,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u @undoBatch @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { - if (this.props.Document.isBackground) return false; + // if (this.props.Document.isBackground) return false; const xf = this.getTransform(); const xfo = this.getTransformOverlay(); const [xp, yp] = xf.transformPoint(de.x, de.y); @@ -164,7 +164,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u const nh = NumCast(layoutDoc._nativeHeight); layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300; } - this.bringToFront(d); + d.isBackground === undefined && this.bringToFront(d); })); (de.complete.docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(de.complete.docDragData.droppedDocuments); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index ec2b2749c..96a7c4fc0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -586,13 +586,19 @@ export class MarqueeView extends React.Component + return

{/* */}
; } render() { - return
e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> + return
e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} {this.props.children}
; diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index a640862f3..b11941f40 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -33,6 +33,7 @@ export const documentSchema = createSchema({ color: "string", // foreground color of document backgroundColor: "string", // background color of document opacity: "number", // opacity of document + overflow: "string", // sets overflow behvavior for CollectionFreeForm views creationDate: DateField, // when the document was created links: listSpec(Doc), // computed (readonly) list of links associated with this document onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b4dc17178..5e0e9ad67 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -57,9 +57,9 @@ export class CurrentUserUtils { 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(this)") })); + 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(this)") })); + 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 }); } -- cgit v1.2.3-70-g09d2 From 46581d75b4171a92f0ba61e3ce55a99499864820 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 11 Apr 2020 20:55:46 -0400 Subject: added tap-to-zoom --- .../collectionFreeForm/CollectionFreeFormView.tsx | 49 +++++++++++++++------- src/client/views/nodes/DocumentView.tsx | 25 +++++++---- 2 files changed, 51 insertions(+), 23 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 e4e1a543a..ed6286675 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -74,6 +74,8 @@ export type collectionFreeformViewProps = { export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, undefined as any as collectionFreeformViewProps) { private _lastX: number = 0; private _lastY: number = 0; + private _downX: number = 0; + private _downY: number = 0; private _inkToTextStartX: number | undefined; private _inkToTextStartY: number | undefined; private _wordPalette: Map = new Map(); @@ -322,17 +324,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); - // if physically using a pen or we're in pen or highlighter mode - // if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { - // e.stopPropagation(); - // e.preventDefault(); - // const point = this.getTransform().transformPoint(e.pageX, e.pageY); - // this._points.push({ X: point[0], Y: point[1] }); - // } // if not using a pen and in no ink mode if (InkingControl.Instance.selectedTool === InkTool.None) { - this._lastX = e.pageX; - this._lastY = e.pageY; + this._downX = this._lastX = e.pageX; + this._downY = this._lastY = e.pageY; } // eraser plus anything else mode else { @@ -492,10 +487,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u } } + _lastTap = 0; + @action onPointerUp = (e: PointerEvent): void => { if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return; + if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { + if (Date.now() - this._lastTap < 300) { + const docpt = this.getTransform().transformPoint(e.clientX, e.clientY); + this.scaleAtPt(docpt, NumCast(this.layoutDoc.targetScale, NumCast(this.layoutDoc.scale))); + } + this._lastTap = Date.now(); + } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this.removeMoveListeners(); @@ -730,6 +734,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u e.stopPropagation(); this.zoom(e.clientX, e.clientY, e.deltaY); } + this.props.Document.targetScale = NumCast(this.props.Document.scale); } @action @@ -759,6 +764,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u } } + scaleAtPt(docpt: number[], scale: number) { + const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]); + this.Document.panTransformType = "Ease"; + this.layoutDoc.scale = scale; + const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]); + const scrDelta = { x: screenXY[0] - newScreenXY[0], y: screenXY[1] - newScreenXY[1] }; + const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y); + this.layoutDoc._panX = NumCast(this.layoutDoc._panX) - newpan[0]; + this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1]; + } + focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => { const state = HistoryUtil.getState(); @@ -797,12 +813,17 @@ 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 (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) { - if (!doc.z) this.setPan(newPanX, newPanY, "Ease"); // 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 + if (!willZoom) { + Doc.BrushDoc(this.props.Document); + !doc.z && this.scaleAtPt([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"); // 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 + } + Doc.BrushDoc(this.props.Document); + this.props.focus(this.props.Document); + willZoom && this.setScaleToZoom(layoutdoc, scale); } - Doc.BrushDoc(this.props.Document); - this.props.focus(this.props.Document); - willZoom && this.setScaleToZoom(layoutdoc, scale); Doc.linkFollowHighlight(doc); afterFocus && setTimeout(() => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7a47ad10a..1d1f5d3fd 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -286,7 +286,7 @@ export class DocumentView extends DocComponent(Docu (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { let stopPropagate = true; let preventDefault = true; - this.props.bringToFront(this.props.Document); + this.props.Document.isBackground === undefined && this.props.bringToFront(this.props.Document); if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click const fullScreenAlias = Doc.MakeAlias(this.props.Document); if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { @@ -313,6 +313,7 @@ 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 { + this.props.focus(this.props.Document, false); SelectionManager.SelectDoc(this, e.ctrlKey); } preventDefault = false; @@ -547,16 +548,21 @@ export class DocumentView extends DocComponent(Docu }); batch.end(); } - static createCustomView = (doc: Doc, creator: (documents: Array, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { + static findTemplate(templateName: string, type: string, signature: string) { + let docLayoutTemplate: Opt; const iconViews = DocListCast(Cast(Doc.UserDoc().iconViews, Doc, null)?.data); const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data); const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data); const allTemplates = iconViews.concat(templBtns).concat(noteTypes).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); - const templateName = templateSignature.replace(/\(.*\)/, ""); // 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) === doc.type + "_" + templateName && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === type + "_" + templateName && (docLayoutTemplate = tempDoc)); !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) => { + const templateName = templateSignature.replace(/\(.*\)/, ""); + docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, doc.type, templateSignature); const customName = "layout_" + templateSignature; const _width = NumCast(doc._width); @@ -661,8 +667,9 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action - makeBackground = (): void => { - this.Document.isBackground = !this.Document.isBackground; + toggleBackground = (temporary: boolean): void => { + this.Document.overflow = temporary; + this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true); this.Document.isBackground && this.props.bringToFront(this.Document, true); } @@ -702,7 +709,7 @@ export class DocumentView extends DocComponent(Docu 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: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" }); + layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: (e) => this.toggleBackground(true), icon: this.Document.lockedPosition ? "unlock" : "lock" }); layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); @@ -1100,7 +1107,7 @@ export class DocumentView extends DocComponent(Docu ; } @computed get ignorePointerEvents() { - return (this.Document.isBackground && !this.isSelected()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); + return (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @observable _animate = 0; @@ -1146,12 +1153,12 @@ export class DocumentView extends DocComponent(Docu background: finalColor, opacity: this.Document.opacity }}> - {this.Document.isBackground ?
: (null)} {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <> {this.innards}
: this.innards} + {this.Document.isBackground !== undefined || this.isSelected(false) ?
this.toggleBackground(false)}>
: (null)}
; { this._showKPQuery ? : undefined; } } -- cgit v1.2.3-70-g09d2 From 952318fb56cce16c428a5b41afe27a0ee4d20589 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 11 Apr 2020 21:50:07 -0400 Subject: fixed double click on freeformviews. changed signature for DocumentHierarchyFromJson() to have an 'all' parameter --- src/client/apis/youtube/YoutubeBox.tsx | 4 +-- src/client/cognitive_services/CognitiveServices.ts | 2 +- src/client/documents/Documents.ts | 33 +++++++++++++--------- .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/Import & Export/ImageUtils.ts | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++++++---- src/client/views/nodes/ImageBox.tsx | 2 +- 7 files changed, 35 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 4b4145fcc..4e990537f 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -156,14 +156,14 @@ export class YoutubeBox extends React.Component { @action processVideoDetails = (videoDetails: any[]) => { this.videoDetails = videoDetails; - this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp"); + this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp", undefined, false); } /** * The function that stores the search results in the props document. */ backUpSearchResults = (videos: any[]) => { - this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp"); + this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp", undefined, false); } /** diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 3f3726621..e464aff55 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -192,7 +192,7 @@ export namespace CognitiveServices { let results = await ExecuteQuery(Service.Handwriting, Manager, inkData); if (results) { results.recognitionUnits && (results = results.recognitionUnits); - target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); + target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis", undefined, false); const recognizedText = results.map((item: any) => item.recognizedText); const recognizedObjects = results.map((item: any) => item.recognizedObject); const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b5c6dc06a..30f72a6b2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -116,7 +116,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildren?: boolean; - "onClick-rawScript"?:string; // onClick script in raw text form + "onClick-rawScript"?: string; // onClick script in raw text form _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views schemaColumns?: List; dockingConfig?: string; @@ -405,7 +405,7 @@ export namespace Docs { const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); - Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto); + Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto, false); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { console.log(errors); @@ -732,19 +732,21 @@ export namespace Docs { * @param input for convenience and flexibility, either a valid JSON string to be parsed, * or the result of any JSON.parse() call. * @param title an optional title to give to the highest parent document in the hierarchy + * @param appendToTarget -??? + * @param all whether fields should be converted even if they contain no data */ - export function DocumentHierarchyFromJson(input: any, title?: string, appendToTarget?: Doc): Opt { + export function DocumentHierarchyFromJson(input: any, title: string, appendToTarget: Opt, all?:boolean): Opt { if (input === undefined || input === null || ![...primitives, "object"].includes(typeof input)) { return undefined; } input = JSON.parse(typeof input === "string" ? input : JSON.stringify(input)); - let converted: Doc; + let converted: Opt; if (typeof input === "object" && !(input instanceof Array)) { - converted = convertObject(input, title, appendToTarget); + converted = convertObject(input, title, appendToTarget, all); } else { (converted = new Doc).json = toField(input); } - title && (converted.title = title); + title && converted && (converted.title = title); return converted; } @@ -755,12 +757,15 @@ export namespace Docs { * @returns the object mapped from JSON to field values, where each mapping * might involve arbitrary recursion (since toField might itself call convertObject) */ - const convertObject = (object: any, title?: string, target?: Doc): Doc => { - const resolved = target ?? new Doc; - let result: Opt; - Object.keys(object).map(key => (result = toField(object[key], key)) && (resolved[key] = result)); - title && !resolved.title && (resolved.title = title); - return resolved; + const convertObject = (object: any, title?: string, target?: Doc, all?:boolean): Opt => { + const objkeys = Object.keys(object); + if (objkeys.length || all) { + const resolved = target ?? new Doc; + let result: Opt; + Object.keys(object).map(key => (result = toField(object[key], key)) && (resolved[key] = result)); + title && !resolved.title && (resolved.title = title); + return resolved; + } }; /** @@ -778,7 +783,7 @@ export namespace Docs { }; - const toField = (data: any, title?: string): Opt => { + const toField = (data: any, title?: string, all?:boolean): Opt => { if (data === null || data === undefined) { return undefined; } @@ -786,7 +791,7 @@ export namespace Docs { return data; } if (typeof data === "object") { - return data instanceof Array ? convertList(data) : convertObject(data, title); + return data instanceof Array ? convertList(data) : convertObject(data, title, undefined, all); } throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`); }; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 3d8bcbab7..01a0eddf8 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -126,7 +126,7 @@ export default class DirectoryImportBox extends React.Component const document = await Docs.Get.DocumentFromType(type, path, { _width: 300, title: name }); const { data, error } = exifData; if (document) { - Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data); + Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data, "", undefined, false); docs.push(document); } })); diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index ab8c73d15..135454a33 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -20,7 +20,7 @@ export namespace ImageUtils { nativeHeight, exifData: { error, data } } = await Networking.PostToServer("/inspectImage", { source }); - document.exif = error || Docs.Get.DocumentHierarchyFromJson(data); + document.exif = error || Docs.Get.DocumentHierarchyFromJson(data, "", undefined, false); const proto = Doc.GetProto(document); proto["data-nativeWidth"] = nativeWidth; proto["data-nativeHeight"] = nativeHeight; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ed6286675..f27b37e2c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -493,17 +493,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u onPointerUp = (e: PointerEvent): void => { if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + this.removeMoveListeners(); + this.removeEndListeners(); + } + + onClick = (e: React.MouseEvent) => { if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { if (Date.now() - this._lastTap < 300) { const docpt = this.getTransform().transformPoint(e.clientX, e.clientY); this.scaleAtPt(docpt, NumCast(this.layoutDoc.targetScale, NumCast(this.layoutDoc.scale))); + e.stopPropagation(); + e.preventDefault(); } this._lastTap = Date.now(); } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this.removeMoveListeners(); - this.removeEndListeners(); } @action @@ -1129,7 +1134,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document return
{ const converter = (results: any) => { const faceDocs = new List(); - results.reduce((face: CognitiveServices.Image.Face, faceDocs: List) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List()); + results.reduce((face: CognitiveServices.Image.Face, faceDocs: List) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`, undefined, false)!), new List()); return faceDocs; }; this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter); -- cgit v1.2.3-70-g09d2 From bd70f0e07e7d578c4ecb544f0e5b1ad16132fc05 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 12 Apr 2020 00:21:38 -0400 Subject: added arrowkey nudging of freeform view. fixed background overflow. --- src/client/documents/Documents.ts | 8 +-- src/client/util/DragManager.ts | 1 - src/client/views/PreviewCursor.tsx | 15 ++++- .../collectionFreeForm/CollectionFreeFormView.scss | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 64 ++++++++++++---------- .../collections/collectionFreeForm/MarqueeView.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 8 +-- 7 files changed, 60 insertions(+), 43 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 30f72a6b2..e3e3d895b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -405,7 +405,7 @@ export namespace Docs { const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); - Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto, false); + Docs.Get.DocumentHierarchyFromJson(device, "", deviceProto, false); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { console.log(errors); @@ -735,7 +735,7 @@ export namespace Docs { * @param appendToTarget -??? * @param all whether fields should be converted even if they contain no data */ - export function DocumentHierarchyFromJson(input: any, title: string, appendToTarget: Opt, all?:boolean): Opt { + export function DocumentHierarchyFromJson(input: any, title: string, appendToTarget: Opt, all?: boolean): Opt { if (input === undefined || input === null || ![...primitives, "object"].includes(typeof input)) { return undefined; } @@ -757,7 +757,7 @@ export namespace Docs { * @returns the object mapped from JSON to field values, where each mapping * might involve arbitrary recursion (since toField might itself call convertObject) */ - const convertObject = (object: any, title?: string, target?: Doc, all?:boolean): Opt => { + const convertObject = (object: any, title?: string, target?: Doc, all?: boolean): Opt => { const objkeys = Object.keys(object); if (objkeys.length || all) { const resolved = target ?? new Doc; @@ -783,7 +783,7 @@ export namespace Docs { }; - const toField = (data: any, title?: string, all?:boolean): Opt => { + const toField = (data: any, title?: string, all?: boolean): Opt => { if (data === null || data === undefined) { return undefined; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index b28bac125..3e9a5b63a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -419,7 +419,6 @@ export namespace DragManager { if (target) { const complete = new DragCompleteEvent(false, dragData); finishDrag?.(complete); - console.log(complete.aborted); target.dispatchEvent( new CustomEvent("dashOnDrop", { bubbles: true, diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index c011adb20..fad2f2f0a 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -13,6 +13,7 @@ export class PreviewCursor extends React.Component<{}> { static _getTransform: () => Transform; static _addLiveTextDoc: (doc: Doc) => void; static _addDocument: (doc: Doc) => boolean; + static _nudge: (x: number, y: number) => void; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; constructor(props: any) { @@ -85,9 +86,17 @@ export class PreviewCursor extends React.Component<{}> { !e.key.startsWith("Arrow") && !e.defaultPrevented) { if ((!e.ctrlKey || (e.keyCode >= 48 && e.keyCode <= 57)) && !e.metaKey) {// /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) { - PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e); + PreviewCursor.Visible && PreviewCursor._onKeyPress?.(e); PreviewCursor.Visible = false; } + } else if (e.key === "ArrowRight") { + PreviewCursor._nudge?.(1, 0); + } else if (e.key === "ArrowLeft") { + PreviewCursor._nudge?.(-1, 0); + } else if (e.key === "ArrowUp") { + PreviewCursor._nudge?.(0, 1); + } else if (e.key === "ArrowDown") { + PreviewCursor._nudge?.(0, -1); } } @@ -101,12 +110,14 @@ export class PreviewCursor extends React.Component<{}> { onKeyPress: (e: KeyboardEvent) => void, addLiveText: (doc: Doc) => void, getTransform: () => Transform, - addDocument: (doc: Doc) => boolean) { + addDocument: (doc: Doc) => boolean, + nudge: (nudgeX: number, nudgeY: number) => void) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; this._addLiveTextDoc = addLiveText; this._getTransform = getTransform; this._addDocument = addDocument; + this._nudge = nudge; this.Visible = true; } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 730392ab5..e1516b468 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -12,7 +12,7 @@ } .collectionfreeformview-ease { - transition: transform 1s; + transition: transform 500ms; } .collectionfreeformview-none { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f27b37e2c..7dca19073 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -518,31 +518,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u PDFMenu.Instance.fadeOut(true); const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - let x = (this.Document._panX || 0) - dx; - let y = (this.Document._panY || 0) - dy; - if (!this.isAnnotationOverlay) { - // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds - const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); - const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc)).map(doc => this.childDataProvider(doc)); - if (measuredDocs.length) { - const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) => // computes range of content - ({ - xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) }, - yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) } - }) - , { - xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, - yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE } - }); - - const panelDim = [this.props.PanelWidth() / this.zoomScaling(), this.props.PanelHeight() / this.zoomScaling()]; - if (ranges.xrange.min > (this.panX() + panelDim[0] / 2)) x = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds - if (ranges.xrange.max < (this.panX() - panelDim[0] / 2)) x = ranges.xrange.min - panelDim[0] / 2; - if (ranges.yrange.min > (this.panY() + panelDim[1] / 2)) y = ranges.yrange.max + panelDim[1] / 2; - if (ranges.yrange.max < (this.panY() - panelDim[1] / 2)) y = ranges.yrange.min - panelDim[1] / 2; - } - } - this.setPan(x, y); + this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true); this._lastX = e.clientX; this._lastY = e.clientY; } @@ -743,7 +719,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u } @action - setPan(panX: number, panY: number, panType: string = "None") { + setPan(panX: number, panY: number, panType: string = "None", clamp: boolean = false) { + if (!this.isAnnotationOverlay && clamp) { + // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds + const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); + const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc)).map(doc => this.childDataProvider(doc)); + if (measuredDocs.length) { + const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) => // computes range of content + ({ + xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) }, + yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) } + }) + , { + xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, + yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE } + }); + + const panelDim = [this.props.PanelWidth() / this.zoomScaling(), this.props.PanelHeight() / this.zoomScaling()]; + if (ranges.xrange.min >= (panX + panelDim[0] / 2)) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds + else if (ranges.xrange.max <= (panX - panelDim[0] / 2)) panX = ranges.xrange.min - panelDim[0] / 2; + if (ranges.yrange.min >= (panY + panelDim[1] / 2)) panY = ranges.yrange.max + panelDim[1] / 2; + else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2; + } + } if (!this.Document.lockedTransform || this.Document.inOverlay) { this.Document.panTransformType = panType; const scale = this.getLocalTransform().inverse().Scale; @@ -823,7 +821,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u !doc.z && this.scaleAtPt([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"); // 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 + 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 } Doc.BrushDoc(this.props.Document); this.props.focus(this.props.Document); @@ -838,7 +836,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u this.Document.scale = savedState.s; this.Document.panTransformType = savedState.pt; } - }, 1000); + }, 500); } } @@ -1104,8 +1102,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u {this.props.Document.title?.toString()}
; } + + _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); + }); @computed get marqueeView() { - return diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 96a7c4fc0..5a6c8573b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -31,6 +31,7 @@ interface MarqueeViewProps { addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; isAnnotationOverlay?: boolean; + nudge:(x:number, y:number) => void; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } @@ -46,7 +47,7 @@ export class MarqueeView extends React.Component(Docu } static createCustomView = (doc: Doc, creator: (documents: Array, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { const templateName = templateSignature.replace(/\(.*\)/, ""); - docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, doc.type, templateSignature); + docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, StrCast(doc.type), templateSignature); const customName = "layout_" + templateSignature; const _width = NumCast(doc._width); @@ -668,7 +668,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action toggleBackground = (temporary: boolean): void => { - this.Document.overflow = temporary; + this.Document.overflow = temporary ? "visible" : "hidden"; this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true); this.Document.isBackground && this.props.bringToFront(this.Document, true); } @@ -709,7 +709,7 @@ export class DocumentView extends DocComponent(Docu 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(true), icon: this.Document.lockedPosition ? "unlock" : "lock" }); + layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: (e) => this.toggleBackground(false), icon: this.Document.lockedPosition ? "unlock" : "lock" }); layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); @@ -1158,7 +1158,7 @@ export class DocumentView extends DocComponent(Docu
: this.innards} - {this.Document.isBackground !== undefined || this.isSelected(false) ?
this.toggleBackground(false)}>
: (null)} + {this.Document.isBackground !== undefined || this.isSelected(false) ?
this.toggleBackground(true)}>
: (null)}
; { this._showKPQuery ? : undefined; } } -- cgit v1.2.3-70-g09d2 From 09793f930a79e9d07a2060578a851ac617e94bc7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 12 Apr 2020 11:26:07 -0400 Subject: changes to treeView titles - show alias/proto. setInPlace --- src/client/views/collections/CollectionTreeView.tsx | 16 ++++++++++------ src/new_fields/Doc.ts | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index fd84c6498..23fb259fc 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -182,14 +182,17 @@ class TreeView extends React.Component { GetValue={() => StrCast(this.props.document[key])} SetValue={undoBatch((value: string) => { Doc.SetInPlace(this.props.document, key, value, false) || true; - this.props.document.editTitle = undefined; + 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]; - this.props.document.editTitle = undefined; - doc.editTitle = true; + 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={() => { @@ -306,7 +309,7 @@ class TreeView extends React.Component { const rows: JSX.Element[] = []; for (const key of Object.keys(ids).slice().sort()) { - if (this.props.ignoreFields?.includes(key)) continue; + if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; const contents = doc[key]; let contentElement: (JSX.Element | null)[] | JSX.Element = []; @@ -423,7 +426,7 @@ class TreeView extends React.Component { @computed get renderTitle() { const onItemDown = SetupDrag(this._tref, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId[Id], true); - const editTitle = ScriptField.MakeFunction("this.editTitle=true", { this: Doc.name }); + const editTitle = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)"); const headerElements = ( { style={{ background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0", 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" }} > - {this.props.document.editTitle ? + {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? this.editableView("title") : Date: Sun, 12 Apr 2020 19:23:21 -0400 Subject: added nudging of documents --- src/client/views/GlobalKeyHandler.ts | 35 +++++++++++++++++----- src/client/views/collections/CollectionSubView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 ++++ src/client/views/nodes/DocumentView.tsx | 1 + 4 files changed, 34 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 52801b570..d01f3f1e5 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -8,7 +8,7 @@ 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 } from "../../new_fields/Types"; +import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; import { ScriptField } from "../../new_fields/ScriptField"; import { InkingControl } from "./InkingControl"; import { InkTool } from "../../new_fields/InkField"; @@ -89,13 +89,20 @@ export default class KeyManager { return { stopPropagation: false, preventDefault: false }; } } - UndoManager.RunInBatch(() => { - SelectionManager.SelectedDocuments().map(docView => { - const doc = docView.props.Document; - const remove = docView.props.removeDocument; - remove && remove(doc); - }); - }, "delete"); + UndoManager.RunInBatch(() => + SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument?.(dv.props.Document)), "delete"); + break; + case "arrowleft": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left"); + break; + case "arrowright": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(1, 0)), "nudge right"); + break; + case "arrowup": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -1)), "nudge up"); + break; + case "arrowdown": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 1)), "nudge down"); break; } @@ -114,6 +121,18 @@ export default class KeyManager { // DictationManager.Controls.listen({ useOverlay: true, tryExecute: true }); // stopPropagation = true; // preventDefault = true; + case "arrowleft": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-10, 0)), "nudge left"); + break; + case "arrowright": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(10, 0)), "nudge right"); + break; + case "arrowup": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -10)), "nudge up"); + break; + case "arrowdown": + UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 10)), "nudge down"); + break; } return { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 0c00a1f22..6b7c3de56 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -381,7 +381,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: alert(`Upload failed: ${result.message}`); return; } - const full = { ...options, _width: 300, title: name }; + const full = { ...options, _width: 400, title: name }; const pathname = Utils.prepend(result.accessPaths.agnostic.client); const doc = await Docs.Get.DocumentFromType(type, pathname, full); if (!doc) { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f9f5f449c..3503af442 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -59,6 +59,10 @@ export class CollectionFreeFormDocumentView extends DocComponent { + this.layoutDoc.x = NumCast(this.layoutDoc.x) + x; + this.layoutDoc.y = NumCast(this.layoutDoc.y) + y; + } contentScaling = () => this.nativeWidth > 0 && !this.props.fitToBox && !this.freezeDimensions ? this.width / this.nativeWidth : 1; panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth?.()); @@ -93,6 +97,7 @@ export class CollectionFreeFormDocumentView extends DocComponent void; addDocument?: (doc: Doc) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean; -- cgit v1.2.3-70-g09d2 From 290feed8a7c2ae700bac0d720874abc511e6cd36 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 12 Apr 2020 20:14:31 -0400 Subject: fixed nudging stoppropagation stuff. fixed inPlace addDocTab for non freeform views --- src/client/views/PreviewCursor.tsx | 22 ++++++++++++++-------- .../views/collections/CollectionStackingView.tsx | 9 ++++++++- .../CollectionMulticolumnView.tsx | 9 +++++++++ .../CollectionMultirowView.tsx | 10 ++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index fad2f2f0a..ecebadd2a 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -89,14 +89,20 @@ export class PreviewCursor extends React.Component<{}> { PreviewCursor.Visible && PreviewCursor._onKeyPress?.(e); PreviewCursor.Visible = false; } - } else if (e.key === "ArrowRight") { - PreviewCursor._nudge?.(1, 0); - } else if (e.key === "ArrowLeft") { - PreviewCursor._nudge?.(-1, 0); - } else if (e.key === "ArrowUp") { - PreviewCursor._nudge?.(0, 1); - } else if (e.key === "ArrowDown") { - PreviewCursor._nudge?.(0, -1); + } else if (PreviewCursor.Visible) { + if (e.key === "ArrowRight") { + PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0); + e.stopPropagation(); + } else if (e.key === "ArrowLeft") { + PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0); + e.stopPropagation(); + } else if (e.key === "ArrowUp") { + PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)); + e.stopPropagation(); + } else if (e.key === "ArrowDown") { + PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)); + e.stopPropagation(); + } } } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index da53888fc..dd84c4d6e 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -153,6 +153,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } @computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); } + addDocTab = (doc: Doc, where: string) => { + if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { + this.dataDoc[this.props.fieldKey] = new List([doc]); + return true; + } + return this.props.addDocTab(doc, where); + } getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.()); const height = () => this.getDocHeight(doc); @@ -181,7 +188,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { removeDocument={this.props.removeDocument} active={this.props.active} whenActiveChanged={this.props.whenActiveChanged} - addDocTab={this.props.addDocTab} + addDocTab={this.addDocTab} pinToPres={this.props.pinToPres}> ; } diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 7e511ae34..0e1cc2010 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -204,6 +204,14 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + + addDocTab = (doc: Doc, where: string) => { + if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { + this.dataDoc[this.props.fieldKey] = new List([doc]); + return true; + } + return this.props.addDocTab(doc, where); + } getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ; const MultirowDocument = makeInterface(documentSchema); @@ -203,6 +204,14 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + + addDocTab = (doc: Doc, where: string) => { + if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { + this.dataDoc[this.props.fieldKey] = new List([doc]); + return true; + } + return this.props.addDocTab(doc, where); + } getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return Date: Sun, 12 Apr 2020 21:36:26 -0700 Subject: rough pass at collection map view --- package-lock.json | 68 +++++++++++++++----- package.json | 6 +- src/client/Network.ts | 13 ++++ src/client/cognitive_services/CognitiveServices.ts | 1 - src/client/documents/Documents.ts | 9 ++- src/client/views/Main.tsx | 1 - .../views/collections/CollectionMapView.scss | 4 ++ src/client/views/collections/CollectionMapView.tsx | 72 ++++++++++++++++++++++ src/client/views/collections/CollectionView.tsx | 10 ++- src/new_fields/Types.ts | 1 + webpack.config.js | 55 ++++++++++------- 11 files changed, 195 insertions(+), 45 deletions(-) create mode 100644 src/client/views/collections/CollectionMapView.scss create mode 100644 src/client/views/collections/CollectionMapView.tsx (limited to 'src/client/views/collections') diff --git a/package-lock.json b/package-lock.json index 1949a8a5c..f40dc7b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -584,6 +584,20 @@ "@types/node": "*" } }, + "@types/google-maps-react": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/google-maps-react/-/google-maps-react-2.0.4.tgz", + "integrity": "sha512-8EGr84L6ozODnfjWN9xVUFIxFedBbZinuDe5lBEJ757yh/lZqoKkqxrYLqvrLksDRVVwQYwWdFvfEASujmk36A==", + "requires": { + "@types/googlemaps": "*", + "@types/react": "*" + } + }, + "@types/googlemaps": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.3.tgz", + "integrity": "sha512-L8O9HAVFZj0TuiS8h5ORthiMsrrhjxTC8XUusp5k47oXCst4VTm+qWKvrAvmYMybZVokbp4Udco1mNwJrTNZPQ==" + }, "@types/isstream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@types/isstream/-/isstream-0.1.0.tgz", @@ -750,7 +764,7 @@ }, "@types/passport": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz", "integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==", "requires": { "@types/express": "*" @@ -5615,7 +5629,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5633,11 +5648,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5650,15 +5667,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5761,7 +5781,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5771,6 +5792,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5783,17 +5805,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5810,6 +5835,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5890,7 +5916,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5900,6 +5927,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5975,7 +6003,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6005,6 +6034,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6022,6 +6052,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6060,11 +6091,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -6344,6 +6377,11 @@ } } }, + "google-maps-react": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/google-maps-react/-/google-maps-react-2.0.2.tgz", + "integrity": "sha512-6cYauGwt22haDUrWxKQ6yoNOqjiuxHo8YYcmb+aBvNICokdXmZOUB6Ah4vD5VexMVlrwP2PFqA/D8sHpEB52KA==" + }, "google-p12-pem": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.1.tgz", @@ -14376,7 +14414,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -15990,7 +16028,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -18303,7 +18341,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/package.json b/package.json index 440646c48..c742857ad 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "scripts": { "start-release": "cross-env RELEASE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", - "start": "cross-env HANDWRITING='61088486d76c4b12ba578775a5f55422' NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", + "start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", "debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --inspect -- src/server/index.ts", "build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production", "test": "mocha -r ts-node/register test/**/*.ts", @@ -83,6 +83,7 @@ "@types/express-validator": "^3.0.0", "@types/formidable": "^1.0.31", "@types/gapi": "0.0.39", + "@types/google-maps-react": "^2.0.4", "@types/jquery": "^3.3.31", "@types/jquery-awesome-cursor": "^0.3.0", "@types/jsonwebtoken": "^8.3.7", @@ -162,6 +163,7 @@ "formidable": "^1.2.1", "golden-layout": "^1.5.9", "google-auth-library": "^4.2.4", + "google-maps-react": "^2.0.2", "googleapis": "^40.0.0", "googlephotos": "^0.2.5", "howler": "^2.1.3", @@ -258,4 +260,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} +} \ No newline at end of file diff --git a/src/client/Network.ts b/src/client/Network.ts index 6982ecf19..bd0e6e61a 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -4,10 +4,23 @@ import { Upload } from "../server/SharedMediaTypes"; export namespace Networking { + const EnvVarCache = new Map(); + export async function FetchFromServer(relativeRoute: string) { return (await fetch(relativeRoute)).text(); } + export async function FetchEnvironmentVariable(varNameLiteral: string) { + let resolved = EnvVarCache.get(varNameLiteral); + if (!resolved) { + resolved = await FetchFromServer(`/environment/${varNameLiteral}`); + if (resolved !== undefined) { + EnvVarCache.set(varNameLiteral, resolved); + } + } + return resolved; + } + export async function PostToServer(relativeRoute: string, body?: any) { const options = { uri: Utils.prepend(relativeRoute), diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 5d83de233..3133bf4b1 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -8,7 +8,6 @@ import { UndoManager } from "../util/UndoManager"; import requestPromise = require("request-promise"); import { List } from "../../new_fields/List"; import { ClientRecommender } from "../ClientRecommender"; -import { ImageBox } from "../views/nodes/ImageBox"; type APIManager = { converter: BodyConverter, requester: RequestExecutor }; type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 703c049cd..43e379125 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -49,6 +49,8 @@ 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"; const requestImageSize = require('../util/request-image-size'); const path = require('path'); @@ -277,8 +279,7 @@ export namespace Docs { }], [DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: data }, - options: {} - }] + }], ]); // All document prototypes are initialized with at least these values @@ -624,6 +625,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id); } + export function MapDocument(documents: Array, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), options); + } + export function CarouselDocument(documents: Array, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Carousel }); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 6d705aa44..b21eb9c8f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,7 +5,6 @@ import * as ReactDOM from 'react-dom'; import * as React from 'react'; import { DocServer } from "../DocServer"; import { AssignAllExtensions } from "../../extensions/General/Extensions"; -process.env.HANDWRITING = "61088486d76c4b12ba578775a5f55422"; AssignAllExtensions(); diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss new file mode 100644 index 000000000..c74433902 --- /dev/null +++ b/src/client/views/collections/CollectionMapView.scss @@ -0,0 +1,4 @@ +.collectionMapView-contents { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx new file mode 100644 index 000000000..49411f61b --- /dev/null +++ b/src/client/views/collections/CollectionMapView.tsx @@ -0,0 +1,72 @@ +import { observer } from "mobx-react"; +import { makeInterface } from "../../../new_fields/Schema"; +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"; + +type MapDocument = makeInterface<[typeof documentSchema]>; +const MapDocument = makeInterface(documentSchema); + +export type LocationData = google.maps.LatLngLiteral & { address?: string }; + +@observer +class CollectionMapView extends CollectionSubView & { google: any }>(MapDocument) { + + 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; + } + return ( +
+ + {childLayoutPairs.map(({ layout }) => { + const location: LocationData = { + lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), + lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) + }; + const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( + { + Document.mapCenterLat = location.lat; + Document.mapCenterLng = location.lng; + }} + icon={{ + size: iconSize, + scaledSize: iconSize, + url: StrCast(Document.mapIconUrl, "https://www.pinclipart.com/picdir/middle/359-3598915_map-marker-icon-location-icon-png-clipart.png") + }} + /> + ); + })} + +
+ ); + } + +} + +declare var process: { + env: { + GOOGLE_MAPS_API_KEY: string; + } +}; + +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 821840e0b..d1dc32829 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye, faEdit } from '@fortawesome/free-regular-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faGlobeAmericas } from '@fortawesome/free-solid-svg-icons'; import { action, observable, computed } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; @@ -44,12 +44,15 @@ import { Docs } from '../../documents/Documents'; 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; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); -library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); +library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faGlobeAmericas, faEllipsisV, faImage, faEye as any, faCopy); export enum CollectionViewType { Invalid = "invalid", @@ -65,6 +68,7 @@ export enum CollectionViewType { Carousel = "carousel", Linear = "linear", Staff = "staff", + Map = "map" } export interface CollectionRenderProps { @@ -170,6 +174,7 @@ export class CollectionView extends Touchable { case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } case CollectionViewType.Time: { return (); } + case CollectionViewType.Map: return (); case CollectionViewType.Freeform: default: { this.props.Document._freeformLayoutEngine = undefined; return (); } } @@ -211,6 +216,7 @@ export class CollectionView extends Touchable { 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) }); diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index 0ca35fab2..aa44cefa0 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -87,6 +87,7 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) export function DateCast(field: FieldResult) { return Cast(field, DateField, null); } + export function ScriptCast(field: FieldResult) { return Cast(field, ScriptField, null); } diff --git a/webpack.config.js b/webpack.config.js index 6a14dfcda..655334ef2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,6 +2,14 @@ var path = require('path'); var webpack = require('webpack'); const CopyWebpackPlugin = require("copy-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); +const env = require('dotenv').config().parsed; +const envKeys = Object.keys(env).reduce((prev, next) => { + if (next.startsWith("DASH_")) { + const resolved = next.replace("DASH_", ""); + prev[`process.env.${resolved}`] = JSON.stringify(env[next]); + } + return prev; +}, {}); module.exports = { mode: 'development', @@ -33,17 +41,18 @@ module.exports = { extensions: ['.js', '.ts', '.tsx'] }, module: { - rules: [ - { + rules: [{ test: [/\.tsx?$/], - use: [ - { loader: 'ts-loader', options: { transpileOnly: true } } - ] + use: [{ + loader: 'ts-loader', + options: { + transpileOnly: true + } + }] }, { test: /\.scss|css$/, - use: [ - { + use: [{ loader: "style-loader" }, { @@ -56,28 +65,30 @@ module.exports = { }, { test: /\.(jpg|png|pdf)$/, - use: [ - { - loader: 'file-loader' - } - ] + use: [{ + loader: 'file-loader' + }] }, { test: /\.(png|jpg|gif)$/i, - use: [ - { - loader: 'url-loader', - options: { - limit: 8192 - } + use: [{ + loader: 'url-loader', + options: { + limit: 8192 } - ] - }] + }] + } + ] }, plugins: [ - new CopyWebpackPlugin([{ from: "deploy", to: path.join(__dirname, "build") }]), + new CopyWebpackPlugin([{ + from: "deploy", + to: path.join(__dirname, "build") + }]), + new webpack.DefinePlugin(envKeys), new ForkTsCheckerWebpackPlugin({ - tslint: true, useTypescriptIncrementalApi: true + tslint: true, + useTypescriptIncrementalApi: true }), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), -- cgit v1.2.3-70-g09d2 From 8143af6ed24b4d3b02e8be306e1dd6fba1e206ce Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 12 Apr 2020 21:41:06 -0700 Subject: clean up --- src/client/views/collections/CollectionMapView.tsx | 3 ++- 1 file changed, 2 insertions(+), 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 49411f61b..6ab152836 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -39,6 +39,7 @@ class CollectionMapView extends CollectionSubView lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) }; const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); + return ( icon={{ size: iconSize, scaledSize: iconSize, - url: StrCast(Document.mapIconUrl, "https://www.pinclipart.com/picdir/middle/359-3598915_map-marker-icon-location-icon-png-clipart.png") + url: StrCast(Document.mapIconUrl, null) }} /> ); -- cgit v1.2.3-70-g09d2 From e3a5dc3ce59b1d8f2dde63395505061b304d64c0 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:03:13 -0700 Subject: completed migration to webpack-transferred environment variables, rather than a server route --- src/Utils.ts | 5 ----- src/client/Network.ts | 13 ------------- src/client/cognitive_services/CognitiveServices.ts | 2 +- src/client/views/collections/CollectionMapView.tsx | 8 +------- src/server/ApiManagers/UtilManager.ts | 13 ------------- webpack.config.js | 3 +-- 6 files changed, 3 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index e3ec10dcd..a8cde0624 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -63,11 +63,6 @@ export namespace Utils { return prepend("/corsProxy/") + encodeURIComponent(url); } - export async function getApiKey(target: string): Promise { - const response = await fetch(prepend(`/environment/${target.toUpperCase()}`)); - return response.text(); - } - export function CopyText(text: string) { const textArea = document.createElement("textarea"); textArea.value = text; diff --git a/src/client/Network.ts b/src/client/Network.ts index bd0e6e61a..6982ecf19 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -4,23 +4,10 @@ import { Upload } from "../server/SharedMediaTypes"; export namespace Networking { - const EnvVarCache = new Map(); - export async function FetchFromServer(relativeRoute: string) { return (await fetch(relativeRoute)).text(); } - export async function FetchEnvironmentVariable(varNameLiteral: string) { - let resolved = EnvVarCache.get(varNameLiteral); - if (!resolved) { - resolved = await FetchFromServer(`/environment/${varNameLiteral}`); - if (resolved !== undefined) { - EnvVarCache.set(varNameLiteral, resolved); - } - } - return resolved; - } - export async function PostToServer(relativeRoute: string, body?: any) { const options = { uri: Utils.prepend(relativeRoute), diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 3133bf4b1..8c63ae906 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -45,7 +45,7 @@ export enum Confidence { export namespace CognitiveServices { const ExecuteQuery = async (service: Service, manager: APIManager, data: D): Promise => { - const apiKey = await Utils.getApiKey(service); + const apiKey = process.env[service.toUpperCase()]; if (!apiKey) { console.log(`No API key found for ${service}: ensure index.ts has access to a .env file in your root directory.`); return undefined; diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 6ab152836..a99d5be50 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -64,10 +64,4 @@ class CollectionMapView extends CollectionSubView } -declare var process: { - env: { - GOOGLE_MAPS_API_KEY: string; - } -}; - -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index ad8119bf4..aec523cd0 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -14,19 +14,6 @@ export default class UtilManager extends ApiManager { protected initialize(register: Registration): void { - register({ - method: Method.GET, - subscription: new RouteSubscriber("environment").add("key"), - secureHandler: ({ req, res }) => { - const { key } = req.params; - const value = process.env[key]; - if (!value) { - console.log(red(`process.env.${key} is not defined.`)); - } - return res.send(value); - } - }); - // register({ // method: Method.POST, // subscription: "/IBMAnalysis", diff --git a/webpack.config.js b/webpack.config.js index 9225093be..c8ef269d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,8 +20,7 @@ const env = require('dotenv').config().parsed; if (env) { plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { if (next.startsWith("DASH_")) { - const resolved = next.replace("DASH_", ""); - prev[`process.env.${resolved}`] = JSON.stringify(env[next]); + prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); } return prev; }, {}))); -- cgit v1.2.3-70-g09d2 From bf622aa5b5cd9d3256ee8e4f26245b9858cccfba Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 13 Apr 2020 00:56:21 -0700 Subject: final env cleanup --- src/client/views/collections/CollectionMapView.tsx | 20 +++++++++++-------- src/server/server_Initialization.ts | 1 - webpack.config.js | 23 ++++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index a99d5be50..b67daeb53 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -6,6 +6,7 @@ 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"; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); @@ -38,8 +39,15 @@ class CollectionMapView extends CollectionSubView lat: NumCast(childLayoutPairs[0].layout.locationLat, 0), lng: NumCast(childLayoutPairs[0].layout.locationLng, 0) }; - const iconSize = new google.maps.Size(NumCast(layout.mapIconWidth, 45), NumCast(layout.mapIconHeight, 45)); - + 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 ( Document.mapCenterLat = location.lat; Document.mapCenterLng = location.lng; }} - icon={{ - size: iconSize, - scaledSize: iconSize, - url: StrCast(Document.mapIconUrl, null) - }} + icon={icon} /> ); })} @@ -64,4 +68,4 @@ class CollectionMapView extends CollectionSubView } -export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS_API_KEY! })(CollectionMapView) as any; \ No newline at end of file +export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any; \ No newline at end of file diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 1150118f7..add607761 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -20,7 +20,6 @@ import * as request from 'request'; import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; import { logPort, } from './ActionUtilities'; -import { Utils } from '../Utils'; import { blue, yellow } from 'colors'; import * as cors from "cors"; diff --git a/webpack.config.js b/webpack.config.js index c8ef269d4..6265883fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,16 +16,23 @@ const plugins = [ new webpack.HotModuleReplacementPlugin(), ]; -const env = require('dotenv').config().parsed; -if (env) { - plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((prev, next) => { - if (next.startsWith("DASH_")) { - prev[`process.env.${next.replace("DASH_", "")}`] = JSON.stringify(env[next]); - } - return prev; - }, {}))); +const dotenv = require('dotenv'); + +function transferEnvironmentVariables() { + const prefix = "_CLIENT_"; + const env = dotenv.config().parsed; + if (env) { + plugins.push(new webpack.DefinePlugin(Object.keys(env).reduce((mapping, envKey) => { + if (envKey.startsWith(prefix)) { + mapping[`process.env.${envKey.replace(prefix, "")}`] = JSON.stringify(env[envKey]); + } + return mapping; + }, {}))); + } } +transferEnvironmentVariables(); + module.exports = { mode: 'development', entry: { -- cgit v1.2.3-70-g09d2 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/DocComponent.tsx | 4 ++-- src/client/views/PreviewCursor.tsx | 14 +++++--------- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 17 +++++++++++------ .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 16 +++++++++------- 8 files changed, 32 insertions(+), 29 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index b1bd4191c..f19f9308a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -53,7 +53,7 @@ export function ViewBoxBaseComponent

(schemaCtor: // key where data is stored @computed get fieldKey() { return this.props.fieldKey; } - active = (outsideReaction?: boolean) => !this.props.Document.isBackground && ((this.props.Document.forceActive && this.props.rootSelected(outsideReaction)) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools + active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; @@ -114,7 +114,7 @@ export function ViewBoxAnnotatableComponent

this.props.whenActiveChanged(this._isChildActive = isActive)); active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) && - ((this.props.Document.forceActive && this.props.rootSelected(outsideReaction)) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) + (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) annotationsActive = (outsideReaction?: boolean) => (InkingControl.Instance.selectedTool !== InkTool.None || (this.props.Document.isBackground && this.props.active()) || (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) } diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index ecebadd2a..92ba13d2f 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -13,7 +13,7 @@ export class PreviewCursor extends React.Component<{}> { static _getTransform: () => Transform; static _addLiveTextDoc: (doc: Doc) => void; static _addDocument: (doc: Doc) => boolean; - static _nudge: (x: number, y: number) => void; + static _nudge: (x: number, y: number) => boolean; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; constructor(props: any) { @@ -91,17 +91,13 @@ export class PreviewCursor extends React.Component<{}> { } } else if (PreviewCursor.Visible) { if (e.key === "ArrowRight") { - PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0); - e.stopPropagation(); + PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation(); } else if (e.key === "ArrowLeft") { - PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0); - e.stopPropagation(); + PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation(); } else if (e.key === "ArrowUp") { - PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)); - e.stopPropagation(); + PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation(); } else if (e.key === "ArrowDown") { - PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)); - e.stopPropagation(); + PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation(); } } } 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; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3503af442..e7cb2c015 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -60,8 +60,8 @@ export class CollectionFreeFormDocumentView extends DocComponent { - this.layoutDoc.x = NumCast(this.layoutDoc.x) + x; - this.layoutDoc.y = NumCast(this.layoutDoc.y) + y; + this.props.Document.x = NumCast(this.props.Document.x) + x; + this.props.Document.y = NumCast(this.props.Document.y) + y; } contentScaling = () => this.nativeWidth > 0 && !this.props.fitToBox && !this.freezeDimensions ? this.width / this.nativeWidth : 1; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3784c5c9c..cc3c39154 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -289,13 +289,15 @@ export class DocumentView extends DocComponent(Docu let preventDefault = true; this.props.Document.isBackground === undefined && this.props.bringToFront(this.props.Document); if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click - const fullScreenAlias = Doc.MakeAlias(this.props.Document); - if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { - fullScreenAlias.layoutKey = "layout_fullScreen"; + if (!(e.nativeEvent as any).formattedHandled) { + const fullScreenAlias = Doc.MakeAlias(this.props.Document); + if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { + fullScreenAlias.layoutKey = "layout_fullScreen"; + } + UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap"); + SelectionManager.DeselectAll(); + Doc.UnBrushDoc(this.props.Document); } - UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap"); - SelectionManager.DeselectAll(); - Doc.UnBrushDoc(this.props.Document); } else if (this.onClickHandler?.script) { SelectionManager.DeselectAll(); const func = () => this.onClickHandler.script.run({ @@ -978,7 +980,7 @@ export class DocumentView extends DocComponent(Docu return typeof fallback === "string" ? fallback : "layout"; } rootSelected = (outsideReaction?: boolean) => { - return this.isSelected(outsideReaction) || (this.props.Document.forceActive && this.props.rootSelected?.(outsideReaction) ? true : false); + return this.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected?.(outsideReaction)); } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); panelWidth = () => this.props.PanelWidth(); -- 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