aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx67
-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.tsx2
4 files changed, 84 insertions, 24 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 1dc09b517..f08c2506e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,5 @@
import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc";
-import { NumCast, StrCast, Cast, DateCast } from "../../../../new_fields/Types";
+import { NumCast, StrCast, Cast, DateCast, BoolCast } from "../../../../new_fields/Types";
import { ScriptBox } from "../../ScriptBox";
import { CompileScript } from "../../../util/Scripting";
import { ScriptField } from "../../../../new_fields/ScriptField";
@@ -16,18 +16,35 @@ interface PivotData {
text: string;
x: number;
y: number;
+ zIndex?: number;
width?: number;
height?: number;
fontSize: number;
+ color?: string;
}
export interface ViewDefBounds {
x: number;
y: number;
z?: number;
+ zIndex?: number;
width?: number;
height?: number;
transition?: string;
+ highlight?: boolean;
+}
+
+export interface PoolData {
+ x?: number,
+ y?: number,
+ z?: number,
+ zIndex?: number,
+ width?: number,
+ height?: number,
+ color?: string,
+ transition?: string,
+ highlight?: boolean,
+ state?: any
}
export interface ViewDefResult {
@@ -43,7 +60,7 @@ function toLabel(target: FieldResult<Field>) {
}
export function computePivotLayout(
- poolData: ObservableMap<string, any>,
+ poolData: ObservableMap<string, PoolData>,
pivotDoc: Doc,
childDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
@@ -116,7 +133,7 @@ export function computePivotLayout(
export function computeTimelineLayout(
- poolData: ObservableMap<string, any>,
+ poolData: ObservableMap<string, PoolData>,
pivotDoc: Doc,
childDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
@@ -128,9 +145,10 @@ export function computeTimelineLayout(
const docMap = new Map<Doc, ViewDefBounds>();
const groupNames: PivotData[] = [];
const timelineFieldKey = Field.toString(pivotDoc.pivotField as Field);
- 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 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 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[]) => {
@@ -151,6 +169,13 @@ export function computeTimelineLayout(
maxTime = Math.max(num, maxTime);
}
});
+ if (curTime !== undefined) {
+ if (curTime > maxTime || curTime - minTime > maxTime - curTime) {
+ maxTime = curTime + (curTime - minTime);
+ } else {
+ minTime = curTime - (maxTime - curTime);
+ }
+ }
pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
@@ -163,16 +188,23 @@ export function computeTimelineLayout(
if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize });
}
+ 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 });
+ }
const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
let stacking: number[] = [];
+ 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 });
+ }
const keyDocs = pivotDateGroups.get(key)!;
keyDocs.forEach(d => d.isMinimized = false);
x += scaling * (key - prevKey);
const stack = findStack(x, stacking);
prevKey = key;
- !stack && groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize });
+ !stack && Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth && 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);
@@ -182,10 +214,17 @@ 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 / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2, width: wid, height: hgt });
+ docMap.set(doc, {
+ x: x, y: - Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+ zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt
+ });
stacking[stack] = x + pivotAxisWidth;
});
});
+ if (sortedKeys.length && curTime > sortedKeys[sortedKeys.length - 1]) {
+ x = (curTime - minTime) * scaling;
+ groupNames.push({ type: "text", text: curTime.toString(), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize });
+ }
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 });
}
@@ -194,8 +233,8 @@ export function computeTimelineLayout(
return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
}
-function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: any,
- poolData: any, viewDefsToJSX: any, groupNames: PivotData[], minWidth: number, extras: any[]) {
+function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<Doc, ViewDefBounds>,
+ poolData: ObservableMap<string, PoolData>, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: PivotData[], minWidth: number, extras: PivotData[]) {
const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, height: gn.height }) as PivotData);
const docEles = childPairs.filter(d => !d.layout.isMinimized).map(pair =>
@@ -212,13 +251,18 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
x: NumCast(pair.layout.x),
y: NumCast(pair.layout.y),
z: NumCast(pair.layout.z),
+ highlight: undefined,
+ zIndex: NumCast(pair.layout.zIndex),
width: NumCast(pair.layout._width),
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 || 0) * scale, height: newPosRaw.height! * scale };
+ const newPos = {
+ x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, zIndex: newPosRaw.zIndex, highlight: newPosRaw.highlight,
+ 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) {
+ if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex || newPos.width !== lastPos.width || newPos.height !== lastPos.height) {
runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
}
});
@@ -229,6 +273,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
text: gname.text,
x: gname.x * scale,
y: gname.y * scale,
+ color: gname.color,
width: gname.width === undefined ? undefined : gname.width * scale,
height: Math.max(fontHeight, gname.height! * scale),
// height: gname.height === undefined ? undefined : gname.height * scale,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 050dca596..f1a239050 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -32,7 +32,7 @@ import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
-import { computePivotLayout, ViewDefResult, computeTimelineLayout } from "./CollectionFreeFormLayoutEngines";
+import { computePivotLayout, ViewDefResult, computeTimelineLayout, PoolData } from "./CollectionFreeFormLayoutEngines";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
@@ -735,13 +735,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
};
}
- getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
+ getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
const result = this.Document.arrangeScript?.script.run(params, console.log);
if (result?.success) {
return { ...result, transition: "transform 1s" };
}
const layoutDoc = Doc.Layout(params.doc);
- return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number") };
+ return {
+ x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), color: Cast(params.doc.color, "string"),
+ zIndex: Cast(params.doc.zIndex, "number"), width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number")
+ };
}
viewDefsToJSX = (views: any[]) => {
@@ -752,6 +755,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const x = Cast(viewDef.x, "number");
const y = Cast(viewDef.y, "number");
const z = Cast(viewDef.z, "number");
+ const highlight = Cast(viewDef.highlight, "boolean");
+ const zIndex = Cast(viewDef.zIndex, "number");
+ const color = Cast(viewDef.color, "string");
const width = Cast(viewDef.width, "number", null);
const height = Cast(viewDef.height, "number", null);
if (viewDef.type === "text") {
@@ -759,17 +765,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const fontSize = Cast(viewDef.fontSize, "number");
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)` }}>
+ ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color}
+ style={{ width, height, color, fontSize, transform: `translate(${x}px, ${y}px)` }}>
{text}
</div>,
- bounds: { x: x!, y: y!, z: z, width, height: height }
+ bounds: { x: x!, y: y!, z, zIndex, width, height }
};
} else if (viewDef.type === "div") {
const backgroundColor = Cast(viewDef.color, "string");
return [x, y].some(val => val === undefined) ? undefined :
{
- 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 }
+ 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, zIndex, width, height }
};
}
}
@@ -801,7 +809,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const data = poolData.get(pair.layout[Id]);
const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
state = pos.state === undefined ? state : pos.state;
- if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
+ if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.zIndex !== data.zIndex ||
+ pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
runInAction(() => poolData.set(pair.layout[Id], pos));
}
});
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 09fcc5362..ea7ed1d54 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -15,9 +15,12 @@ import { returnFalse } from "../../../Utils";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
- dataProvider?: (doc: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined;
+ dataProvider?: (doc: Doc) => { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string } | undefined;
x?: number;
y?: number;
+ z?: number;
+ zIndex?: number;
+ highlight?: boolean;
width?: number;
height?: number;
jitterRotation: number;
@@ -30,8 +33,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@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)`; }
- get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
- get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
+ get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
+ get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
+ get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
+ get Highlight() { return this.dataProvider?.highlight; }
get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); }
get height() {
const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
@@ -78,11 +83,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
+ outline: this.Highlight ? "orange solid 2px" : "",
transform: this.transform,
transition: this.Document.isAnimating ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
- zIndex: this.Document.zIndex || 0,
+ zIndex: this.ZInd,
}} >
{!this.props.fitToBox ? <DocumentView {...this.props}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e9c0a6df3..0f517197a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -919,7 +919,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const titleView = (!showTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} style={{
position: showTextTitle ? "relative" : "absolute",
- pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
+ pointerEvents: SelectionManager.GetIsDragging() || this.onClickHandler || this.Document.ignoreClick ? "none" : "all",
}}>
<EditableView ref={this._titleRef}
contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}