aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss20
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx53
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx14
-rw-r--r--src/client/views/collections/CollectionTimeView.scss18
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx113
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx29
-rw-r--r--src/client/views/collections/CollectionView.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx85
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx42
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx22
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx2
15 files changed, 247 insertions, 203 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 4815f1a59..ad369bbff 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -14,27 +14,23 @@
width: 100%;
}
}
-.carouselView-back {
+.carouselView-back, .carouselView-fwd {
position: absolute;
display: flex;
- left: 0;
top: 50%;
width: 30;
height: 30;
- background: lightgray;
align-items: center;
border-radius: 5px;
justify-content: center;
+ background : rgba(255, 255, 255, 0.46);
}
-.carouselView-fwd {
- position: absolute;
- display: flex;
+.carouselView-fwd {
right: 0;
- top: 50%;
- width: 30;
- height: 30;
+}
+.carouselView-back {
+ left: 0;
+}
+.carouselView-back:hover, .carouselView-fwd:hover {
background: lightgray;
- align-items: center;
- border-radius: 5px;
- justify-content: center;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d772ace23..d21ae32bc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,25 +4,25 @@ import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
+import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types";
-import { emptyFunction, Utils } from "../../../Utils";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from "../../../new_fields/util";
+import { Utils } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
-import { TraceMobx } from "../../../new_fields/util";
import { CollectionViewType } from "./CollectionView";
@observer
@@ -55,7 +55,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
this._docXfs.length = 0;
return docs.map((d, i) => {
const height = () => this.getDocHeight(d);
- const width = () => this.widthScale * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ const width = () => this.getDocWidth(d);
const dref = React.createRef<HTMLDivElement>();
const dxf = () => this.getDocTransform(d, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
@@ -154,11 +154,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
- const layoutDoc = Doc.Layout(doc);
+ const layoutDoc = Doc.Layout(doc, this.props.childLayoutTemplate?.());
const height = () => this.getDocHeight(doc);
return <ContentFittingDocumentView
Document={doc}
DataDocument={dataDoc}
+ LayoutDoc={this.props.childLayoutTemplate}
LibraryPath={this.props.LibraryPath}
renderDepth={this.props.renderDepth + 1}
fitToBox={this.props.fitToBox}
@@ -178,19 +179,26 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
pinToPres={this.props.pinToPres}>
</ContentFittingDocumentView>;
}
+
+ getDocWidth(d?: Doc) {
+ if (!d) return 0;
+ const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+ const nw = NumCast(layoutDoc._nativeWidth);
+ return Math.min(nw && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ }
getDocHeight(d?: Doc) {
if (!d) return 0;
- const layoutDoc = Doc.Layout(d);
+ let layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const nw = NumCast(layoutDoc._nativeWidth);
const nh = NumCast(layoutDoc._nativeHeight);
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
if (!layoutDoc.ignoreAspect && !layoutDoc._fitWidth && nw && nh) {
const aspect = nw && nh ? nh / nw : 1;
- if (!(d._nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+ if (!(!layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
- return layoutDoc._fitWidth ? !layoutDoc._nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin :
- Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
+ return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
+ Math.min(wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
}
columnDividerDown = (e: React.PointerEvent) => {
@@ -376,16 +384,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1]));
}
- @computed get contentScale() {
- const heightExtra = this.heightPercent > 1 ? this.heightPercent : 1;
- return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), heightExtra * this.props.Document[HeightSym]() / this.props.PanelHeight());
- }
- @computed get widthScale() {
- return this.heightPercent < 1 ? Math.max(1, this.contentScale) : 1;
- }
- @computed get heightPercent() {
- return this.props.PanelHeight() / this.layoutDoc[HeightSym]();
- }
+
+ @computed get scaling() { return !this.props.Document._nativeWidth ? 1 : this.props.PanelHeight() / NumCast(this.props.Document._nativeHeight); }
+
render() {
TraceMobx();
const editableViewProps = {
@@ -399,9 +400,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
ref={this.createRef}
style={{
overflowY: this.props.active() ? "auto" : "hidden",
- transform: `scale(${Math.min(1, this.heightPercent)})`,
- height: `${100 * Math.max(1, this.contentScale)}%`,
- width: `${100 * this.widthScale}%`,
+ transform: `scale(${this.scaling}`,
+ height: `${1 / this.scaling * 100}%`,
+ width: `${1 / this.scaling * 100}%`,
transformOrigin: "top left",
}}
onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 21982f1ca..3fc05c6b7 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -285,8 +285,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
description: ":" + fieldKey, event: () => {
const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
if (created) {
- if (this.props.parent.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
+ const container = this.props.parent.Document.resolvedDataDoc ? Doc.GetProto(this.props.parent.Document) : this.props.parent.Document;
+ if (container.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, container);
+ return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
}
return this.props.parent.props.addDocument(created);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 62b9e8380..20941493f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,7 +1,7 @@
import { action, computed, IReactionDisposer, reaction, trace } from "mobx";
import * as rp from 'request-promise';
import CursorField from "../../../new_fields/CursorField";
-import { Doc, DocListCast, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
@@ -33,7 +33,6 @@ export interface CollectionViewProps extends FieldViewProps {
PanelWidth: () => number;
PanelHeight: () => number;
VisibleHeight?: () => number;
- chromeCollapsed: boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
fieldKey: string;
}
@@ -167,6 +166,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return true;
}
let added = false;
+ if (this.props.Document._freezeOnDrop) {
+ de.complete.docDragData?.droppedDocuments.forEach(drop => Doc.freezeNativeDimensions(drop, drop[WidthSym](), drop[HeightSym]()));
+ }
if (docDragData.dropAction || docDragData.userDropAction) {
added = docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
} else if (docDragData.moveDocument) {
@@ -230,8 +232,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const split = img.split("src=\"")[1].split("\"")[0];
let source = split;
if (split.startsWith("data:image") && split.includes("base64")) {
- const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
- source = Utils.prepend(clientAccessPath);
+ const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+ source = Utils.prepend(accessPaths.agnostic.client);
}
const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
ImageUtils.ExtractExif(doc);
@@ -310,9 +312,9 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const dropFileName = file ? file.name : "-empty-";
promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => {
results.map(action((result: any) => {
- const { clientAccessPath, nativeWidth, nativeHeight, contentSize } = result;
+ const { accessPaths, nativeWidth, nativeHeight, contentSize } = result;
const full = { ...options, _width: 300, title: dropFileName };
- const pathname = Utils.prepend(clientAccessPath);
+ const pathname = Utils.prepend(accessPaths.agnostic.client);
Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
if (doc) {
const proto = Doc.GetProto(doc);
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index a5ce73a92..2dffb3ea0 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -1,21 +1,26 @@
-.collectionTimeView, .collectionTimeView-pivot {
+.collectionTimeView,
+.collectionTimeView-pivot {
display: flex;
flex-direction: row;
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
+
.collectionTimeView-backBtn {
background: green;
display: inline;
margin-right: 20px;
}
+
.collectionFreeform-customText {
text-align: left;
}
+
.collectionFreeform-customDiv {
position: absolute;
}
+
.collectionTimeView-thumb {
position: absolute;
width: 30px;
@@ -28,19 +33,21 @@
border-radius: 9px;
opacity: 0.25;
}
+
.collectionTimeView-thumb-min {
- margin-left:25%;
+ margin-left: 25%;
}
+
.collectionTimeView-thumb-max {
- margin-left:75%;
+ margin-left: 75%;
}
+
.collectionTimeView-thumb-mid {
- margin-left:50%;
+ margin-left: 50%;
}
.collectionTimeView-flyout {
width: 400px;
- height: 300px;
display: inline-block;
.collectionTimeView-flyout-item {
@@ -119,6 +126,7 @@
left: -10px;
}
}
+
.collectionTimeView-pivot {
.collectionFreeform-customText {
text-align: center;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4983acbc2..50e297f0b 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,27 +1,29 @@
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, trace, runInAction } from "mobx";
+import { action, computed, observable, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
-import { Set } from "typescript-collections";
-import { Doc, DocListCast, Field } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
+import { ObjectField } from "../../../new_fields/ObjectField";
import { RichTextField } from "../../../new_fields/RichTextField";
import { listSpec } from "../../../new_fields/Schema";
import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import { Scripting } from "../../util/Scripting";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
-import { anchorPoints, Flyout } from "../TemplateMenu";
import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines";
import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTimeView.scss";
-import React = require("react");
import { CollectionTreeView } from "./CollectionTreeView";
-import { ObjectField } from "../../../new_fields/ObjectField";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+import React = require("react");
@observer
export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -29,11 +31,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
@observable _layoutEngine = "pivot";
componentDidMount() {
+ this.props.Document._freezeOnDrop = true;
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;
if (!this.props.Document._facetCollection) {
- const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true });
+ const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true, treeViewHideHeaderFields: true });
facetCollection.target = this.props.Document;
- this.props.Document.excludeFields = new List<string>(["_facetCollection", "_docFilter"]);
+ this.props.Document.excludeFields = new List<string>(["_facetCollection", "_docFilters"]);
const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)";
const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailView'); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
@@ -43,7 +46,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
this.props.Document._fitToBox = true;
}
if (!this.props.Document.onViewDefClick) {
- this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" })
+ this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
}
}
@@ -54,7 +57,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const facets = new Set<string>();
this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key)));
- return facets.toArray();
+ return Array.from(facets);
}
/**
@@ -66,19 +69,58 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader);
if (found !== -1) {
(facetCollection.data as List<Doc>).splice(found, 1);
- const docFilter = Cast(this.props.Document._docFilter, listSpec("string"));
+ const docFilter = Cast(this.props.Document._docFilters, listSpec("string"));
if (docFilter) {
let index: number;
while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
docFilter.splice(index, 3);
}
}
+ const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"));
+ if (docRangeFilters) {
+ let index: number;
+ while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) {
+ docRangeFilters.splice(index, 3);
+ }
+ }
} else {
- const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
- const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
- const params = { layoutDoc: Doc.name, dataDoc: Doc.name, };
- newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables);
- Doc.AddDocToList(facetCollection, "data", newFacet);
+ const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
+ const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
+ set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
+
+ let nonNumbers = 0;
+ let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+ facetValues.map(val => {
+ const num = Number(val);
+ if (Number.isNaN(num)) {
+ nonNumbers++;
+ } else {
+ minVal = Math.min(num, minVal);
+ maxVal = Math.max(num, maxVal);
+ }
+ });
+ if (nonNumbers / allCollectionDocs.length < .1) {
+ const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
+ const newFacet = Docs.Create.SliderDocument({ title: facetHeader });
+ Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+ newFacet.treeViewExpandedView = "layout";
+ newFacet.treeViewOpen = true;
+ newFacet._sliderMin = ranged === undefined ? minVal : ranged[0];
+ newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1];
+ newFacet._sliderMinThumb = minVal;
+ newFacet._sliderMaxThumb = maxVal;
+ newFacet.target = this.props.Document;
+ const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
+ newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
+
+ Doc.AddDocToList(facetCollection, "data", newFacet);
+ } else {
+ const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
+ const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
+ const params = { layoutDoc: Doc.name, dataDoc: Doc.name, };
+ newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables);
+ Doc.AddDocToList(facetCollection, "data", newFacet);
+ }
}
}
}
@@ -120,9 +162,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
pair.layout[fieldKey] instanceof RichTextField ||
typeof (pair.layout[fieldKey]) === "number" ||
typeof (pair.layout[fieldKey]) === "string").map(fieldKey => keySet.add(fieldKey)));
- keySet.toArray().map(fieldKey =>
+ Array.from(keySet).map(fieldKey =>
docItems.push({ description: ":" + fieldKey, event: () => this.props.Document._pivotField = fieldKey, icon: "compress-arrows-alt" }));
- docItems.push({ description: ":(null)", event: () => this.props.Document._pivotField = undefined, icon: "compress-arrows-alt" })
+ docItems.push({ description: ":(null)", event: () => this.props.Document._pivotField = undefined, icon: "compress-arrows-alt" });
ContextMenu.Instance.addItem({ description: "Pivot Fields ...", subitems: docItems, icon: "eye" });
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
ContextMenu.Instance.displayMenu(x, y, ":");
@@ -148,6 +190,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const minReq = NumCast(this.props.Document[this.props.fieldKey + "-timelineMinReq"], NumCast(this.props.Document[this.props.fieldKey + "-timelineMin"], 0));
const maxReq = NumCast(this.props.Document[this.props.fieldKey + "-timelineMaxReq"], NumCast(this.props.Document[this.props.fieldKey + "-timelineMax"], 10));
this.props.Document[this.props.fieldKey + "-timelineMinReq"] = minReq + (maxReq - minReq) * delta / this.props.PanelWidth();
+ this.props.Document[this.props.fieldKey + "-timelineSpan"] = undefined;
}
onMinUp = (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onMinMove);
@@ -170,6 +213,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const minReq = NumCast(this.props.Document[this.props.fieldKey + "-timelineMinReq"], NumCast(this.props.Document[this.props.fieldKey + "-timelineMin"], 0));
const maxReq = NumCast(this.props.Document[this.props.fieldKey + "-timelineMaxReq"], NumCast(this.props.Document[this.props.fieldKey + "-timelineMax"], 10));
this.props.Document[this.props.fieldKey + "-timelineMaxReq"] = maxReq + (maxReq - minReq) * delta / this.props.PanelWidth();
+ this.props.Document[this.props.fieldKey + "-timelineSpan"] = undefined;
}
onMaxUp = (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onMaxMove);
@@ -209,7 +253,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
trace();
const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null);
const flyout = (
- <div className="collectionTimeView-flyout" style={{ width: `${this._facetWidth}` }}>
+ <div className="collectionTimeView-flyout" style={{ width: `${this._facetWidth}`, height: this.props.PanelHeight() - 30, display: "block" }} onWheel={e => e.stopPropagation()}>
{this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
<input type="checkbox" onChange={e => { }} checked={DocListCast((this.props.Document._facetCollection as Doc)?.data).some(d => d.title === facet)} />
<span className="checkmark" />
@@ -227,7 +271,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
</Flyout>
</div>
<div className="collectionTimeView-tree" key="tree">
- <CollectionTreeView {...this.props} Document={facetCollection} />
+ <CollectionTreeView {...this.props} PanelWidth={() => this._facetWidth} Document={facetCollection} />
</div>
</div>;
}
@@ -240,9 +284,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const layoutItems: ContextMenuProps[] = [];
const doc = this.props.Document;
- layoutItems.push({ description: "Force Timeline", event: () => { doc._forceRenderEngine = "timeline" }, icon: "compress-arrows-alt" });
- layoutItems.push({ description: "Force Pivot", event: () => { doc._forceRenderEngine = "pivot" }, icon: "compress-arrows-alt" });
- layoutItems.push({ description: "Auto Time/Pivot layout", event: () => { doc._forceRenderEngine = undefined }, icon: "compress-arrows-alt" });
+ layoutItems.push({ description: "Force Timeline", event: () => { doc._forceRenderEngine = "timeline"; }, icon: "compress-arrows-alt" });
+ layoutItems.push({ description: "Force Pivot", event: () => { doc._forceRenderEngine = "pivot"; }, icon: "compress-arrows-alt" });
+ 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" });
@@ -289,14 +333,16 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
<div className={"collectionTimeView" + (doTimeline ? "" : "-pivot")} onContextMenu={this.specificMenu}
style={{ height: `calc(100% - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
<div className={"pivotKeyEntry"}>
- <button className="collectionTimeView-backBtn" style={{ width: 50, height: 20, background: "green" }}
+ <button className="collectionTimeView-backBtn"
onClick={action(() => {
- let pfilterIndex = NumCast(this.props.Document._pfilterIndex);
- if (pfilterIndex > 0) {
- this.props.Document._docFilter = ObjectField.MakeCopy(this.props.Document["_pfilter" + --pfilterIndex] as ObjectField);
- this.props.Document._pfilterIndex = pfilterIndex;
+ 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._docFilter = new List([]);
+ this.props.Document._docFilters = new List([]);
}
})}>
back
@@ -320,11 +366,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
}
Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
- let pfilterIndex = NumCast(pivotDoc._pfilterIndex);
- pivotDoc["_pfilter" + pfilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilter as ObjectField);
- pivotDoc._pfilterIndex = ++pfilterIndex;
+ let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
+ pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
+ pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
+ pivotDoc._prevFilterIndex = ++prevFilterIndex;
runInAction(() => {
- pivotDoc._docFilter = new List();
+ pivotDoc._docFilters = new List();
(bounds.payload as string[]).map(filterVal =>
Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check"));
});
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 001064b30..be2947dff 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -60,7 +60,7 @@ export interface TreeViewProps {
treeViewId: Doc;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
- hideHeaderFields: () => boolean;
+ treeViewHideHeaderFields: () => boolean;
preventTreeViewOpen: boolean;
renderedIds: string[];
onCheckedClick?: ScriptField;
@@ -292,7 +292,7 @@ class TreeView extends React.Component<TreeViewProps> {
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick);
} else {
contentElement = <EditableView
@@ -335,7 +335,7 @@ class TreeView extends React.Component<TreeViewProps> {
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.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.hideHeaderFields, this.props.preventTreeViewOpen,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
@@ -425,7 +425,7 @@ class TreeView extends React.Component<TreeViewProps> {
}} >
{this.editableView("title")}
</div >
- {this.props.hideHeaderFields() ? (null) : headerElements}
+ {this.props.treeViewHideHeaderFields() ? (null) : headerElements}
{openRight}
</>;
}
@@ -464,7 +464,7 @@ class TreeView extends React.Component<TreeViewProps> {
panelWidth: () => number,
ChromeHeight: undefined | (() => number),
renderDepth: number,
- hideHeaderFields: () => boolean,
+ treeViewHideHeaderFields: () => boolean,
preventTreeViewOpen: boolean,
renderedIds: string[],
libraryPath: Doc[] | undefined,
@@ -574,7 +574,7 @@ class TreeView extends React.Component<TreeViewProps> {
outerXf={outerXf}
parentKey={key}
active={active}
- hideHeaderFields={hideHeaderFields}
+ treeViewHideHeaderFields={treeViewHideHeaderFields}
preventTreeViewOpen={preventTreeViewOpen}
renderedIds={renderedIds} />;
});
@@ -625,7 +625,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
} else {
const layoutItems: ContextMenuProps[] = [];
layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
- layoutItems.push({ description: (this.props.Document.hideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.hideHeaderFields = !this.props.Document.hideHeaderFields, 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" });
}
@@ -633,11 +633,10 @@ export class CollectionTreeView extends CollectionSubView(Document) {
description: "Buxton Layout", icon: "eye", event: () => {
DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
DocListCast(d.data).map((img, i) => {
- const caption = (d.captions as any)[i]?.data;
- if (caption instanceof ObjectField) {
- Doc.GetProto(img).caption = ObjectField.MakeCopy(caption);
+ const caption = (d.captions as any)[i];
+ if (caption) {
+ Doc.GetProto(img).caption = caption;
}
- d.captions = undefined;
});
});
const { TextDocument, ImageDocument, CarouselDocument, TreeDocument } = Docs.Create;
@@ -649,13 +648,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
const detailView = Docs.Create.StackingDocument([
CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }),
textDoc,
- TextDocument("", { title: "short_description", _autoHeight: true }),
+ TextDocument("", { title: "shortDescription", _autoHeight: true }),
TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true })
], { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" });
textDoc.data = new RichTextField(detailedTemplate, "year company");
detailView.isTemplateDoc = makeTemplate(detailView);
-
const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
heroView.proto!.layout = ImageBox.LayoutString("hero");
heroView.showTitle = "title";
@@ -673,7 +671,6 @@ export class CollectionTreeView extends CollectionSubView(Document) {
dragFactory: detailView, removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "file-alt"
}));
-
Document.childLayout = heroView;
Document.childDetailed = detailView;
Document._viewType = CollectionViewType.Time;
@@ -732,7 +729,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
{
TreeView.GetChildElements(this.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.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.hideHeaderFields),
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields),
BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
}
</ul>
@@ -764,7 +761,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey
});
Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
- const docFilters = Cast(layoutDoc._docFilter, listSpec("string"), []);
+ const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
for (let i = 0; i < docFilters.length; i += 3) {
const [header, value, state] = docFilters.slice(i, i + 3);
if (header === facetHeader && value === facetValue) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bdd908807..8dd844711 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
import Lightbox from 'react-image-lightbox-with-rotate';
import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
+import { Doc, DocListCast, DataSym } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { listSpec } from '../../../new_fields/Schema';
import { BoolCast, Cast, StrCast, NumCast } from '../../../new_fields/Types';
@@ -54,8 +54,7 @@ export enum CollectionViewType {
Time,
Carousel,
Linear,
- Staff,
- Timeline
+ Staff
}
export namespace CollectionViewType {
@@ -116,7 +115,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
// chrome status is one of disabled, collapsed, or visible. this determines initial state from document
// chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
const chromeStatus = this.props.Document._chromeStatus;
- if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
+ if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed" || chromeStatus === "replaced")) {
runInAction(() => this._collapsed = true);
}
});
@@ -133,7 +132,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
addDocument(doc: Doc): boolean {
- const targetDataDoc = Doc.GetProto(this.props.DataDoc || this.props.Document);
+ const targetDataDoc = this.props.Document[DataSym];
Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
Doc.GetProto(doc).lastOpened = new DateField;
@@ -144,7 +143,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
removeDocument(doc: Doc): boolean {
const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- const value = Cast((this.props.DataDoc || this.props.Document)[this.props.fieldKey], listSpec(Doc), []);
+ const value = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
@@ -176,14 +175,14 @@ export class CollectionView extends Touchable<FieldViewProps> {
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- const props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ const props = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
switch (type) {
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
- case CollectionViewType.Staff: return (<CollectionStaffView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
- case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
- case CollectionViewType.Multirow: return (<CollectionMultirowView chromeCollapsed={true} key="rpwview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
+ case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
+ case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />);
+ case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />);
case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
@@ -202,7 +201,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
// currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- const chrome = this.props.Document._chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
+ const chrome = this.props.Document._chromeStatus === "disabled" || this.props.Document._chromeStatus === "replaced" || type === CollectionViewType.Docking ? (null) :
<CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />;
return [chrome, this.SubViewHelper(type, renderProps)];
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index baf09fe5b..050ca8347 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -43,6 +43,9 @@ export interface ViewDefResult {
bounds?: ViewDefBounds;
}
function toLabel(target: FieldResult<Field>) {
+ if (typeof target === "number" || Number(target)) {
+ return Number(target).toFixed(2).toString();
+ }
if (target instanceof ObjectField || target instanceof RefField) {
return target[ToString]();
}
@@ -186,7 +189,7 @@ export function computePivotLayout(
const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
const dividers = sortedPivotKeys.map((key, i) =>
- ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
+ ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
groupNames.push(...dividers);
return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childDocs.filter(c => !filterDocs.includes(c)));
}
@@ -211,8 +214,8 @@ export function computeTimelineLayout(
const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null);
- const minTimeReq = curTime === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTimeSpan && (curTime - curTimeSpan);
- const maxTimeReq = curTime === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTimeSpan && (curTime + curTimeSpan);
+ const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTime && (curTime - curTimeSpan);
+ const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTime && (curTime + curTimeSpan);
const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
const fontHeight = panelDim[1] > 58 ? 30 : panelDim[1] / 2;
const findStack = (time: number, stack: number[]) => {
@@ -220,11 +223,11 @@ export function computeTimelineLayout(
return index === -1 ? stack.length : index;
}
- let minTime = Number.MAX_VALUE;
- let maxTime = -Number.MAX_VALUE;
+ let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
+ let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
filterDocs.map(doc => {
const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey])));
- if (!(Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq))) {
+ if (!Number.isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
pivotDateGroups.get(num)!.push(doc);
minTime = Math.min(num, minTime);
@@ -254,10 +257,10 @@ export function computeTimelineLayout(
let prevKey = Math.floor(minTime);
if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
- groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
}
if (!sortedKeys.length && curTime !== undefined) {
- groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
}
const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -265,7 +268,7 @@ export function computeTimelineLayout(
let zind = 0;
sortedKeys.forEach(key => {
if (curTime !== undefined && curTime > prevKey && curTime <= key) {
- groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+ groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
}
const keyDocs = pivotDateGroups.get(key)!;
x += scaling * (key - prevKey);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index bb9ae4326..92fa2781c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -8,74 +8,65 @@ import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormView.scss";
import React = require("react");
import v5 = require("uuid/v5");
+import { computed } from "mobx";
+import { FieldResult } from "../../../../new_fields/Doc";
+import { List } from "../../../../new_fields/List";
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
- protected getCursors(): CursorField[] {
+ @computed protected get cursors(): CursorField[] {
const doc = this.props.Document;
- const id = CurrentUserUtils.id;
- if (!id) {
+ let cursors: FieldResult<List<CursorField>>;
+ const { id } = CurrentUserUtils;
+ if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
return [];
}
-
- const cursors = Cast(doc.cursors, listSpec(CursorField));
-
const now = mobxUtils.now();
- // const now = Date.now();
- return (cursors || []).filter(cursor => cursor.data.metadata.id !== id && (now - cursor.data.metadata.timestamp) < 1000);
+ return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && (now - metadata.timestamp) < 1000);
}
- private crosshairs?: HTMLCanvasElement;
- drawCrosshairs = (backgroundColor: string) => {
- if (this.crosshairs) {
- const ctx = this.crosshairs.getContext('2d');
- if (ctx) {
- ctx.fillStyle = backgroundColor;
- ctx.fillRect(0, 0, 20, 20);
-
- ctx.fillStyle = "black";
- ctx.lineWidth = 0.5;
-
- ctx.beginPath();
+ @computed get renderedCursors() {
+ return this.cursors.map(({ data: { metadata, position: { x, y } } }) => {
+ return (
+ <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont"
+ style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}
+ >
+ <canvas className="collectionFreeFormRemoteCursors-canvas"
+ ref={(el) => {
+ if (el) {
+ const ctx = el.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = "#" + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + "22";
+ ctx.fillRect(0, 0, 20, 20);
- ctx.moveTo(10, 0);
- ctx.lineTo(10, 8);
+ ctx.fillStyle = "black";
+ ctx.lineWidth = 0.5;
- ctx.moveTo(10, 20);
- ctx.lineTo(10, 12);
+ ctx.beginPath();
- ctx.moveTo(0, 10);
- ctx.lineTo(8, 10);
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
- ctx.moveTo(20, 10);
- ctx.lineTo(12, 10);
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
- ctx.stroke();
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
- // ctx.font = "10px Arial";
- // ctx.fillText(Doc.CurrentUserEmail[0].toUpperCase(), 10, 10);
- }
- }
- }
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
- get sharedCursors() {
- return this.getCursors().map(c => {
- const m = c.data.metadata;
- const l = c.data.position;
- this.drawCrosshairs("#" + v5(m.id, v5.URL).substring(0, 6).toUpperCase() + "22");
- return (
- <div key={m.id} className="collectionFreeFormRemoteCursors-cont"
- style={{ transform: `translate(${l.x - 10}px, ${l.y - 10}px)` }}
- >
- <canvas className="collectionFreeFormRemoteCursors-canvas"
- ref={(el) => { if (el) this.crosshairs = el; }}
+ ctx.stroke();
+ }
+ }
+ }}
width={20}
height={20}
/>
<p className="collectionFreeFormRemoteCursors-symbol">
- {m.identifier[0].toUpperCase()}
+ {metadata.identifier[0].toUpperCase()}
</p>
</div>
);
@@ -83,6 +74,6 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
}
render() {
- return this.sharedCursors;
+ return this.renderedCursors;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2518a4a55..07a5a2c7b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,20 +1,22 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } from "../../../../new_fields/Doc";
+import { computedFn } from "mobx-utils";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
-import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema";
+import { InkTool } from "../../../../new_fields/InkField";
+import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { TraceMobx } from "../../../../new_fields/util";
+import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
-import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
+import { Docs } from "../../../documents/Documents";
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager } from "../../../util/DragManager";
import { HistoryUtil } from "../../../util/History";
@@ -32,15 +34,12 @@ import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
-import { computePivotLayout, ViewDefResult, computeTimelineLayout, PoolData, ViewDefBounds } from "./CollectionFreeFormLayoutEngines";
+import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { computedFn } from "mobx-utils";
-import { TraceMobx } from "../../../../new_fields/util";
-import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -807,7 +806,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
doFreeformLayout(poolData: Map<string, any>) {
const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
- let state = initResult && initResult.success ? initResult.result.scriptState : undefined;
+ const state = initResult && initResult.success ? initResult.result.scriptState : undefined;
const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
@@ -827,7 +826,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@computed get filterDocs() {
- const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []);
+ const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
+ const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
const clusters: { [key: string]: { [value: string]: string } } = {};
for (let i = 0; i < docFilters.length; i += 3) {
const [key, value, modifiers] = docFilters.slice(i, i + 3);
@@ -853,7 +853,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
return true;
}) : this.childDocs;
- return filteredDocs;
+ const rangeFilteredDocs = filteredDocs.filter(d => {
+ for (let i = 0; i < docRangeFilters.length; i += 3) {
+ const key = docRangeFilters[i];
+ const min = Number(docRangeFilters[i + 1]);
+ const max = Number(docRangeFilters[i + 2]);
+ const val = Cast(d[key], "number", null);
+ if (val !== undefined && (val < min || val > max)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ return rangeFilteredDocs;
}
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e16f4011e..4b0855635 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -346,8 +346,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const bounds = this.Bounds;
const selected = this.marqueeSelect(false);
- const newCollection = this.getCollection(selected, false);
-
selected.map(d => {
this.props.removeDocument(d);
d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
@@ -355,23 +353,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.page = -1;
return d;
});
- newCollection._chromeStatus = "disabled";
- const summary = Docs.Create.TextDocument("", { x: bounds.left, y: bounds.top, _width: 300, _height: 100, _autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
- Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
- newCollection.x = bounds.left + bounds.width;
- Doc.GetProto(newCollection).summaryDoc = summary;
- Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
- if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
- const container = Docs.Create.FreeformDocument([summary, newCollection], {
- x: bounds.left, y: bounds.top, _width: 300, _height: 200, _autoHeight: true,
- _viewType: CollectionViewType.Stacking, _chromeStatus: "disabled", title: "-summary-"
- });
- Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
- this.props.addLiveTextDocument(container);
- } else if (e instanceof KeyboardEvent ? e.key === "S" : false) { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
- Doc.GetProto(summary).maximizeLocation = "inTab"; // or "inPlace", or "onRight"
- this.props.addLiveTextDocument(summary);
- }
+ const summary = Docs.Create.TextDocument("", { x: bounds.left, y: bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ Doc.GetProto(summary)["data-annotations"] = new List<Doc>(selected);
+ this.props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index 0c74b8ddb..821c8d804 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -1,8 +1,8 @@
.collectionMulticolumnView_contents {
display: flex;
+ overflow: hidden;
width: 100%;
height: 100%;
- overflow: hidden;
.document-wrapper {
display: flex;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index 64f607680..a7e2c5707 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -1,8 +1,8 @@
.collectionMultirowView_contents {
display: flex;
+ overflow: hidden;
width: 100%;
height: 100%;
- overflow: hidden;
flex-direction: column;
.document-wrapper {
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index ff7c4998f..ec05443e5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -258,6 +258,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
return (
<div className={"collectionMultirowView_contents"}
style={{
+ width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
+ height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
}} ref={this.createDashEventsTarget}>