aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts19
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/DocumentManager.ts29
-rw-r--r--src/client/views/collections/CollectionTimeView.scss25
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx95
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx120
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx25
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx14
-rw-r--r--src/client/views/nodes/DocumentView.tsx8
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx2
-rw-r--r--src/new_fields/documentSchemas.ts1
-rw-r--r--src/new_fields/util.ts2
12 files changed, 192 insertions, 150 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 13bdb990d..29fce3409 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -328,16 +328,15 @@ export function timenow() {
return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
}
-export function aggregateBounds(boundsList: { x: number, y: number, width: number, height?: number }[], xpad: number, ypad: number) {
- const bounds = boundsList.reduce((bounds, b) => {
- const [sptX, sptY] = [b.x, b.y];
- const [bptX, bptY] = [sptX + b.width, sptY + (b.height || 0)];
- return {
- x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
- r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
- };
- }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
- return { x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y, r: bounds.r !== -Number.MAX_VALUE ? bounds.r + xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + ypad : bounds.b };
+export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) {
+ const bounds = boundsList.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) })).reduce((bounds, b) => ({
+ x: Math.min(b.x, bounds.x), y: Math.min(b.y, bounds.y),
+ r: Math.max(b.r, bounds.r), b: Math.max(b.b, bounds.b)
+ }), { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+ return {
+ x: bounds.x !== Number.MAX_VALUE ? bounds.x - xpad : bounds.x, y: bounds.y !== Number.MAX_VALUE ? bounds.y - ypad : bounds.y,
+ r: bounds.r !== -Number.MAX_VALUE ? bounds.r + xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + ypad : bounds.b
+ };
}
export function intersectRect(r1: { left: number, top: number, width: number, height: number },
r2: { left: number, top: number, width: number, height: number }) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ab22906bb..5eb4dc5fb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -249,7 +249,7 @@ export namespace Docs {
]);
// All document prototypes are initialized with at least these values
- const defaultOptions: DocumentOptions = { _width: 300 }; // bcz: do we really want to set anything here? could also try to set in render() methods for types that need a default
+ const defaultOptions: DocumentOptions = { x: 0, y: 0, _width: 300 }; // bcz: do we really want to set anything here? could also try to set in render() methods for types that need a default
const suffix = "Proto";
/**
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index f024b9116..9fff8faa7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -221,34 +221,5 @@ export class DocumentManager {
return 1;
}
}
-
- @action
- animateBetweenPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
- expandedDocs && expandedDocs.map(expDoc => {
- if (expDoc.isMinimized || expDoc.isAnimating === "min") { // MAXIMIZE DOC
- if (expDoc.isMinimized) { // docs are never actaully at the minimized location. so when we unminimize one, we have to set our overrides to make it look like it was at the minimize location
- expDoc.isMinimized = false;
- expDoc.animateToPos = new List<number>([...scrpt, 0]);
- expDoc.animateToDimensions = new List<number>([0, 0]);
- }
- setTimeout(() => {
- expDoc.isAnimating = "max";
- expDoc.animateToPos = new List<number>([0, 0, 1]);
- expDoc.animateToDimensions = new List<number>([NumCast(expDoc.width), NumCast(expDoc.height)]);
- setTimeout(() => expDoc.isAnimating === "max" && (expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined), 600);
- }, 0);
- } else { // MINIMIZE DOC
- expDoc.isAnimating = "min";
- expDoc.animateToPos = new List<number>([...scrpt, 0]);
- expDoc.animateToDimensions = new List<number>([0, 0]);
- setTimeout(() => {
- if (expDoc.isAnimating === "min") {
- expDoc.isMinimized = true;
- expDoc.isAnimating = expDoc.animateToPos = expDoc.animateToDimensions = undefined;
- }
- }, 600);
- }
- });
- }
}
Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index 4e06fdb84..805d06d3d 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -4,6 +4,31 @@
position: absolute;
height: 100%;
width: 100%;
+ overflow: hidden;
+ .collectionFreeform-customText {
+ text-align: left;
+ }
+ .collectionTimeView-thumb {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ transform: rotate(45deg);
+ display: inline-block;
+ background: gray;
+ bottom: 0;
+ margin-bottom: -17px;
+ border-radius: 9px;
+ opacity: 0.25;
+ }
+ .collectionTimeView-thumb-min {
+ margin-left:25%;
+ }
+ .collectionTimeView-thumb-max {
+ margin-left:75%;
+ }
+ .collectionTimeView-thumb-mid {
+ margin-left:50%;
+ }
.collectionTimeView-flyout {
width: 400px;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 19a48d6ef..b4ab88f9a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { Docs } from "../../documents/Documents";
import { EditableView } from "../EditableView";
import { anchorPoints, Flyout } from "../TemplateMenu";
@@ -107,7 +107,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
const keySet: Set<string> = new Set();
this.childLayoutPairs.map(pair =>
- Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof (pair.layout[fieldKey]) === "string").map(fieldKey =>
+ Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof (pair.layout[fieldKey]) === "number" || typeof (pair.layout[fieldKey]) === "string").map(fieldKey =>
keySet.add(fieldKey)));
keySet.toArray().map(fieldKey =>
docItems.push({ description: ":" + fieldKey, event: () => this.props.Document.pivotField = fieldKey, icon: "compress-arrows-alt" }));
@@ -120,6 +120,80 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
@observable private collapsed: boolean = false;
private toggleVisibility = action(() => this.collapsed = !this.collapsed);
+ _downX = 0;
+ onMinDown = (e: React.PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMinMove);
+ document.removeEventListener("pointerup", this.onMinUp);
+ document.addEventListener("pointermove", this.onMinMove);
+ document.addEventListener("pointerup", this.onMinUp);
+ this._downX = e.clientX;
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onMinMove = (e: PointerEvent) => {
+ const delta = e.clientX - this._downX;
+ this._downX = e.clientX;
+ 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();
+ }
+ onMinUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMinMove);
+ document.removeEventListener("pointermove", this.onMinUp);
+ }
+
+ onMaxDown = (e: React.PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMaxMove);
+ document.removeEventListener("pointermove", this.onMaxUp);
+ document.addEventListener("pointermove", this.onMaxMove);
+ document.addEventListener("pointerup", this.onMaxUp);
+ this._downX = e.clientX;
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onMaxMove = (e: PointerEvent) => {
+ const delta = e.clientX - this._downX;
+ this._downX = e.clientX;
+ 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();
+ }
+ onMaxUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMaxMove);
+ document.removeEventListener("pointermove", this.onMaxUp);
+ }
+
+ onMidDown = (e: React.PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMidMove);
+ document.removeEventListener("pointermove", this.onMidUp);
+ document.addEventListener("pointermove", this.onMidMove);
+ document.addEventListener("pointerup", this.onMidUp);
+ this._downX = e.clientX;
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onMidMove = (e: PointerEvent) => {
+ const delta = e.clientX - this._downX;
+ this._downX = e.clientX;
+ 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 + "-timelineMaxReq"] = maxReq - (maxReq - minReq) * delta / this.props.PanelWidth();
+ }
+ onMidUp = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.onMidMove);
+ document.removeEventListener("pointermove", this.onMidUp);
+ }
+
+ @computed get contents() {
+ return <div className="collectionTimeView-pivot" key="pivot" style={{ width: this.bodyPanelWidth() }}>
+ <CollectionFreeFormView {...this.props} ScreenToLocalTransform={this.getTransform} PanelWidth={this.bodyPanelWidth} />
+ </div>;
+ }
+
render() {
const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null);
const flyout = (
@@ -150,9 +224,11 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
<div className={"pivotKeyEntry"}>
<EditableView {...newEditableViewProps} menuCallback={this.menuCallback} />
</div>
- <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>
+ {!this.props.isSelected() || this.props.PanelHeight() < 100 ? (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-treeView" style={{ width: `${this._facetWidth}px`, overflow: this._facetWidth < 15 ? "hidden" : undefined }}>
<div className="collectionTimeView-addFacet" style={{ width: `${this._facetWidth}px` }} onPointerDown={e => e.stopPropagation()}>
<Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
@@ -166,9 +242,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
<CollectionTreeView {...this.props} Document={facetCollection} />
</div>
</div>
- <div className="collectionTimeView-pivot" key="pivot" style={{ width: this.bodyPanelWidth() }}>
- <CollectionFreeFormView {...this.props} ScreenToLocalTransform={this.getTransform} PanelWidth={this.bodyPanelWidth} />
- </div>
+ {this.contents}
+ {!this.props.isSelected() ? (null) : <>
+ <div className="collectionTimeView-thumb-min collectionTimeView-thumb" key="min" onPointerDown={this.onMinDown} />
+ <div className="collectionTimeView-thumb-max collectionTimeView-thumb" key="mid" onPointerDown={this.onMaxDown} />
+ <div className="collectionTimeView-thumb-mid collectionTimeView-thumb" key="max" onPointerDown={this.onMidDown} />
+ </>}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 6e4af3520..d2a2b42a0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,4 +1,4 @@
-import { Doc, Field, FieldResult } from "../../../../new_fields/Doc";
+import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc";
import { NumCast, StrCast, Cast, DateCast } from "../../../../new_fields/Types";
import { ScriptBox } from "../../ScriptBox";
import { CompileScript } from "../../../util/Scripting";
@@ -16,7 +16,7 @@ interface PivotData {
text: string;
x: number;
y: number;
- width: number;
+ width?: number;
height?: number;
fontSize: number;
}
@@ -25,7 +25,7 @@ export interface ViewDefBounds {
x: number;
y: number;
z?: number;
- width: number;
+ width?: number;
height?: number;
transition?: string;
}
@@ -123,7 +123,7 @@ export function computePivotLayout(
height: NumCast(pair.layout._height)
};
const newPosRaw = docMap.get(pair.layout) || fallbackPos; // new pos is computed pos, or pos written to the document's fields
- const newPos = { x: newPosRaw.x * scale + centerX, y: (newPosRaw.y - aggBounds.y) * scale + centerY, z: newPosRaw.z, width: newPosRaw.width * scale, height: newPosRaw.height! * scale };
+ const newPos = { x: newPosRaw.x * scale + centerX, y: (newPosRaw.y - aggBounds.y) * scale + centerY, z: newPosRaw.z, width: (newPosRaw.width || 0) * scale, height: newPosRaw.height! * scale };
const lastPos = poolData.get(pair.layout[Id]); // last computed pos
if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.width !== lastPos.width || newPos.height !== lastPos.height) {
runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
@@ -131,7 +131,7 @@ export function computePivotLayout(
});
return {
elements: viewDefsToJSX([{ type: "text", text: "", x: 0, y: -aggBounds.y * scale - minLabelHeight, width: panelDim[0], height: panelDim[1], fontSize: 1 }].concat(groupNames.map(gname => {
- return { type: gname.type, text: gname.text, x: gname.x * scale + centerX, y: (gname.y - aggBounds.y) * scale + centerY, width: gname.width * scale, height: Math.max(minLabelHeight, centerY), fontSize: gname.fontSize };
+ return { type: gname.type, text: gname.text, x: gname.x * scale + centerX, y: (gname.y - aggBounds.y) * scale + centerY, width: (gname.width || 0) * scale, height: Math.max(minLabelHeight, centerY), fontSize: gname.fontSize };
})))
};
}
@@ -146,63 +146,57 @@ export function computeTimelineLayout(
viewDefsToJSX: (views: any) => ViewDefResult[]
) {
const fieldKey = "data";
- const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200);
const pivotDateGroups = new Map<number, Doc[]>();
-
+ const docMap = new Map<Doc, ViewDefBounds>();
+ const groupNames: PivotData[] = [];
const timelineFieldKey = Field.toString(pivotDoc.pivotField as Field);
- let minTime = Number.MAX_VALUE, maxTime = Number.MIN_VALUE;
- for (const doc of childDocs) {
+ const minTimeReq = Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null);
+ const maxTimeReq = Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null);
+ const curTime = Cast(pivotDoc[fieldKey + "-timelineCur"], "number", null);
+ 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[]) => {
+ const index = stack.findIndex(val => val === undefined || val < x);
+ return index === -1 ? stack.length : index;
+ }
+
+ let minTime = Number.MAX_VALUE;
+ let maxTime = Number.MIN_VALUE;
+ childDocs.map(doc => {
const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey])));
- if (Number.isNaN(num)) continue;
- if (num) {
+ if (Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq)) {
+ doc.isMinimized = true;
+ } else {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
pivotDateGroups.get(num)!.push(doc);
+ minTime = Math.min(num, minTime);
+ maxTime = Math.max(num, maxTime);
}
- minTime = Math.min(num, minTime);
- maxTime = Math.max(num, maxTime);
- }
- minTime = NumCast(pivotDoc[fieldKey + "-timelineMin"], minTime);
- maxTime = NumCast(pivotDoc[fieldKey + "-timelineMax"], maxTime);
- const curTime = Cast(pivotDoc[fieldKey + "-timelineCur"], "number", null);
-
- const docMap = new Map<Doc, ViewDefBounds>();
- const groupNames: PivotData[] = [];
+ });
+ pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
+ pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
+ const arrayofKeys = Array.from(pivotDateGroups.keys());
+ const sortedKeys = arrayofKeys.sort((n1, n2) => n1 - n2);
const scaling = panelDim[0] / (maxTime - minTime);
- const expander = 1.05;
let x = 0;
- let prevKey = minTime;
- const sortedKeys = Array.from(pivotDateGroups.keys()).sort();
+ 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 });
+ }
+
+ const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, panelDim[1] / 2.5);
let stacking: number[] = [];
- for (let i = 0; i < sortedKeys.length; i++) {
- const key = sortedKeys[i];
- const val = pivotDateGroups.get(key)!;
- val.forEach(d => d.isMinimized = key < minTime || key > maxTime);
- if (key < minTime || key > maxTime) {
- continue;
- }
- x += Math.max(25, scaling * (key - prevKey));
- let stack = 0;
- for (; stack < stacking.length; stack++) {
- if (stacking[stack] === undefined || stacking[stack] < x)
- break;
- }
+ sortedKeys.forEach(key => {
+ const keyDocs = pivotDateGroups.get(key)!;
+ keyDocs.forEach(d => d.isMinimized = false);
+ x += scaling * (key - prevKey);
+ const stack = findStack(x, stacking);
prevKey = key;
- groupNames.push({
- type: "text",
- text: toLabel(key),
- x: x,
- y: stack * 25,
- width: pivotAxisWidth * expander,
- height: 35,
- fontSize: NumCast(pivotDoc.pivotFontSize, 20)
- });
- val.forEach((doc, i) => {
- let stack = 0;
- for (; stack < stacking.length; stack++) {
- if (stacking[stack] === undefined || stacking[stack] < x)
- break;
- }
+ !stack && groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize });
+ keyDocs.forEach(doc => {
+ const stack = findStack(x, stacking);
const layoutDoc = Doc.Layout(doc);
let wid = pivotAxisWidth;
let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth;
@@ -210,25 +204,22 @@ export function computeTimelineLayout(
hgt = pivotAxisWidth;
wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
}
- docMap.set(doc, {
- x: x,
- y: - Math.sqrt(stack) * pivotAxisWidth - pivotAxisWidth,
- width: wid,
- height: hgt
- });
+ docMap.set(doc, { x: x, y: - Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth, width: wid, height: hgt });
stacking[stack] = x + pivotAxisWidth;
});
+ });
+ if (Math.ceil(maxTime - minTime) * scaling > x + 25) {
+ groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize });
}
- const grpEles = groupNames.map(gn => { return { x: gn.x, y: gn.y, width: gn.width, height: gn.height } as PivotData; });
+ const grpEles = groupNames.map(gn => { return { x: gn.x, y: gn.y, height: gn.height } as PivotData; });
const docEles = childPairs.filter(d => !d.layout.isMinimized).map(pair =>
- docMap.get(pair.layout) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), width: NumCast(pair.layout._width), height: NumCast(pair.layout._height) } // new pos is computed pos, or pos written to the document's fields
+ docMap.get(pair.layout) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), width: pair.layout[WidthSym](), height: pair.layout[HeightSym]() } as PivotData // new pos is computed pos, or pos written to the document's fields
);
const aggBounds = aggregateBounds(docEles.concat(grpEles), 0, 0);
+ aggBounds.r = Math.max((maxTime - minTime) * scaling, aggBounds.r - aggBounds.x);
const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
- const centerY = (panelDim[1] - (aggBounds.b - aggBounds.y) * scale) / 2;
- const centerX = (panelDim[0] - (aggBounds.r - aggBounds.x) * scale) / 2;
if (Number.isNaN(scale)) scale = 1;
childPairs.map(pair => {
@@ -240,7 +231,7 @@ export function computeTimelineLayout(
height: NumCast(pair.layout._height)
};
const newPosRaw = docMap.get(pair.layout) || fallbackPos; // new pos is computed pos, or pos written to the document's fields
- const newPos = { x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, width: newPosRaw.width * scale, height: newPosRaw.height! * scale };
+ const newPos = { x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, width: (newPosRaw.width || 0) * scale, height: newPosRaw.height! * scale };
const lastPos = poolData.get(pair.layout[Id]); // last computed pos
if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.width !== lastPos.width || newPos.height !== lastPos.height) {
runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
@@ -248,10 +239,9 @@ export function computeTimelineLayout(
});
return {
elements: viewDefsToJSX([
- { type: "text", text: "", x: -centerX, y: aggBounds.y * scale - centerY, width: panelDim[0], height: panelDim[1], fontSize: 1 },
- { type: "div", color: "black", x: -centerX, y: 0, width: panelDim[0], height: 1 }
+ { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any
].concat(groupNames.map(gname => {
- return { type: gname.type, text: gname.text, x: gname.x * scale, y: gname.y * scale, width: gname.width * scale, height: gname.height! * scale, fontSize: gname.fontSize };
+ return { type: gname.type, text: gname.text, x: gname.x * scale, y: gname.y * scale, width: gname.width === undefined ? gname.width : gname.width * scale, height: Math.max(fontHeight, gname.height! * scale), fontSize: gname.fontSize };
})))
};
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 990a2f3ba..050dca596 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -749,33 +749,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
private viewDefToJSX(viewDef: any): Opt<ViewDefResult> {
+ const x = Cast(viewDef.x, "number");
+ const y = Cast(viewDef.y, "number");
+ const z = Cast(viewDef.z, "number");
+ const width = Cast(viewDef.width, "number", null);
+ const height = Cast(viewDef.height, "number", null);
if (viewDef.type === "text") {
const text = Cast(viewDef.text, "string"); // don't use NumCast, StrCast, etc since we want to test for undefined below
- const x = Cast(viewDef.x, "number");
- const y = Cast(viewDef.y, "number");
- const z = Cast(viewDef.z, "number");
- const width = Cast(viewDef.width, "number");
- const height = Cast(viewDef.height, "number");
const fontSize = Cast(viewDef.fontSize, "number");
- return [text, x, y, width].some(val => val === undefined) ? undefined :
+ return [text, x, y].some(val => val === undefined) ? undefined :
{
ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z} style={{ width, height, fontSize, transform: `translate(${x}px, ${y}px)` }}>
{text}
</div>,
- bounds: { x: x!, y: y!, z: z, width: width!, height: height }
+ bounds: { x: x!, y: y!, z: z, width, height: height }
};
} else if (viewDef.type === "div") {
- const x = Cast(viewDef.x, "number");
- const y = Cast(viewDef.y, "number");
- const z = Cast(viewDef.z, "number");
const backgroundColor = Cast(viewDef.color, "string");
- const width = Cast(viewDef.width, "number");
- const height = Cast(viewDef.height, "number");
- const fontSize = Cast(viewDef.fontSize, "number");
- return [x, y, width].some(val => val === undefined) ? undefined :
+ return [x, y].some(val => val === undefined) ? undefined :
{
- ele: <div className="collectionFreeform-customDiv" key={"div" + x + y + z} style={{ width, height, fontSize, backgroundColor, transform: `translate(${x}px, ${y}px)` }}>
- </div>,
+ ele: <div className="collectionFreeform-customDiv" key={"div" + x + y + z} style={{ width, height, backgroundColor, transform: `translate(${x}px, ${y}px)` }} />,
bounds: { x: x!, y: y!, z: z, width: width!, height: height }
};
}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 2183129cf..09fcc5362 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -27,8 +27,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
- _disposer: IReactionDisposer | undefined = undefined;
-
@observable _animPos: number[] | undefined = undefined;
get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${anime.random(-1, 1) * this.props.jitterRotation}deg)`; }
@@ -58,14 +56,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return undefined;
}
- componentWillUnmount() { this._disposer?.(); }
- componentDidMount() {
- this._disposer = reaction(() => Array.from(Cast(this.props.Document?.animateToPos, listSpec("number"), null) || []),
- target => this._animPos = !target || !target?.length ? undefined : target[2] ? [NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y)] :
- this.props.ScreenToLocalTransform().transformPoint(target[0], target[1]),
- { fireImmediately: true });
- }
-
contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect && !this.props.fitToBox ? this.width / this.nativeWidth : 1;
clusterColorFunc = (doc: Doc) => this.clusterColor;
panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth());
@@ -76,7 +66,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed
get clusterColor() { return this.props.backgroundColor(this.props.Document); }
-
+ focusDoc = (doc: Doc) => this.props.focus(doc, false);
render() {
TraceMobx();
return <div className="collectionFreeFormDocumentView-container"
@@ -107,7 +97,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
DataDocument={this.props.DataDoc}
getTransform={this.getTransform}
active={returnFalse}
- focus={(doc: Doc) => this.props.focus(doc, false)}
+ focus={this.focusDoc}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
/>}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 82382c536..e9c0a6df3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -969,10 +969,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const borderRounding = this.getLayoutPropStr("borderRounding");
const localScale = fullDegree;
- const animDims = this.Document.animateToDimensions ? Array.from(this.Document.animateToDimensions) : undefined;
- const animheight = animDims ? animDims[1] : "100%";
- const animwidth = animDims ? animDims[0] : "100%";
-
const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear;
@@ -988,8 +984,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor,
- width: animwidth,
- height: animheight,
+ width: "100%",
+ height: "100%",
opacity: this.Document.opacity
}}>
{this.innards}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fe9b8e0ff..969df7c5d 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1044,7 +1044,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
@action
tryUpdateHeight(limitHeight?: number) {
let scrollHeight = this._ref.current?.scrollHeight;
- if (!this.layoutDoc.animateToPos && this.layoutDoc._autoHeight && scrollHeight &&
+ if (this.layoutDoc._autoHeight && scrollHeight &&
getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts
index 4a5c1fdb0..0b3bd6338 100644
--- a/src/new_fields/documentSchemas.ts
+++ b/src/new_fields/documentSchemas.ts
@@ -50,7 +50,6 @@ export const documentSchema = createSchema({
isButton: "boolean", // whether document functions as a button (overiding native interactions of its content)
ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events)
isAnimating: "string", // whether the document is in the midst of animating between two layouts (used by icons to de/iconify documents). value is undefined|"min"|"max"
- animateToDimensions: listSpec("number"), // layout information about the target rectangle a document is animating towards
scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc.
strokeWidth: "number",
fontSize: "string",
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 2cedda7a6..19ca8a7ee 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -14,7 +14,7 @@ function _readOnlySetter(): never {
}
export function TraceMobx() {
- //trace();
+ // trace();
}
export interface GetterResult {