aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authoranika-ahluwalia <anika.ahluwalia@gmail.com>2020-04-18 17:56:30 -0500
committeranika-ahluwalia <anika.ahluwalia@gmail.com>2020-04-18 17:56:30 -0500
commit04e2cdc30dd71cccb066f713a93cdc4d3bbd169c (patch)
treeebacc25b4b99ac74b4bf79c12d0ae17a46033b0b /src/client/views/collections
parenta261ab5e9697277bec029803f1fa0c890f0c8fd5 (diff)
parent9f8e76451b807ed8f51ef32aab9b2255dfccd525 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into script_documents
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx30
-rw-r--r--src/client/views/collections/CollectionMapView.tsx191
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx53
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx31
-rw-r--r--src/client/views/collections/CollectionView.scss4
-rw-r--r--src/client/views/collections/CollectionView.tsx65
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss9
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx308
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx17
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss1
11 files changed, 313 insertions, 398 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d77ef812f..c74f5555b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -739,19 +739,27 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
contentScaling = () => {
- if (this.layoutDoc!.type === DocumentType.PDF) {
- if ((this.layoutDoc && this.layoutDoc._fitWidth) ||
- this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
- return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
- } else {
- return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
- }
- }
const nativeH = this.nativeHeight();
const nativeW = this.nativeWidth();
- if (!nativeW || !nativeH) return 1;
- const wscale = this.panelWidth() / nativeW;
- return wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+ let scaling = 1;
+ if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) {
+ scaling = 1;
+ } else if ((this.layoutDoc?._fitWidth) ||
+ this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
+ scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
+ } else {
+ // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) {
+ // if ((this.layoutDoc?._fitWidth) ||
+ // this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
+ // return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
+ // } else {
+ // return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
+ // }
+ // }
+ const wscale = this.panelWidth() / nativeW;
+ scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+ }
+ return scaling;
}
ScreenToLocalTransform = () => {
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 64946e59e..7b7828d7d 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,6 +1,6 @@
import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react";
import { observer } from "mobx-react";
-import { Doc, Opt, DocListCast, FieldResult } from "../../../new_fields/Doc";
+import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { Id } from "../../../new_fields/FieldSymbols";
import { makeInterface } from "../../../new_fields/Schema";
@@ -10,7 +10,7 @@ import { CollectionSubView } from "./CollectionSubView";
import React = require("react");
import { DocumentManager } from "../../util/DocumentManager";
import { UndoManager, undoBatch } from "../../util/UndoManager";
-import { IReactionDisposer, reaction, computed, runInAction } from "mobx";
+import { computed, runInAction, Lambda, action } from "mobx";
import requestPromise = require("request-promise");
type MapSchema = makeInterface<[typeof documentSchema]>;
@@ -22,8 +22,14 @@ export type LocationData = google.maps.LatLngLiteral & {
zoom?: number;
};
+interface DocLatLng {
+ lat: FieldResult<Field>;
+ lng: FieldResult<Field>;
+}
+
// Nowhere, Oklahoma
const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 };
+const noResults = "ZERO_RESULTS";
const query = async (data: string | google.maps.LatLngLiteral) => {
const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`;
@@ -41,8 +47,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
private _cancelAddrReq = new Map<string, boolean>();
private _cancelLocReq = new Map<string, boolean>();
private _initialLookupPending = new Map<string, boolean>();
- private addressUpdaters: IReactionDisposer[] = [];
- private latlngUpdaters: IReactionDisposer[] = [];
+ private responders: { location: Lambda, address: Lambda }[] = [];
/**
* Note that all the uses of runInAction below are not included
@@ -52,34 +57,35 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
* and address–updating reactions.
*/
- private getLocation = (doc: Opt<Doc>, fieldKey: string): Opt<LocationData> => {
+ private getLocation = (doc: Opt<Doc>, fieldKey: string, returnDefault: boolean = true): Opt<LocationData> => {
if (doc) {
- const lat: Opt<number> = Cast(doc[fieldKey + "-lat"], "number", null) || (Cast(doc[fieldKey + "-lat"], "string", null) && Number(Cast(doc[fieldKey + "-lat"], "string", null))) || undefined;
- const lng: Opt<number> = Cast(doc[fieldKey + "-lng"], "number", null) || (Cast(doc[fieldKey + "-lng"], "string", null) && Number(Cast(doc[fieldKey + "-lng"], "string", null))) || undefined;
- const zoom: Opt<number> = Cast(doc[fieldKey + "-zoom"], "number", null) || (Cast(doc[fieldKey + "-zoom"], "string", null) && Number(Cast(doc[fieldKey + "-zoom"], "string", null))) || undefined;
- const address: Opt<string> = Cast(doc[fieldKey + "-address"], "string", null);
- if (lat !== undefined && lng !== undefined) {
- return ({ lat, lng, zoom });
- } else if (address) {
+ const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined;
+ const lat = Cast(doc[`${fieldKey}-lat`], "number", null) || (Cast(doc[`${fieldKey}-lat`], "string", null) && Number(Cast(doc[`${fieldKey}-lat`], "string", null))) || undefined;
+ const lng = Cast(doc[`${fieldKey}-lng`], "number", null) || (Cast(doc[`${fieldKey}-lng`], "string", null) && Number(Cast(doc[`${fieldKey}-lng`], "string", null))) || undefined;
+ const zoom = Cast(doc[`${fieldKey}-zoom`], "number", null) || (Cast(doc[`${fieldKey}-zoom`], "string", null) && Number(Cast(doc[`${fieldKey}-zoom`], "string", null))) || undefined;
+ const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title).replace(/^-/, ""));
+ if (titleLoc || (address && (lat === undefined || lng === undefined))) {
const id = doc[Id];
if (!this._initialLookupPending.get(id)) {
this._initialLookupPending.set(id, true);
setTimeout(() => {
- this.respondToAddressChange(address, doc).then(() => this._initialLookupPending.delete(id));
+ titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true);
+ this.respondToAddressChange(doc, fieldKey, address).then(() => this._initialLookupPending.delete(id));
});
}
- return defaultLocation;
}
+ return (lat === undefined || lng === undefined) ? (returnDefault ? defaultLocation : undefined) : { lat, lng, zoom };
}
return undefined;
}
private markerClick = async (layout: Doc, { lat, lng, zoom }: LocationData) => {
const batch = UndoManager.StartBatch("marker click");
+ const { fieldKey } = this.props;
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);
+ this.layoutDoc[`${fieldKey}-mapCenter-lat`] = lat;
+ this.layoutDoc[`${fieldKey}-mapCenter-lng`] = lng;
+ zoom && (this.layoutDoc[`${fieldKey}-mapCenter-zoom`] = zoom);
});
if (layout.isLinkButton && DocListCast(layout.links).length) {
await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
@@ -93,10 +99,12 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
}
private renderMarkerIcon = (layout: Doc) => {
- const iconUrl = StrCast(this.props.Document.mapIconUrl, null);
+ const { Document } = this.props;
+ const fieldKey = Doc.LayoutFieldKey(layout);
+ const iconUrl = StrCast(layout.mapIconUrl, StrCast(Document.mapIconUrl));
if (iconUrl) {
- const iconWidth = NumCast(layout["mapLocation-iconWidth"], 45);
- const iconHeight = NumCast(layout["mapLocation-iconHeight"], 45);
+ const iconWidth = NumCast(layout[`${fieldKey}-iconWidth`], 45);
+ const iconHeight = NumCast(layout[`${fieldKey}-iconHeight`], 45);
const iconSize = new google.maps.Size(iconWidth, iconHeight);
return {
size: iconSize,
@@ -107,7 +115,7 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
}
private renderMarker = (layout: Doc) => {
- const location = this.getLocation(layout, "mapLocation");
+ const location = this.getLocation(layout, Doc.LayoutFieldKey(layout));
return !location ? (null) :
<Marker
key={layout[Id]}
@@ -118,64 +126,79 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
/>;
}
- private respondToAddressChange = async (newAddress: string, doc: Doc) => {
- const { results } = await query(newAddress);
- if (!results?.length) {
- return;
+ private respondToAddressChange = async (doc: Doc, fieldKey: string, newAddress: string, oldAddress?: string) => {
+ if (newAddress === oldAddress) {
+ return false;
}
- const { geometry, formatted_address } = results[0];
+ const response = await query(newAddress);
+ const id = doc[Id];
+ if (!response || response.status === noResults) {
+ this._cancelAddrReq.set(id, true);
+ doc[`${fieldKey}-address`] = oldAddress;
+ return false;
+ }
+ const { geometry, formatted_address } = response.results[0];
const { lat, lng } = geometry.location;
runInAction(() => {
- if (doc["mapLocation-lat"] !== lat || doc["mapLocation-lng"] !== lng) {
- this._cancelLocReq.set(doc[Id], true);
- Doc.SetInPlace(doc, "mapLocation-lat", lat, true);
- Doc.SetInPlace(doc, "mapLocation-lng", lng, true);
+ if (doc[`${fieldKey}-lat`] !== lat || doc[`${fieldKey}-lng`] !== lng) {
+ this._cancelLocReq.set(id, true);
+ Doc.SetInPlace(doc, `${fieldKey}-lat`, lat, true);
+ Doc.SetInPlace(doc, `${fieldKey}-lng`, lng, true);
}
if (formatted_address !== newAddress) {
- this._cancelAddrReq.set(doc[Id], true);
- Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true);
+ this._cancelAddrReq.set(id, true);
+ Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
}
});
+ return true;
}
- private respondToLocationChange = async (newLat: FieldResult, newLng: FieldResult, doc: Doc) => {
- const { results } = await query({ lat: NumCast(newLat), lng: NumCast(newLng) });
- if (!results?.length) {
- return;
+ private respondToLocationChange = async (doc: Doc, fieldKey: string, newLatLng: DocLatLng, oldLatLng: Opt<DocLatLng>) => {
+ if (newLatLng === oldLatLng) {
+ return false;
+ }
+ const response = await query({ lat: NumCast(newLatLng.lat), lng: NumCast(newLatLng.lng) });
+ const id = doc[Id];
+ if (!response || response.status === noResults) {
+ this._cancelLocReq.set(id, true);
+ runInAction(() => {
+ doc[`${fieldKey}-lat`] = oldLatLng?.lat;
+ doc[`${fieldKey}-lng`] = oldLatLng?.lng;
+ });
+ return false;
}
- const { formatted_address } = results[0];
- if (formatted_address !== doc["mapLocation-address"]) {
+ const { formatted_address } = response.results[0];
+ if (formatted_address !== doc[`${fieldKey}-address`]) {
this._cancelAddrReq.set(doc[Id], true);
- Doc.SetInPlace(doc, "mapLocation-address", formatted_address, true);
+ Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
}
+ return true;
}
- @computed get contents() {
- this.addressUpdaters.forEach(disposer => disposer());
- this.addressUpdaters = [];
- this.latlngUpdaters.forEach(disposer => disposer());
- this.latlngUpdaters = [];
+ @computed get reactiveContents() {
+ this.responders.forEach(({ location, address }) => { location(); address(); });
+ this.responders = [];
return this.childLayoutPairs.map(({ layout }) => {
- this.addressUpdaters.push(reaction(
- () => ({ lat: layout["mapLocation-lat"], lng: layout["mapLocation-lng"] }),
- ({ lat, lng }) => {
- if (this._cancelLocReq.get(layout[Id])) {
- this._cancelLocReq.set(layout[Id], false);
- } else if (lat !== undefined && lng !== undefined) {
- this.respondToLocationChange(lat, lng, layout);
- }
- }
- ));
- this.latlngUpdaters.push(reaction(
- () => ({ address: Cast(layout["mapLocation-address"], "string", null) }),
- ({ address }) => {
- if (this._cancelAddrReq.get(layout[Id])) {
- this._cancelAddrReq.set(layout[Id], false);
- } else if (address?.length) {
- this.respondToAddressChange(address, layout);
- }
- }
- ));
+ const fieldKey = Doc.LayoutFieldKey(layout);
+ const id = layout[Id];
+ this.responders.push({
+ location: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] }))
+ .observe(({ oldValue, newValue }) => {
+ if (this._cancelLocReq.get(id)) {
+ this._cancelLocReq.set(id, false);
+ } else if (newValue.lat !== undefined && newValue.lng !== undefined) {
+ this.respondToLocationChange(layout, fieldKey, newValue, oldValue);
+ }
+ }),
+ address: computed(() => Cast(layout[`${fieldKey}-address`], "string", null))
+ .observe(({ oldValue, newValue }) => {
+ if (this._cancelAddrReq.get(id)) {
+ this._cancelAddrReq.set(id, false);
+ } else if (newValue?.length) {
+ this.respondToAddressChange(layout, fieldKey, newValue, oldValue);
+ }
+ })
+ });
return this.renderMarker(layout);
});
}
@@ -183,12 +206,10 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
render() {
const { childLayoutPairs } = this;
const { Document, fieldKey, active, google } = this.props;
- let center = this.getLocation(Document, fieldKey + "-mapCenter");
+ let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false);
if (center === undefined) {
- center = childLayoutPairs.map(pair => this.getLocation(pair.layout, "mapLocation")).find(layout => layout);
- if (center === undefined) {
- center = defaultLocation;
- }
+ const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false));
+ center = childLocations.find(location => location) || defaultLocation;
}
return <div className="collectionMapView" ref={this.createDashEventsTarget}>
<div className={"collectionMapView-contents"}
@@ -200,15 +221,31 @@ class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> &
zoom={center.zoom || 10}
initialCenter={center}
center={center}
- onDragend={undoBatch((_props: MapProps, map: google.maps.Map) => {
- const { lat, lng } = map.getCenter();
- runInAction(() => {
- Document[fieldKey + "-mapCenter-lat"] = lat();
- Document[fieldKey + "-mapCenter-lng"] = lng();
- });
- })}
+ onIdle={(_props?: MapProps, map?: google.maps.Map) => {
+ if (this.layoutDoc.lockedTransform) {
+ // reset zoom (ideally, we could probably can tell the map to disallow zooming somehow instead)
+ map?.setZoom(center?.zoom || 10);
+ map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
+ } else {
+ const zoom = map?.getZoom();
+ (center?.zoom !== zoom) && undoBatch(action(() => {
+ Document[`${fieldKey}-mapCenter-zoom`] = zoom;
+ }))();
+ }
+ }}
+ onDragend={(_props?: MapProps, map?: google.maps.Map) => {
+ if (this.layoutDoc.lockedTransform) {
+ // reset the drag (ideally, we could probably can tell the map to disallow dragging somehow instead)
+ map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
+ } else {
+ undoBatch(action(({ lat, lng }) => {
+ Document[`${fieldKey}-mapCenter-lat`] = lat();
+ Document[`${fieldKey}-mapCenter-lng`] = lng();
+ }))(map?.getCenter());
+ }
+ }}
>
- {this.contents}
+ {this.reactiveContents}
</GeoMap>
</div>
</div>;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 57be77fdd..380d91d2f 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -736,7 +736,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return (doc as any)[key][row + ${row}][(doc as any).schemaColumns[col + ${col}].heading];
}
return ${script}`;
- const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: true, transformer: this.createTransformer(row, col) });
+ const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
if (compiled.compiled) {
doc[field] = new ComputedField(compiled);
return true;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 31b720b81..1d1949c99 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,6 +1,6 @@
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { ObjectField } from "../../../new_fields/ObjectField";
import { RichTextField } from "../../../new_fields/RichTextField";
@@ -26,18 +26,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
_changing = false;
@observable _layoutEngine = "pivot";
@observable _collapsed: boolean = false;
- componentWillUnmount() {
- this.props.Document.onChildClick = undefined;
- }
+ @observable _childClickedScript: Opt<ScriptField>;
+ @observable _viewDefDivClick: Opt<ScriptField>;
async componentDidMount() {
- const childText = "const alias = getAlias(this); switchView(alias, thisContainer.childDetailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
- this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "string", thisContainer: Doc.name, shiftKey: "boolean" });
- this.props.Document._fitToBox = true;
- if (!this.props.Document.onViewDefClick) {
- this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
- }
- this.props.Document.childDetailView = Cast(this.props.Document.childDetailView, Doc, null) ||// bcz: needs to be here to make sure the childDetailView layout template has been loaded when the first item is clicked;
- DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+ const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), "");
+ const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
+ runInAction(() => {
+ this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
+ this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
+ });
}
layoutEngine = () => this._layoutEngine;
@@ -72,9 +69,23 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
}), returnFalse, emptyFunction);
}
+ contentsDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+ let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
+ if (prevFilterIndex > 0) {
+ prevFilterIndex--;
+ this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
+ this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
+ this.props.Document._prevFilterIndex = prevFilterIndex;
+ } else {
+ this.props.Document._docFilters = new List([]);
+ }
+ }), false);
+ }
+
@computed get contents() {
- return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }}>
- <CollectionFreeFormView {...this.props} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
+ return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
+ <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
</div>;
}
@@ -132,20 +143,6 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
};
return <div className={"pivotKeyEntry"}>
- <button className="collectionTimeView-backBtn"
- onClick={action(() => {
- let prevFilterIndex = NumCast(this.props.Document._prevFilterIndex);
- if (prevFilterIndex > 0) {
- prevFilterIndex--;
- this.props.Document._docFilters = ObjectField.MakeCopy(this.props.Document["_prevDocFilter" + prevFilterIndex] as ObjectField);
- this.props.Document._docRangeFilters = ObjectField.MakeCopy(this.props.Document["_prevDocRangeFilters" + prevFilterIndex] as ObjectField);
- this.props.Document._prevFilterIndex = prevFilterIndex;
- } else {
- this.props.Document._docFilters = new List([]);
- }
- })}>
- back
- </button>
<EditableView {...newEditableViewProps} display={"inline"} menuCallback={this.menuCallback} />
</div>;
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b96ee4bc4..5ff8c11ee 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -19,7 +19,7 @@ import { makeTemplate } from '../../util/DropConverter';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from "../EditableView";
@@ -145,7 +145,6 @@ class TreeView extends React.Component<TreeViewProps> {
ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this)));
}
- onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
onPointerEnter = (e: React.PointerEvent): void => {
this.props.active(true) && Doc.BrushDoc(this.dataDoc);
if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
@@ -397,11 +396,13 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
+ get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.props.document.onCheckedClick); }
+
@action
bulletClick = (e: React.MouseEvent) => {
- if (this.props.onCheckedClick && this.props.document.type !== DocumentType.COL) {
+ if (this.onCheckedClick && this.props.document.type !== DocumentType.COL) {
// this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check";
- ScriptCast(this.props.onCheckedClick).script.run({
+ this.onCheckedClick.script.run({
this: this.props.document.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.props.document,
heading: this.props.containingCollection.title,
checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check",
@@ -415,7 +416,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
- const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
+ const checked = this.props.document.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}>
{<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
</div>;
@@ -451,7 +452,7 @@ class TreeView extends React.Component<TreeViewProps> {
fontWeight: this.props.document.searchMatch ? "bold" : undefined,
textDecoration: Doc.GetT(this.props.document, "title", "string", true) ? "underline" : undefined,
outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
- pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
+ pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? undefined : "none"
}} >
{Doc.GetT(this.props.document, "editTitle", "boolean", true) ?
this.editableView("title") :
@@ -764,8 +765,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({
- description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document,
- "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", treeViewContainer: Doc.name })
+ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
});
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
@@ -818,7 +818,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields),
- BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick || ScriptCast(this.props.Document.onCheckedClick),
+ BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
this.props.onChildClick || ScriptCast(this.props.Document.onChildClick), this.props.ignoreFields)
}
</ul>
@@ -839,13 +839,12 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
nonNumbers++;
}
});
- const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue =>
- Docs.Create.TextDocument("", {
- title: facetValue.toString(),
- treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",
- {},
- { layoutDoc, facetHeader, facetValue })
- }));
+ const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+ const doc = new Doc();
+ doc.title = facetValue.toString();
+ doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
+ return doc;
+ });
return new List<Doc>(facetValueDocSet);
});
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index a0b73b90f..d43dd387a 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -20,7 +20,7 @@
top: 55%;
border: 1px black solid;
z-index: 2;
- left: -10px;
+ right: -10px;
}
.collectionTimeView-treeView {
display: flex;
@@ -28,7 +28,7 @@
width: 200px;
height: 100%;
position: absolute;
- left: 0;
+ right: 0;
top: 0;
.collectionTimeView-addfacet {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 13a657800..877e4355f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ import { List } from '../../../new_fields/List';
import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types';
import { ImageField } from '../../../new_fields/URLField';
import { TraceMobx } from '../../../new_fields/util';
-import { Utils, setupMoveUpEvents, returnFalse, returnZero } from '../../../Utils';
+import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
@@ -45,8 +45,7 @@ import { ScriptField, ComputedField } from '../../../new_fields/ScriptField';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ObjectField } from '../../../new_fields/ObjectField';
import CollectionMapView from './CollectionMapView';
-import { ClientUtils } from '../../util/ClientUtils';
-import { GoogleApiWrapper } from 'google-maps-react';
+import { Transform } from 'prosemirror-transform';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -279,10 +278,10 @@ export class CollectionView extends Touchable<FieldViewProps> {
onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
}
- @observable _facetWidth = 0;
+ get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
+ set _facetWidth(value) { this.props.Document._facetWidth = value; }
bodyPanelWidth = () => this.props.PanelWidth() - this.facetWidth();
- getTransform = () => this.props.ScreenToLocalTransform().translate(-this.facetWidth(), 0);
facetWidth = () => Math.max(0, Math.min(this.props.PanelWidth() - 25, this._facetWidth));
@computed get dataDoc() {
@@ -365,17 +364,22 @@ export class CollectionView extends Touchable<FieldViewProps> {
Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
newFacet.treeViewExpandedView = "layout";
newFacet.treeViewOpen = true;
- newFacet[newFacetField + "-min"] = ranged === undefined ? minVal : ranged[0];
- newFacet[newFacetField + "-max"] = ranged === undefined ? maxVal : ranged[1];
- Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = minVal;
- Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = maxVal;
+ const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
+ const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
+ newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
+ newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
+ Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
+ Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
newFacet.target = this.props.Document;
const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
} else {
- newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
+ newFacet = new Doc();
+ newFacet.title = facetHeader;
+ newFacet.treeViewOpen = true;
+ newFacet.type = DocumentType.COL;
const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
}
@@ -385,7 +389,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
onPointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
- this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
+ this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
return false;
}), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0));
}
@@ -411,27 +415,44 @@ export class CollectionView extends Touchable<FieldViewProps> {
<div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
<Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
<div className="collectionTimeView-button">
- <span className="collectionTimeView-span">Facet Filters</span>
<FontAwesomeIcon icon={faEdit} size={"lg"} />
+ <span className="collectionTimeView-span">Facet Filters</span>
</div>
</Flyout>
</div>
<div className="collectionTimeView-tree" key="tree">
- <CollectionTreeView {...this.props}
+ <CollectionTreeView
+ Document={facetCollection}
+ DataDoc={facetCollection}
+ fieldKey={`${this.props.fieldKey}-filter`}
CollectionView={this}
- treeViewHideTitle={true}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ PanelWidth={this.facetWidth}
+ PanelHeight={this.props.PanelHeight}
NativeHeight={returnZero}
NativeWidth={returnZero}
+ LibraryPath={emptyPath}
+ rootSelected={this.props.rootSelected}
+ renderDepth={1}
+ dropAction={this.props.dropAction}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ isSelected={returnFalse}
+ select={returnFalse}
+ bringToFront={emptyFunction}
+ active={this.props.active}
+ whenActiveChanged={returnFalse}
+ treeViewHideTitle={true}
+ ContentScaling={returnOne}
+ focus={returnFalse}
treeViewHideHeaderFields={true}
onCheckedClick={this.scriptField!}
ignoreFields={this.ignoreFields}
annotationsKey={""}
dontRegisterView={true}
- PanelWidth={this.facetWidth}
- DataDoc={facetCollection}
- Document={facetCollection}
backgroundColor={this.filterBackground}
- fieldKey={`${this.props.fieldKey}-filter`}
moveDocument={returnFalse}
removeDocument={returnFalse}
addDocument={returnFalse} />
@@ -451,13 +472,13 @@ export class CollectionView extends Touchable<FieldViewProps> {
};
return (<div className={"collectionView"}
style={{
- pointerEvents: this.props.Document.isBackground ? "none" : "all",
+ pointerEvents: this.props.Document.isBackground ? "none" : undefined,
boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
`${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31)" : "#9c9396"} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
}}
onContextMenu={this.onContextMenu}>
{this.showIsTagged()}
- <div style={{ width: `calc(100% - ${this.facetWidth()}px)`, marginLeft: `${this.facetWidth()}px` }}>
+ <div style={{ width: `calc(100% - ${this.facetWidth()}px)` }}>
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
</div>
{this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
@@ -467,9 +488,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
:
""))}
{!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
- <div className="collectionTimeView-dragger" key="dragger" onPointerDown={this.onPointerDown} style={{ transform: `translate(${this.facetWidth()}px, 0px)` }} >
- <span title="library View Dragger" style={{ width: "5px", position: "absolute", top: "0" }} />
- </div>
+ <div className="collectionTimeView-dragger" title="library View Dragger" onPointerDown={this.onPointerDown} style={{ right: this.facetWidth() - 10 }} />
}
{this.filterView}
</div>);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index a691b4805..5203eb55f 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -61,16 +61,21 @@
pointer-events: all;
// margin-top: 10px;
}
- .collectionViewBaseChrome-template {
+ .collectionViewBaseChrome-template,
+ .collectionViewBaseChrome-viewModes {
display: grid;
background: rgb(238, 238, 238);
color:grey;
margin-top:auto;
margin-bottom:auto;
+ margin-left: 5px;
+ }
+ .collectionViewBaseChrome-viewModes {
+ margin-left: 25px;
}
.collectionViewBaseChrome-viewSpecs {
- margin-left: 10px;
+ margin-left: 5px;
display: grid;
.collectionViewBaseChrome-filterIcon {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 66f627083..7315d2c4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -6,7 +6,6 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
@@ -16,8 +15,6 @@ import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
import { CollectionViewType } from "./CollectionView";
import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
-import * as Autosuggest from 'react-autosuggest';
-import KeyRestrictionRow from "./KeyRestrictionRow";
const datepicker = require('js-datepicker');
interface CollectionViewChromeProps {
@@ -84,62 +81,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
private _picker: any;
private _commandRef = React.createRef<HTMLInputElement>();
private _viewRef = React.createRef<HTMLInputElement>();
- private _autosuggestRef = React.createRef<Autosuggest>();
@observable private _currentKey: string = "";
- @observable private _viewSpecsOpen: boolean = false;
- @observable private _dateWithinValue: string = "";
- @observable private _dateValue: Date | string = "";
- @observable private _keyRestrictions: [JSX.Element, string][] = [];
- @observable private suggestions: string[] = [];
- @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
-
- getFilters = (script: string) => {
- const re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
- const arr: any[] = re.exec(script);
- const toReturn: Filter[] = [];
- if (arr !== null) {
- const filter: Filter = {
- key: arr[2],
- value: arr[3],
- contains: (arr[1] === "!") ? false : true,
- };
- toReturn.push(filter);
- script = script.replace(arr[0], "");
- if (re.exec(script) !== null) {
- toReturn.push(...this.getFilters(script));
- }
- else { return toReturn; }
- }
- return toReturn;
- }
-
- addKeyRestrictions = (fields: Filter[]) => {
-
- if (fields.length !== 0) {
- for (let i = 0; i < fields.length; i++) {
- this._keyRestrictions.push([<KeyRestrictionRow field={fields[i].key} value={fields[i].value} key={Utils.GenerateGuid()} contains={fields[i].contains} script={(value: string) => runInAction(() => this._keyRestrictions[i][1] = value)} />, ""]);
-
- }
- if (this._keyRestrictions.length === 1) {
- this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
- }
- }
- else {
- this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]);
- this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]);
- }
- }
componentDidMount = () => {
-
- let fields: Filter[] = [];
- if (this.filterValue) {
- const string = this.filterValue.script.originalScript;
- fields = this.getFilters(string);
- }
-
runInAction(() => {
- this.addKeyRestrictions(fields);
// chrome status is one of disabled, collapsed, or visible. this determines initial state from document
const chromeStatus = this.props.CollectionView.props.Document._chromeStatus;
if (chromeStatus) {
@@ -158,7 +103,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
//@ts-ignore
- this.props.CollectionView.props.Document._viewType = e.target.selectedOptions[0].value;
+ this.document._viewType = e.target.selectedOptions[0].value;
}
commandChanged = (e: React.ChangeEvent) => {
@@ -167,104 +112,93 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
@action
- openViewSpecs = (e: React.SyntheticEvent) => {
- if (this._viewSpecsOpen) this.closeViewSpecs();
- else {
- this._viewSpecsOpen = true;
-
- //@ts-ignore
- if (!e.target?.classList[0]?.startsWith("qs")) {
- this.closeDatePicker();
- }
-
- e.stopPropagation();
- document.removeEventListener("pointerdown", this.closeViewSpecs);
- document.addEventListener("pointerdown", this.closeViewSpecs);
- }
+ toggleViewSpecs = (e: React.SyntheticEvent) => {
+ this.document._facetWidth = this.document._facetWidth ? 0 : 200;
+ e.stopPropagation();
}
@action closeViewSpecs = () => {
- this._viewSpecsOpen = false;
- document.removeEventListener("pointerdown", this.closeViewSpecs);
- }
-
- @action
- openDatePicker = (e: React.PointerEvent) => {
- this.openViewSpecs(e);
- if (this._picker) {
- this._picker.alwaysShow = true;
- this._picker.show();
- // TODO: calendar is offset when zoomed in/out
- // this._picker.calendar.style.position = "absolute";
- // let transform = this.props.CollectionView.props.ScreenToLocalTransform();
- // let x = parseInt(this._picker.calendar.style.left) / transform.Scale;
- // let y = parseInt(this._picker.calendar.style.top) / transform.Scale;
- // this._picker.calendar.style.left = x;
- // this._picker.calendar.style.top = y;
-
- e.stopPropagation();
- }
- }
-
- @action
- addKeyRestriction = (e: React.MouseEvent) => {
- const index = this._keyRestrictions.length;
- this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]);
-
- this.openViewSpecs(e);
- }
-
- @action.bound
- applyFilter = (e: React.MouseEvent) => {
-
- this.openViewSpecs(e);
-
- const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
- const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
- const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
- const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
- const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
- let dateRestrictionScript = "";
- if (this._dateValue instanceof Date) {
- const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
- const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
- dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
- }
- else {
- const createdDate = new Date(this._dateValue);
- if (!isNaN(createdDate.getTime())) {
- const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
- const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
- dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
- }
- }
- const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
- `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
- `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
- "true";
-
- this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name });
- }
+ this.document._facetWidth = 0;
+ }
+
+ // @action
+ // openDatePicker = (e: React.PointerEvent) => {
+ // if (this._picker) {
+ // this._picker.alwaysShow = true;
+ // this._picker.show();
+ // // TODO: calendar is offset when zoomed in/out
+ // // this._picker.calendar.style.position = "absolute";
+ // // let transform = this.props.CollectionView.props.ScreenToLocalTransform();
+ // // let x = parseInt(this._picker.calendar.style.left) / transform.Scale;
+ // // let y = parseInt(this._picker.calendar.style.top) / transform.Scale;
+ // // this._picker.calendar.style.left = x;
+ // // this._picker.calendar.style.top = y;
+
+ // e.stopPropagation();
+ // }
+ // }
+
+ // <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
+ // id={Utils.GenerateGuid()}
+ // ref={this.datePickerRef}
+ // value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
+ // onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
+ // onPointerDown={this.openDatePicker}
+ // placeholder="Value" />
+ // @action.bound
+ // applyFilter = (e: React.MouseEvent) => {
+ // const keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")";
+ // const yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0;
+ // const monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0;
+ // const weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0;
+ // const dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
+ // let dateRestrictionScript = "";
+ // if (this._dateValue instanceof Date) {
+ // const lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
+ // const upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
+ // dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
+ // }
+ // else {
+ // const createdDate = new Date(this._dateValue);
+ // if (!isNaN(createdDate.getTime())) {
+ // const lowerBound = new Date(createdDate.getFullYear() - yearOffset, createdDate.getMonth() - monthOffset, createdDate.getDate() - dayOffset);
+ // const upperBound = new Date(createdDate.getFullYear() + yearOffset, createdDate.getMonth() + monthOffset, createdDate.getDate() + dayOffset + 1);
+ // dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
+ // }
+ // }
+ // const fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ?
+ // `${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` :
+ // `(${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` :
+ // "true";
+
+ // this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction(fullScript, { doc: Doc.name });
+ // }
+
+ // datePickerRef = (node: HTMLInputElement) => {
+ // if (node) {
+ // try {
+ // this._picker = datepicker("#" + node.id, {
+ // disabler: (date: Date) => date > new Date(),
+ // onSelect: (instance: any, date: Date) => runInAction(() => {}), // this._dateValue = date),
+ // dateSelected: new Date()
+ // });
+ // } catch (e) {
+ // console.log("date picker exception:" + e);
+ // }
+ // }
+ // }
- @action
- closeDatePicker = () => {
- if (this._picker) {
- this._picker.alwaysShow = false;
- this._picker.hide();
- }
- document.removeEventListener("pointerdown", this.closeDatePicker);
- }
@action
toggleCollapse = () => {
- this.props.CollectionView.props.Document._chromeStatus = this.props.CollectionView.props.Document._chromeStatus === "enabled" ? "collapsed" : "enabled";
+ this.document._chromeStatus = this.document._chromeStatus === "enabled" ? "collapsed" : "enabled";
if (this.props.collapse) {
this.props.collapse(this.props.CollectionView.props.Document._chromeStatus !== "enabled");
}
}
subChrome = () => {
- const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+ const collapsed = this.document._chromeStatus !== "enabled";
if (collapsed) return null;
switch (this.props.type) {
case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
@@ -279,13 +213,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
return this.props.CollectionView.props.Document;
}
- @action.bound
- clearFilter = () => {
- this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction("true", { doc: Doc.name });
- this._keyRestrictions = [];
- this.addKeyRestrictions([]);
- }
-
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
this.dropDisposer && this.dropDisposer();
@@ -304,48 +231,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
return true;
}
- datePickerRef = (node: HTMLInputElement) => {
- if (node) {
- try {
- this._picker = datepicker("#" + node.id, {
- disabler: (date: Date) => date > new Date(),
- onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
- dateSelected: new Date()
- });
- } catch (e) {
- console.log("date picker exception:" + e);
- }
- }
- }
-
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- }
- getSuggestionValue = (suggestion: string) => suggestion;
-
- @action
- onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
- this._currentKey = newValue;
- }
- onSuggestionFetch = async ({ value }: { value: string }) => {
- const sugg = await this.getKeySuggestions(value);
- runInAction(() => this.suggestions = sugg);
- }
- @action
- onSuggestionClear = () => {
- this.suggestions = [];
- }
- getKeySuggestions = async (value: string): Promise<string[]> => {
- return this._buttonizableCommands.filter(c => c.title.indexOf(value) !== -1).map(c => c.title);
- }
-
- autoSuggestDown = (e: React.PointerEvent) => {
- e.stopPropagation();
- }
-
- private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
- private _sensitivity: number = 16;
-
dragViewDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, (e, down, delta) => {
const vtype = this.props.CollectionView.collectionViewType;
@@ -367,7 +252,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
{ target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY));
return true;
}, emptyFunction, emptyFunction);
- this._startDragPosition = { x: e.clientX, y: e.clientY };
}
render() {
@@ -392,7 +276,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
title="Collapse collection chrome" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
</button>
- <div className="collectionViewBaseChrome-template" style={{ marginLeft: 25, display: collapsed ? "none" : undefined }}>
+ <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}>
<div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}>
<div className="commandEntry-drop">
<FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
@@ -415,47 +299,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
</div>
</div>
<div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
- <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
+ <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.toggleViewSpecs} >
<FontAwesomeIcon icon="filter" size="2x" />
</div>
- <div className="collectionViewBaseChrome-viewSpecsMenu"
- onPointerDown={this.openViewSpecs}
- style={{
- height: this._viewSpecsOpen ? "fit-content" : "0px",
- overflow: this._viewSpecsOpen ? "initial" : "hidden"
- }}>
- {this._keyRestrictions.map(i => i[0])}
- <div className="collectionViewBaseChrome-viewSpecsMenu-row">
- <div className="collectionViewBaseChrome-viewSpecsMenu-rowLeft">
- CREATED WITHIN:
- </div>
- <select className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
- style={{ textTransform: "uppercase", textAlign: "center" }}
- value={this._dateWithinValue}
- onChange={(e) => runInAction(() => this._dateWithinValue = e.target.value)}>
- <option value="1d">1 day of</option>
- <option value="3d">3 days of</option>
- <option value="1w">1 week of</option>
- <option value="2w">2 weeks of</option>
- <option value="1m">1 month of</option>
- <option value="2m">2 months of</option>
- <option value="6m">6 months of</option>
- <option value="1y">1 year of</option>
- </select>
- <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
- id={Utils.GenerateGuid()}
- ref={this.datePickerRef}
- value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
- onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
- onPointerDown={this.openDatePicker}
- placeholder="Value" />
- </div>
- <div className="collectionViewBaseChrome-viewSpecsMenu-lastRow">
- <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}> ADD KEY RESTRICTION </button>
- <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> APPLY FILTER </button>
- <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> CLEAR </button>
- </div>
- </div>
</div>
<div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
<div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a77f09163..87cf716e5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -31,7 +31,7 @@ import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentViewProps, DocumentView } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
@@ -44,6 +44,7 @@ import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { CollectionViewType } from "../CollectionView";
+import { Script } from "vm";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -69,6 +70,8 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchem
const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema);
export type collectionFreeformViewProps = {
forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox)
+ childClickScript?: ScriptField;
+ viewDefDivClick?: ScriptField;
};
@observer
@@ -801,7 +804,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
if (!annotOn) {
this.props.focus(doc);
} else {
- const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height);
+ const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
const offset = annotOn && (contextHgt / 2 * 96 / 72);
this.props.Document.scrollY = NumCast(doc.y) - offset;
}
@@ -817,9 +820,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document.scale, pt: this.Document.panTransformType };
- if (!willZoom) {
+ if (!willZoom && DocumentView._focusHack.length) {
Doc.BrushDoc(this.props.Document);
- !doc.z && this.scaleAtPt([NumCast(doc.x), NumCast(doc.y)], 1);
+ !doc.z && NumCast(this.layoutDoc.scale) < 1 && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
} else {
if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
if (!doc.z) this.setPan(newPanX, newPanY, "Ease", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
@@ -847,7 +850,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
}
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
- @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+ @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
backgroundHalo = () => BoolCast(this.Document.useClusters);
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
@@ -906,7 +909,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
}
onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
- (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
+ (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
+ e.stopPropagation();
}
private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
const { x, y, z } = viewDef;
@@ -991,6 +995,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
{...this.getChildDocumentViewProps(pair.layout, pair.data)}
dataProvider={this.childDataProvider}
LayoutDoc={this.childLayoutDocFunc}
+ pointerEvents={this.props.layoutEngine?.() !== undefined ? false : undefined}
jitterRotation={NumCast(this.props.Document.jitterRotation)}
fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 18d6da0da..1291e7dc1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -6,7 +6,6 @@
width:100%;
height:100%;
overflow: hidden;
- pointer-events: inherit;
border-radius: inherit;
}