aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorMelissa Zhang <mzhang19096@gmail.com>2020-04-22 23:40:32 -0700
committerMelissa Zhang <mzhang19096@gmail.com>2020-04-22 23:40:32 -0700
commit4f3964ecaa67797237734deb4b2ae9aaaf77a940 (patch)
tree04460478d173f56d3f2950fcf001ab63e000a9b8 /src/client/views/collections
parent13f182c2f138c25b0b57169dc94ecbfdd59483ef (diff)
parentd2682eba642d2f10e94822d9c9864a6477052e06 (diff)
fixed merge conflicts
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx52
-rw-r--r--src/client/views/collections/CollectionMapView.scss28
-rw-r--r--src/client/views/collections/CollectionMapView.tsx300
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx4
-rw-r--r--src/client/views/collections/CollectionSubView.tsx3
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx61
-rw-r--r--src/client/views/collections/CollectionTreeView.scss32
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx174
-rw-r--r--src/client/views/collections/CollectionView.scss5
-rw-r--r--src/client/views/collections/CollectionView.tsx145
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss9
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx330
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx13
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx18
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx56
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
21 files changed, 686 insertions, 557 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d77ef812f..0d859c3f1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -14,11 +14,9 @@ import { List } from '../../../new_fields/List';
import { FieldId } from "../../../new_fields/RefField";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { TraceMobx } from '../../../new_fields/util';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from "../../util/DragManager";
import { Scripting } from '../../util/Scripting';
@@ -31,6 +29,7 @@ import "./CollectionDockingView.scss";
import { SubCollectionViewProps } from "./CollectionSubView";
import { DockingViewButtonSelector } from './ParentDocumentSelector';
import React = require("react");
+import { CollectionViewType } from './CollectionView';
library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
@@ -95,6 +94,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@undoBatch
@action
public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
+ if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
+ return MainView.Instance.openWorkspace(docView.props.Document);
+ }
const document = Doc.MakeAlias(docView.props.Document);
const newItemStackConfig = {
type: 'stack',
@@ -376,8 +378,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
// Because this is in a set timeout, if this component unmounts right after mounting,
// we will leak a GoldenLayout, because we try to destroy it before we ever create it
setTimeout(() => this.setupGoldenLayout(), 1);
- const userDoc = CurrentUserUtils.UserDocument;
- userDoc && DocListCast((userDoc.workspaces as Doc).data).map(d => d.workspaceBrush = false);
+ DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false);
this.props.Document.workspaceBrush = true;
}
this._ignoreStateChange = "";
@@ -544,9 +545,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const theDoc = doc;
CollectionDockingView.Instance._removedDocs.push(theDoc);
- const userDoc = CurrentUserUtils.UserDocument;
- let recent: Doc | undefined;
- if (userDoc && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
+ const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
+ if (recent) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
SelectionManager.DeselectAll();
@@ -606,7 +606,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
if (doc instanceof Doc) {
let recent: Doc | undefined;
- if (CurrentUserUtils.UserDocument && (recent = await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))) {
+ if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) {
Doc.AddDocToList(recent, "data", doc, undefined, true, true);
}
const theDoc = doc;
@@ -681,7 +681,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@action
public static PinDoc(doc: Doc) {
//add this new doc to props.Document
- const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+ const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
if (curPres) {
const pinDoc = Doc.MakeAlias(doc);
pinDoc.presentationTargetDoc = doc;
@@ -698,7 +698,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@action
public static UnpinDoc(doc: Doc) {
//add this new doc to props.Document
- const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+ const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
if (curPres) {
const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
@@ -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 = () => {
@@ -767,7 +775,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
SelectionManager.DeselectAll();
- if (doc.dockingConfig) {
+ if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
return MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
return CollectionDockingView.AddRightSplit(doc, libraryPath);
diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/CollectionMapView.scss
index c74433902..870b7fda8 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/CollectionMapView.scss
@@ -1,4 +1,30 @@
-.collectionMapView-contents {
+.collectionMapView {
width: 100%;
height: 100%;
+
+ .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
+ }
+ }
+}
+
+.loadingWrapper {
+ width: 100%;
+ height: 100%;
+ background-color: pink;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+
+ .loadingGif {
+ align-self: center;
+ justify-self: center;
+ 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 b67daeb53..7b7828d7d 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,71 +1,263 @@
+import { GoogleApiWrapper, Map as GeoMap, MapProps, Marker } from "google-maps-react";
import { observer } from "mobx-react";
-import { makeInterface } from "../../../new_fields/Schema";
+import { Doc, Opt, DocListCast, FieldResult, Field } 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 { Id } from "../../../new_fields/FieldSymbols";
+import { makeInterface } from "../../../new_fields/Schema";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import "./CollectionMapView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { Utils } from "../../../Utils";
-import { Opt } from "../../../new_fields/Doc";
+import React = require("react");
+import { DocumentManager } from "../../util/DocumentManager";
+import { UndoManager, undoBatch } from "../../util/UndoManager";
+import { computed, runInAction, Lambda, action } from "mobx";
+import requestPromise = require("request-promise");
+
+type MapSchema = makeInterface<[typeof documentSchema]>;
+const MapSchema = makeInterface(documentSchema);
-type MapDocument = makeInterface<[typeof documentSchema]>;
-const MapDocument = makeInterface(documentSchema);
+export type LocationData = google.maps.LatLngLiteral & {
+ address?: string
+ resolvedAddress?: string;
+ zoom?: number;
+};
-export type LocationData = google.maps.LatLngLiteral & { address?: string };
+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}`;
+ const target = `https://maps.googleapis.com/maps/api/geocode/json?${contents}&key=${process.env.GOOGLE_MAPS_GEO}`;
+ try {
+ return JSON.parse(await requestPromise.get(target));
+ } catch {
+ return undefined;
+ }
+};
@observer
-class CollectionMapView extends CollectionSubView<MapDocument, Partial<MapProps> & { google: any }>(MapDocument) {
+class CollectionMapView extends CollectionSubView<MapSchema, Partial<MapProps> & { google: any }>(MapSchema) {
+
+ private _cancelAddrReq = new Map<string, boolean>();
+ private _cancelLocReq = new Map<string, boolean>();
+ private _initialLookupPending = new Map<string, boolean>();
+ private responders: { location: Lambda, address: Lambda }[] = [];
+
+ /**
+ * 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.
+ */
+
+ private getLocation = (doc: Opt<Doc>, fieldKey: string, returnDefault: boolean = true): Opt<LocationData> => {
+ if (doc) {
+ const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined;
+ const lat = Cast(doc[`${fieldKey}-lat`], "number", null) || (Cast(doc[`${fieldKey}-lat`], "string", null) && Number(Cast(doc[`${fieldKey}-lat`], "string", null))) || undefined;
+ const lng = Cast(doc[`${fieldKey}-lng`], "number", null) || (Cast(doc[`${fieldKey}-lng`], "string", null) && Number(Cast(doc[`${fieldKey}-lng`], "string", null))) || undefined;
+ const zoom = Cast(doc[`${fieldKey}-zoom`], "number", null) || (Cast(doc[`${fieldKey}-zoom`], "string", null) && Number(Cast(doc[`${fieldKey}-zoom`], "string", null))) || undefined;
+ const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title).replace(/^-/, ""));
+ if (titleLoc || (address && (lat === undefined || lng === undefined))) {
+ const id = doc[Id];
+ if (!this._initialLookupPending.get(id)) {
+ this._initialLookupPending.set(id, true);
+ setTimeout(() => {
+ titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true);
+ this.respondToAddressChange(doc, fieldKey, address).then(() => this._initialLookupPending.delete(id));
+ });
+ }
+ }
+ 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[`${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) => {
+ 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();
+ }
+ }
+
+ private renderMarkerIcon = (layout: Doc) => {
+ const { Document } = this.props;
+ const fieldKey = Doc.LayoutFieldKey(layout);
+ const iconUrl = StrCast(layout.mapIconUrl, StrCast(Document.mapIconUrl));
+ if (iconUrl) {
+ const iconWidth = NumCast(layout[`${fieldKey}-iconWidth`], 45);
+ const iconHeight = NumCast(layout[`${fieldKey}-iconHeight`], 45);
+ const iconSize = new google.maps.Size(iconWidth, iconHeight);
+ return {
+ size: iconSize,
+ scaledSize: iconSize,
+ url: iconUrl
+ };
+ }
+ }
+
+ private renderMarker = (layout: Doc) => {
+ const location = this.getLocation(layout, Doc.LayoutFieldKey(layout));
+ return !location ? (null) :
+ <Marker
+ key={layout[Id]}
+ label={StrCast(layout.title)}
+ position={location}
+ onClick={() => this.markerClick(layout, location)}
+ icon={this.renderMarkerIcon(layout)}
+ />;
+ }
+
+ private respondToAddressChange = async (doc: Doc, fieldKey: string, newAddress: string, oldAddress?: string) => {
+ if (newAddress === oldAddress) {
+ return false;
+ }
+ 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[`${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(id, true);
+ Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
+ }
+ });
+ return true;
+ }
+
+ 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 } = response.results[0];
+ if (formatted_address !== doc[`${fieldKey}-address`]) {
+ this._cancelAddrReq.set(doc[Id], true);
+ Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
+ }
+ return true;
+ }
+
+ @computed get reactiveContents() {
+ this.responders.forEach(({ location, address }) => { location(); address(); });
+ this.responders = [];
+ return this.childLayoutPairs.map(({ layout }) => {
+ const fieldKey = Doc.LayoutFieldKey(layout);
+ const id = layout[Id];
+ this.responders.push({
+ location: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] }))
+ .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);
+ });
+ }
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, fieldKey, active, google } = this.props;
+ let center = this.getLocation(Document, `${fieldKey}-mapCenter`, false);
+ if (center === undefined) {
+ const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false));
+ center = childLocations.find(location => location) || defaultLocation;
}
- return (
- <div
- className={"collectionMapView-contents"}
- >
- <Map
- {...props}
- zoom={NumCast(Document.zoom, 10)}
- center={center}
+ return <div className="collectionMapView" ref={this.createDashEventsTarget}>
+ <div className={"collectionMapView-contents"}
+ style={{ pointerEvents: active() ? undefined : "none" }}
+ onWheel={e => e.stopPropagation()}
+ onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
+ <GeoMap
+ google={google}
+ zoom={center.zoom || 10}
initialCenter={center}
- >
- {childLayoutPairs.map(({ layout }) => {
- const location: LocationData = {
- lat: NumCast(childLayoutPairs[0].layout.locationLat, 0),
- lng: NumCast(childLayoutPairs[0].layout.locationLng, 0)
- };
- let icon: Opt<google.maps.Icon>, iconUrl: Opt<string>;
- 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
- };
+ center={center}
+ 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;
+ }))();
}
- return (
- <Marker
- key={Utils.GenerateGuid()}
- label={StrCast(layout.title)}
- position={location}
- onClick={() => {
- Document.mapCenterLat = location.lat;
- Document.mapCenterLng = location.lng;
- }}
- icon={icon}
- />
- );
- })}
- </Map>
+ }}
+ 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.reactiveContents}
+ </GeoMap>
</div>
- );
+ </div>;
}
}
-export default GoogleApiWrapper({ apiKey: process.env.GOOGLE_MAPS! })(CollectionMapView) as any; \ No newline at end of file
+export default GoogleApiWrapper({
+ apiKey: process.env.GOOGLE_MAPS!,
+ LoadingContainer: () => (
+ <div className={"loadingWrapper"}>
+ <img className={"loadingGif"} src={"/assets/loading.gif"} />
+ </div>
+ )
+})(CollectionMapView) as any; \ No newline at end of file
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/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dd84c4d6e..24a3119cc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -63,7 +63,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
const dxf = () => this.getDocTransform(d, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- const style = this.isStackingView ? { width: width(), marginTop: this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(d, (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc, dxf, width)}
</div>;
@@ -397,7 +397,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (!e.isPropagationStopped()) {
const subItems: ContextMenuProps[] = [];
subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
- ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 6b7c3de56..ca38a21b8 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -100,7 +100,7 @@ export function CollectionSubView<T, X>(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.
@@ -388,6 +388,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
continue;
}
const proto = Doc.GetProto(doc);
+ proto.text = result.rawText;
proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
if (Upload.isImageInformation(result)) {
proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 53de2fbbe..06ebf6d2d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,11 +1,11 @@
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";
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,23 +19,22 @@ 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) {
_changing = false;
@observable _layoutEngine = "pivot";
@observable _collapsed: boolean = false;
- 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<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" });
- }
+ @observable _childClickedScript: Opt<ScriptField>;
+ @observable _viewDefDivClick: Opt<ScriptField>;
+ async componentDidMount() {
+ 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;
@@ -70,15 +69,29 @@ 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>;
}
public static SyncTimelineToPresentation(doc: Doc) {
const fieldKey = Doc.LayoutFieldKey(doc);
- doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(curPresentationItem()[this._pivotField || 'year'] || 0)");
+ doc[fieldKey + "-timelineCur"] = ComputedField.MakeFunction("(activePresentationItem()[this._pivotField || 'year'] || 0)");
}
specificMenu = (e: React.MouseEvent) => {
const layoutItems: ContextMenuProps[] = [];
@@ -89,7 +102,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
layoutItems.push({ description: "Auto Time/Pivot layout", event: () => { doc._forceRenderEngine = undefined; }, icon: "compress-arrows-alt" });
layoutItems.push({ description: "Sync with presentation", event: () => CollectionTimeView.SyncTimelineToPresentation(doc), icon: "compress-arrows-alt" });
- ContextMenu.Instance.addItem({ description: "Pivot/Time Options ...", subitems: layoutItems, icon: "eye" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
}
@computed get _allFacets() {
const facets = new Set<string>();
@@ -130,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.scss b/src/client/views/collections/CollectionTreeView.scss
index 8e95f7fbe..a00bb6bfb 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -82,6 +82,7 @@
text-overflow: ellipsis;
white-space: pre-wrap;
overflow: hidden;
+ min-width: 10px;
// width:100%;//width: max-content;
}
@@ -100,10 +101,29 @@
border-left: dashed 1px #00000042;
}
+.treeViewItem-header {
+ border: transparent 1px solid;
+ display: flex;
+
+ .editableView-container-editing-oneLine {
+ min-width: 15px;
+ }
+ .documentView-node-topmost {
+ width: unset;
+ }
+ > svg {
+ display: none;
+ }
+
+}
+
.treeViewItem-header:hover {
.collectionTreeView-keyHeader {
display: inherit;
}
+ > svg {
+ display: inherit;
+ }
.treeViewItem-openRight {
display: inline-block;
@@ -119,18 +139,6 @@
}
}
-.treeViewItem-header {
- border: transparent 1px solid;
- display: flex;
-
- .editableView-container-editing-oneLine {
- min-width: 15px;
- }
- .documentView-node-topmost {
- width: unset;
- }
-}
-
.treeViewItem-header-above {
border-top: black 1px solid;
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 23fb259fc..a052d045c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -6,11 +6,10 @@ import { observer } from "mobx-react";
import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
-import { Document, listSpec, createSchema, makeInterface } from '../../../new_fields/Schema';
+import { Document, listSpec } from '../../../new_fields/Schema';
import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types';
-import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -19,7 +18,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";
@@ -32,7 +31,7 @@ import { Templates } from '../Templates';
import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
-import { CollectionViewType } from './CollectionView';
+import { CollectionViewType, CollectionView } from './CollectionView';
import { RichTextField } from '../../../new_fields/RichTextField';
import { DocumentView } from '../nodes/DocumentView';
@@ -139,13 +138,15 @@ class TreeView extends React.Component<TreeViewProps> {
@undoBatch @action remove = (document: Document, key: string) => {
return Doc.RemoveDocFromList(this.dataDoc, key, document);
}
+ @undoBatch @action removeDoc = (document: Document) => {
+ return Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), document);
+ }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer && this._treedropDisposer();
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()) {
@@ -183,21 +184,17 @@ class TreeView extends React.Component<TreeViewProps> {
SetValue={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false) || true;
Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
- //this.props.document.editTitle = undefined;
})}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- //EditableView.loadId = doc[Id];
Doc.SetInPlace(this.props.document, "editTitle", undefined, false);
- // this.props.document.editTitle = undefined;
- Doc.SetInPlace(this.props.document, "editTitle", true, false);
- //doc.editTitle = true;
+ Doc.SetInPlace(doc, "editTitle", true, false);
return this.props.addDocument(doc);
})}
onClick={() => {
SelectionManager.DeselectAll();
- Doc.UserDoc().SelectedDocs = new List([this.props.document]);
+ Doc.UserDoc().activeSelection = new List([this.props.document]);
return false;
}}
OnTab={undoBatch((shift?: boolean) => {
@@ -211,33 +208,6 @@ class TreeView extends React.Component<TreeViewProps> {
})}
/>)
- onWorkspaceContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view
- const sort = this.props.document[`${this.fieldKey}-sortAscending`];
- if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) {
- ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
- } else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
- ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" });
- if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
- }
- ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
- } else {
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
- ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
- }
- ContextMenu.Instance.addItem({ description: (sort ? "Sort Descending" : (sort === false ? "Unsort" : "Sort Ascending")), event: () => this.props.document[`${this.fieldKey}-sortAscending`] = (sort ? false : (sort === false ? undefined : true)), icon: "minus" });
- ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" });
- ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
- ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
- e.stopPropagation();
- e.preventDefault();
- }
- }
-
@undoBatch
treeDrop = (e: Event, de: DragManager.DropEvent) => {
const pt = [de.x, de.y];
@@ -258,7 +228,8 @@ class TreeView extends React.Component<TreeViewProps> {
addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
}
const movedDocs = (de.complete.docDragData.treeViewId === this.props.treeViewId[Id] ? de.complete.docDragData.draggedDocuments : de.complete.docDragData.droppedDocuments);
- return ((de.complete.docDragData.dropAction && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ?
+ const move = de.complete.docDragData.dropAction === "move" || de.complete.docDragData.dropAction;
+ return ((!move && (de.complete.docDragData.treeViewId !== this.props.treeViewId[Id])) || de.complete.docDragData.userDropAction) ?
de.complete.docDragData.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
: de.complete.docDragData.moveDocument ?
movedDocs.reduce((added, d) => de.complete.docDragData?.moveDocument?.(d, undefined, addDoc) || added, false)
@@ -357,11 +328,15 @@ class TreeView extends React.Component<TreeViewProps> {
const remDoc = (doc: Doc) => this.remove(doc, expandKey);
const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
const docs = expandKey === "links" ? this.childLinks : this.childDocs;
- return <ul key={expandKey + "more"}>
+ const sortKey = `${this.fieldKey}-sortAscending`;
+ return <ul key={expandKey + "more"} onClick={(e) => {
+ this.props.document[sortKey] = (this.props.document[sortKey] ? false : (this.props.document[sortKey] === false ? undefined : true));
+ e.stopPropagation();
+ }}>
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ StrCast(this.props.document.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
[...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
</ul >;
@@ -397,11 +372,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,11 +392,22 @@ 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>;
}
+
+ showContextMenu = (e: React.MouseEvent) => {
+ simulateMouseClick(this._docRef.current!.ContentDiv!, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ e.stopPropagation();
+ }
+ focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
+ contextMenuItems = () => {
+ const focusScript = ScriptField.MakeFunction(`DocFocus(self)`);
+ return [{ script: focusScript!, label: "Focus" }];
+ }
+ _docRef = React.createRef<DocumentView>();
/**
* Renders the EditableView title element for placement into the tree.
*/
@@ -429,19 +417,22 @@ class TreeView extends React.Component<TreeViewProps> {
const editTitle = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)");
const headerElements = (
- <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
- onPointerDown={action(() => {
- if (this.treeViewOpen) {
- this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
- this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
- this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
- this.childDocs ? this.fieldKey : "fields";
- }
- this.treeViewOpen = true;
- })}>
- {this.treeViewExpandedView}
- </span>);
- const openRight = (<div className="treeViewItem-openRight" onPointerDown={this.onPointerDown} onClick={this.openRight}>
+ <>
+ <FontAwesomeIcon icon="cog" size="sm" onClick={e => this.showContextMenu(e)}></FontAwesomeIcon>
+ <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
+ onPointerDown={action(() => {
+ if (this.treeViewOpen) {
+ this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
+ this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
+ this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
+ this.childDocs ? this.fieldKey : "fields";
+ }
+ this.treeViewOpen = true;
+ })}>
+ {this.treeViewExpandedView}
+ </span>
+ </>);
+ const openRight = (<div className="treeViewItem-openRight" onClick={this.openRight}>
<FontAwesomeIcon title="open in pane on right" icon="angle-right" size="lg" />
</div>);
return <>
@@ -451,11 +442,12 @@ 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") :
<DocumentView
+ ref={this._docRef}
Document={this.props.document}
DataDoc={undefined}
LibraryPath={this.props.libraryPath || []}
@@ -465,14 +457,15 @@ class TreeView extends React.Component<TreeViewProps> {
pinToPres={emptyFunction}
onClick={this.props.onChildClick || editTitle}
dropAction={this.props.dropAction}
- moveDocument={this.props.moveDocument}
- removeDocument={undefined}
+ moveDocument={this.move}
+ removeDocument={this.removeDoc}
ScreenToLocalTransform={this.getTransform}
ContentScaling={returnOne}
PanelWidth={returnZero}
PanelHeight={returnZero}
NativeHeight={returnZero}
NativeWidth={returnZero}
+ contextMenuItems={this.contextMenuItems}
renderDepth={1}
focus={emptyFunction}
parentActive={returnTrue}
@@ -480,7 +473,7 @@ class TreeView extends React.Component<TreeViewProps> {
bringToFront={emptyFunction}
dontRegisterView={BoolCast(this.props.treeViewId.dontRegisterChildren)}
ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
+ ContainingCollectionDoc={this.props.containingCollection}
/>}
</div >
{this.props.treeViewHideHeaderFields() ? (null) : headerElements}
@@ -489,14 +482,25 @@ class TreeView extends React.Component<TreeViewProps> {
}
render() {
+ const sorting = this.props.document[`${this.fieldKey}-sortAscending`];
setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0);
- return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onContextMenu={this.onWorkspaceContextMenu}>
+ return <div className="treeViewItem-container" ref={this.createTreeDropTarget}>
<li className="collection-child">
- <div className="treeViewItem-header" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ <div className="treeViewItem-header" ref={this._header} onClick={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }} onPointerDown={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
{this.renderBullet}
{this.renderTitle}
</div>
- <div className="treeViewItem-border">
+ <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
{!this.treeViewOpen || this.props.renderedIds.indexOf(this.props.document[Id]) !== -1 ? (null) : this.renderContent}
</div>
</li>
@@ -651,14 +655,14 @@ export type collectionTreeViewProps = {
};
@observer
-export class CollectionTreeView extends CollectionSubView(Document, undefined as any as collectionTreeViewProps) {
+export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
@computed get dataDoc() { return this.props.DataDoc || this.props.Document; }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
- this.treedropDisposer && this.treedropDisposer();
+ this.treedropDisposer?.();
if (this._mainEle = ele) {
this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
}
@@ -666,7 +670,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
componentWillUnmount() {
super.componentWillUnmount();
- this.treedropDisposer && this.treedropDisposer();
+ this.treedropDisposer?.();
}
@action
@@ -691,14 +695,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
+ if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myWorkspaces) {
ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- } else if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.recentlyClosed) {
- ContextMenu.Instance.addItem({ description: "Clear All", event: () => CurrentUserUtils.UserDocument.recentlyClosed = new List<Doc>(), icon: "plus" });
+ } else if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myRecentlyClosed) {
+ ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
@@ -707,7 +711,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
- ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
}
ContextMenu.Instance.addItem({
description: "Buxton Layout", icon: "eye", event: () => {
@@ -739,14 +743,14 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
heroView._showTitle = "title";
heroView._showTitleHover = "titlehover";
- Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
+ Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
Docs.Create.FontIconDocument({
title: "hero view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
dragFactory: heroView, removeDropProperties: new List<string>(["dropAction"]), icon: "portrait",
onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
}));
- Doc.AddDocToList(Doc.UserDoc().expandingButtons as Doc, "data",
+ Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data",
Docs.Create.FontIconDocument({
title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias",
dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), icon: "file-alt",
@@ -754,7 +758,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";
@@ -764,8 +768,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" });
}
@@ -782,6 +785,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
}
render() {
+ if (!(this.props.Document instanceof Doc)) return (null);
const dropAction = StrCast(this.props.Document.childDropAction) as dropActionType;
const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
@@ -810,6 +814,7 @@ export class CollectionTreeView extends CollectionSubView(Document, undefined as
Doc.SetInPlace(this.dataDoc, "title", value, false);
const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
EditableView.loadId = doc[Id];
+ Doc.SetInPlace(doc, "editTitle", true, false);
this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
})} />)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
@@ -818,7 +823,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 +844,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 b92c5fdd1..d43dd387a 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;
@@ -21,7 +20,7 @@
top: 55%;
border: 1px black solid;
z-index: 2;
- left: -10px;
+ right: -10px;
}
.collectionTimeView-treeView {
display: flex;
@@ -29,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 d1dc32829..4f1242c33 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;
@@ -106,7 +105,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
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) ? true : false;
whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
@@ -193,55 +192,55 @@ export class CollectionView extends Touchable<FieldViewProps> {
}
+ setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
+ const existingVm = ContextMenu.Instance.findByDescription(category);
+ const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+
+ subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
+ if (addExtras && CollectionView._safeMode) {
+ ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+ }
+ subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
+ subItems.push({ description: "Treeview", event: () => func(CollectionViewType.Tree), icon: "tree" });
+ subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" });
+ subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
+ subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" });
+ subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
+ subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
+ subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
+ subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
+ subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
+ subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+ if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
+ subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
+ }
+ addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
+ !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: subItems, icon: "eye" });
+ }
+
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
- const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
- subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" });
- if (CollectionView._safeMode) {
- ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" });
- }
- subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" });
- subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" });
- subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
- subItems.push({
- description: "Stacking (AutoHeight)", event: () => {
- this.props.Document._viewType = CollectionViewType.Stacking;
- this.props.Document._autoHeight = true;
- }, icon: "ellipsis-v"
- });
- subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" });
- subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" });
- subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" });
- subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" });
- subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
- subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
- subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
- switch (this.props.Document._viewType) {
- case CollectionViewType.Freeform: {
- subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
- break;
- }
- }
- subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
- !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
- const existing = ContextMenu.Instance.findByDescription("Layout...");
+ this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true);
+ this.setupViewTypes("Open New Perspective...", vtype => {
+ const newRendition = Doc.MakeAlias(this.props.Document);
+ newRendition._viewType = vtype;
+ this.props.addDocTab(newRendition, "onRight");
+ return newRendition;
+ }, false);
+
+ const existing = ContextMenu.Instance.findByDescription("Options...");
const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
if (this.props.Document.childLayout instanceof Doc) {
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" });
- !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
-
- const open = ContextMenu.Instance.findByDescription("Open...");
- const openItems = open && "subitems" in open ? open.subitems : [];
- !open && ContextMenu.Instance.addItem({ description: "Open...", subitems: openItems, icon: "hand-point-right" });
+ !existing && ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "hand-point-right" });
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -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,11 +389,11 @@ 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));
}
- 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)";
@@ -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 ba95dce00..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 {
@@ -48,8 +45,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
};
_narrativeCommand = {
params: ["target", "source"], title: "=> 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 = {
@@ -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>
@@ -402,60 +286,22 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
onPointerDown={stopPropagation}
onChange={this.viewChanged}
value={StrCast(this.props.CollectionView.props.Document._viewType)}>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="freeform">Freeform</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="schema">Schema</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="tree">Tree</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="stacking">Stacking</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="masonry">Masonry</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="multicolumn">MultiCol</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="multirow">MultiRow</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="time">Pivot/Time</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="carousel">Carousel</option>
+ {Object.values(CollectionViewType).map(type => ["invalid", "docking"].includes(type) ? (null) : (
+ <option
+ key={Utils.GenerateGuid()}
+ className="collectionViewBaseChrome-viewOption"
+ onPointerDown={stopPropagation}
+ value={type}>
+ {type[0].toUpperCase() + type.substring(1)}
+ </option>
+ ))}
</select>
</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/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 5c9d2b0dd..10c6ead1a 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import './ParentDocumentSelector.scss';
import { Doc } from "../../../new_fields/Doc";
import { observer } from "mobx-react";
-import { observable, action, runInAction, trace, computed } from "mobx";
+import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx";
import { Id } from "../../../new_fields/FieldSymbols";
import { SearchUtil } from "../../util/SearchUtil";
import { CollectionDockingView } from "./CollectionDockingView";
@@ -31,13 +31,14 @@ type SelectorProps = {
export class SelectorContextMenu extends React.Component<SelectorProps> {
@observable private _docs: { col: Doc, target: Doc }[] = [];
@observable private _otherDocs: { col: Doc, target: Doc }[] = [];
+ _reaction: IReactionDisposer | undefined;
- constructor(props: SelectorProps) {
- super(props);
-
- this.fetchDocuments();
+ componentDidMount() {
+ this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true });
+ }
+ componentWillUnmount() {
+ this._reaction?.();
}
-
async fetchDocuments() {
const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.Document[Id]}"` });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 75af11537..05111adb4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -4,6 +4,7 @@
pointer-events: all;
stroke-width: 3px;
transition: opacity 0.5s ease-in;
+ fill: transparent;
}
.collectionfreeformlinkview-linkCircle {
stroke: rgb(0,0,0);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 09fc5148e..cf12ef382 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -25,7 +25,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)],
action(() => {
setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
- setTimeout(action(() => this._opacity = 0.05), 750); // this will unhighlight the link line.
+ setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv!);
@@ -81,6 +81,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
render() {
+ this.props.A.props.ScreenToLocalTransform().transform(this.props.B.props.ScreenToLocalTransform());
const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("linkAnchorBox-cont") : [];
const a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
@@ -93,17 +94,26 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
apt.point.x, apt.point.y);
const pt1 = [apt.point.x, apt.point.y];
const pt2 = [bpt.point.x, bpt.point.y];
+ const pt1vec = [pt1[0] - (a.left + a.width / 2), pt1[1] - (a.top + a.height / 2)];
+ const pt2vec = [pt2[0] - (b.left + b.width / 2), pt2[1] - (b.top + b.height / 2)];
+ const pt1len = Math.sqrt((pt1vec[0] * pt1vec[0]) + (pt1vec[1] * pt1vec[1]));
+ const pt2len = Math.sqrt((pt2vec[0] * pt2vec[0]) + (pt2vec[1] * pt2vec[1]));
+ const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 3;
+ const pt1norm = [pt1vec[0] / pt1len * ptlen, pt1vec[1] / pt1len * ptlen];
+ const pt2norm = [pt2vec[0] / pt2len * ptlen, pt2vec[1] / pt2len * ptlen];
const aActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
const bActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
const text = StrCast(this.props.A.props.Document.linkRelationship);
- return !aActive && !bActive ? (null) : (<>
+ return !a.width || !b.width || ((!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
<text x={(pt1[0] + pt2[0]) / 2} y={(pt1[1] + pt2[1]) / 2}>
{text !== "-ungrouped-" ? text : ""}
</text>
- <line key="linkLine" className="collectionfreeformlinkview-linkLine"
+ <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
+ d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
+ {/* <line key="linkLine" className="collectionfreeformlinkview-linkLine"
style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
x1={`${pt1[0]}`} y1={`${pt1[1]}`}
- x2={`${pt2[0]}`} y2={`${pt2[1]}`} />
+ x2={`${pt2[0]}`} y2={`${pt2[1]}`} /> */}
</>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index d12f93f15..702b02a20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -31,14 +31,14 @@ export class CollectionFreeFormLinksView extends React.Component {
}, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
return connections.filter(c =>
c.a.props.layoutKey && c.b.props.layoutKey && c.a.props.Document.type === DocumentType.LINK &&
- c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed
+ c.a.props.bringToFront !== emptyFunction && c.b.props.bringToFront !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed
).map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
}
render() {
return <div className="collectionfreeformlinksview-container">
<svg className="collectionfreeformlinksview-svgCanvas">
- {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
+ {this.uniqueConnections}
</svg>
{this.props.children}
</div>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index e1516b468..a00311a9c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -36,6 +36,7 @@
height: 100%;
display: flex;
align-items: center;
+ overflow: hidden;
.collectionfreeformview-placeholderSpan {
font-size: 32;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7dca19073..8ead1c300 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";
@@ -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";
@@ -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);
@@ -68,10 +69,12 @@ 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
-export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, undefined as any as collectionFreeformViewProps) {
+export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) {
private _lastX: number = 0;
private _lastY: number = 0;
private _downX: number = 0;
@@ -800,7 +803,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;
}
@@ -816,9 +819,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
@@ -846,7 +849,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 {
@@ -860,7 +863,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,
@@ -905,7 +908,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;
@@ -990,6 +994,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)}
@@ -1043,16 +1048,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
onContextMenu = (e: React.MouseEvent) => {
if (this.props.children && this.props.annotationsKey) return;
- const layoutItems: ContextMenuProps[] = [];
-
- layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
- layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
- layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
- layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
- layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
+ const options = ContextMenu.Instance.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+
+ optionItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
+ optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" });
+ optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+ optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
+ optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
// layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
- layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
- layoutItems.push({
+ optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" });
+ optionItems.push({
description: "Import document", icon: "upload", event: ({ x, y }) => {
const input = document.createElement("input");
input.type = "file";
@@ -1080,7 +1086,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument, u
}
});
- ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
}
private childViews = () => {
@@ -1105,10 +1111,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 <MarqueeView {...this.props} nudge={this.nudge} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
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;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5a6c8573b..454c3a5f2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -31,7 +31,7 @@ interface MarqueeViewProps {
addLiveTextDocument: (doc: Doc) => 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;
}
@@ -309,7 +309,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
}
- getCollection = (selected: Doc[], asTemplate: boolean, isBackground: boolean = false) => {
+ 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;