aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-04-25 03:42:51 -0400
committerBob Zeleznik <zzzman@gmail.com>2020-04-25 03:42:51 -0400
commitefd038bd12de35abc80141b130be523e3fea077a (patch)
tree2be5229f4babd1a22f41518f80417d086dce60ce
parent896c0fef965458bd23ff098c7c95b09799528033 (diff)
hopefully final cleanup of pile up collection view. added doubleTap support in setupMoveUpEvents
-rw-r--r--src/Utils.ts12
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/views/collections/CollectionPileView.scss8
-rw-r--r--src/client/views/collections/CollectionPileView.tsx97
-rw-r--r--src/client/views/collections/CollectionSubView.tsx4
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx3
-rw-r--r--src/client/views/collections/CollectionView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx95
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx16
-rw-r--r--src/new_fields/Doc.ts35
11 files changed, 189 insertions, 109 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 9acdc8731..ad12c68a1 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -504,8 +504,9 @@ export function setupMoveUpEvents(
e: React.PointerEvent,
moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
upEvent: (e: PointerEvent) => void,
- clickEvent: (e: PointerEvent) => void,
- stopPropagation: boolean = true
+ clickEvent: (e: PointerEvent, doubleTap?: boolean) => void,
+ stopPropagation: boolean = true,
+ stopMovePropagation: boolean = true
) {
(target as any)._downX = (target as any)._lastX = e.clientX;
(target as any)._downY = (target as any)._lastY = e.clientY;
@@ -520,12 +521,15 @@ export function setupMoveUpEvents(
}
(target as any)._lastX = e.clientX;
(target as any)._lastY = e.clientY;
- e.stopPropagation();
+ stopMovePropagation && e.stopPropagation();
};
+ (target as any)._doubleTap = false;
const _upEvent = (e: PointerEvent): void => {
+ (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < 300);
+ (target as any)._lastTap = Date.now();
upEvent(e);
if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) {
- clickEvent(e);
+ clickEvent(e, (target as any)._doubleTap);
}
document.removeEventListener("pointermove", _moveEvent);
document.removeEventListener("pointerup", _upEvent);
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 578d337d6..1268c97b0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -612,6 +612,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id);
}
+ export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Pile }, id);
+ }
+
export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id);
}
diff --git a/src/client/views/collections/CollectionPileView.scss b/src/client/views/collections/CollectionPileView.scss
new file mode 100644
index 000000000..ac874b663
--- /dev/null
+++ b/src/client/views/collections/CollectionPileView.scss
@@ -0,0 +1,8 @@
+.collectionPileView {
+ display: flex;
+ flex-direction: row;
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ overflow: visible;
+}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
new file mode 100644
index 000000000..511efe0a7
--- /dev/null
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -0,0 +1,97 @@
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types";
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
+import { CollectionSubView } from "./CollectionSubView";
+import "./CollectionPileView.scss";
+import React = require("react");
+import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+
+@observer
+export class CollectionPileView extends CollectionSubView(doc => doc) {
+ _lastTap = 0;
+ _doubleTap: boolean | undefined = false;
+ _originalChrome: string = "";
+ @observable _contentsActive = true;
+ @observable _layoutEngine = "pass";
+ @observable _collapsed: boolean = false;
+ @observable _childClickedScript: Opt<ScriptField>;
+ componentDidMount() {
+ this._originalChrome = StrCast(this.layoutDoc._chromeStatus);
+ this.layoutDoc._chromeStatus = "disabled";
+ this.layoutDoc.hideFilterView = true;
+ }
+ componentWillUnmount() {
+ this.layoutDoc.hideFilterView = false;
+ this.layoutDoc._chromeStatus = this._originalChrome;
+ }
+
+ layoutEngine = () => this._layoutEngine;
+
+ @computed get contents() {
+ return <div className="collectionPileView-innards" style={{ width: "100%", pointerEvents: this._contentsActive && (this.props.active() || this.layoutEngine() === "starburst") ? undefined : "none" }} >
+ <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} />
+ </div>;
+ }
+
+ specificMenu = (e: React.MouseEvent) => {
+ const layoutItems: ContextMenuProps[] = [];
+ const doc = this.props.Document;
+
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
+ }
+
+ toggleStarburst = action(() => {
+ if (this._layoutEngine === 'starburst') {
+ const defaultSize = 110;
+ this.layoutDoc.overflow = undefined;
+ this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
+ this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
+ this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
+ this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
+ this._layoutEngine = 'pass';
+ } else {
+ const defaultSize = 25;
+ this.layoutDoc.overflow = 'visible';
+ !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
+ !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
+ if (this._layoutEngine === 'pass') {
+ this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
+ this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2;
+ this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym]();
+ this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym]();
+ }
+ this.layoutDoc._width = this.layoutDoc._height = defaultSize;
+ this._layoutEngine = 'starburst';
+ }
+ });
+
+ pointerDown = (e: React.PointerEvent) => {
+ // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false, false); // this sets _doubleTap
+ }
+
+ onClick = (e: React.MouseEvent) => {
+ if (e.button === 0 && (this._doubleTap || this.layoutEngine() === "starburst")) {
+ SelectionManager.DeselectAll();
+ this.toggleStarburst();
+ e.stopPropagation();
+ } else if (this.layoutEngine() === "pass") {
+ runInAction(() => this._contentsActive = false);
+ setTimeout(action(() => this._contentsActive = true), 300);
+ }
+ }
+
+ render() {
+
+ return <div className={"collectionPileView"} onContextMenu={this.specificMenu} onClick={this.onClick} onPointerDown={this.pointerDown}
+ style={{ width: this.props.PanelWidth(), height: `calc(100% - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
+ {this.contents}
+ </div>;
+ }
+}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 78ba3d6e6..c530b24fb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -400,9 +400,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (generatedDocuments.length) {
const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d));
if (set) {
- const pile = Docs.Create.FreeformDocument(generatedDocuments, { ...options, title: "pile", _LODdisable: true, });
- Doc.pileup(pile, generatedDocuments);
- addDocument(pile);
+ addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!));
} else {
generatedDocuments.forEach(addDocument);
}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 63498cea1..045134225 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -19,7 +19,6 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
import React = require("react");
-import { DocumentView } from "../nodes/DocumentView";
@observer
export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -84,7 +83,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
}
@computed get contents() {
- return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%" }} onPointerDown={this.contentsDown}>
+ return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}>
<CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={BoolCast(this.layoutDoc._freezeChildDimensions, true)} layoutEngine={this.layoutEngine} />
</div>;
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 47e79f088..f82c6d8a6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -46,6 +46,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
import { ObjectField } from '../../../new_fields/ObjectField';
import CollectionMapView from './CollectionMapView';
import { Transform } from 'prosemirror-transform';
+import { CollectionPileView } from './CollectionPileView';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -67,7 +68,8 @@ export enum CollectionViewType {
Carousel = "carousel",
Linear = "linear",
Staff = "staff",
- Map = "map"
+ Map = "map",
+ Pile = "pileup"
}
export interface CollectionRenderProps {
@@ -169,6 +171,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
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.Pile: { return (<CollectionPileView key="collview" {...props} />); }
case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d8bd18ec7..9ca747113 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -13,10 +13,11 @@ import { listSpec } from "../../../../new_fields/Schema";
export interface ViewDefBounds {
type: string;
- text?: string;
+ payload: any;
x: number;
y: number;
z?: number;
+ text?: string;
zIndex?: number;
width?: number;
height?: number;
@@ -24,14 +25,13 @@ export interface ViewDefBounds {
fontSize?: number;
highlight?: boolean;
color?: string;
- payload: any;
replica?: string;
pair?: { layout: Doc, data?: Doc };
}
export interface PoolData {
- x?: number;
- y?: number;
+ x: number;
+ y: number;
z?: number;
zIndex?: number;
width?: number;
@@ -40,7 +40,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
replica?: string;
- pair: { layout: Doc, data?: Doc }
+ pair: { layout: Doc, data?: Doc };
}
export interface ViewDefResult {
@@ -84,96 +84,88 @@ interface PivotColumn {
export function computerPassLayout(
poolData: Map<string, PoolData>,
pivotDoc: Doc,
- filterDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
) {
- const docMap = new Map<string, ViewDefBounds>();
+ const docMap = new Map<string, PoolData>();
childPairs.forEach(({ layout, data }, i) => {
docMap.set(layout[Id], {
- type: "doc",
x: NumCast(layout.x),
y: NumCast(layout.y),
width: layout[WidthSym](),
height: layout[HeightSym](),
- payload: undefined,
pair: { layout, data },
replica: ""
});
});
- return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+ return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
export function computerStarburstLayout(
poolData: Map<string, PoolData>,
pivotDoc: Doc,
- filterDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
) {
- const docMap = new Map<string, ViewDefBounds>();
+ const docMap = new Map<string, PoolData>();
const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
const docScale = NumCast(pivotDoc._starburstDocScale);
const docSize = docScale * 100; // assume a icon sized at 100
const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
- childPairs.forEach((pair, i) => {
+ childPairs.forEach(({ layout, data }, i) => {
const deg = i / childPairs.length * Math.PI * 2;
- docMap.set(pair.layout[Id], {
- type: "doc",
- x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * pair.layout[WidthSym]() / 2,
- y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * pair.layout[HeightSym]() / 2,
- width: docScale * pair.layout[WidthSym](),
- height: docScale * pair.layout[HeightSym](),
- payload: undefined,
+ docMap.set(layout[Id], {
+ x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * layout[WidthSym]() / 2,
+ y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * layout[HeightSym]() / 2,
+ width: docScale * layout[WidthSym](),
+ height: docScale * layout[HeightSym](),
+ pair: { layout, data },
replica: ""
});
});
- return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+ return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
export function computePivotLayout(
poolData: Map<string, PoolData>,
pivotDoc: Doc,
- filterDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
) {
- const docMap = new Map<string, ViewDefBounds>();
+ const docMap = new Map<string, PoolData>();
const fieldKey = "data";
const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
const pivotFieldKey = toLabel(pivotDoc._pivotField);
- for (const doc of filterDocs) {
- const lval = Cast(doc[pivotFieldKey], listSpec("string"), null);
- const val = Field.toString(doc[pivotFieldKey] as Field);
+ childPairs.map(pair => {
+ const lval = Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+ const val = Field.toString(pair.layout[pivotFieldKey] as Field);
if (lval) {
lval.forEach((val, i) => {
!pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
- pivotColumnGroups.get(val)!.docs.push(doc);
+ pivotColumnGroups.get(val)!.docs.push(pair.layout);
pivotColumnGroups.get(val)!.replicas.push(i.toString());
});
} else if (val) {
!pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
- pivotColumnGroups.get(val)!.docs.push(doc);
+ pivotColumnGroups.get(val)!.docs.push(pair.layout);
pivotColumnGroups.get(val)!.replicas.push("");
} else {
- docMap.set(doc[Id], {
- type: "doc",
+ docMap.set(pair.layout[Id], {
x: 0,
y: 0,
zIndex: -99,
width: 0,
height: 0,
- payload: undefined,
- pair: { layout: doc },
+ pair,
replica: ""
- })
+ });
}
- }
+ });
let nonNumbers = 0;
childPairs.map(pair => {
const num = toNumber(pair.layout[pivotFieldKey]);
@@ -252,12 +244,10 @@ export function computePivotLayout(
wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
}
docMap.set(doc[Id] + (val.replicas || ""), {
- type: "doc",
x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
y: -y + (pivotAxisWidth - hgt) / 2,
width: wid,
height: hgt,
- payload: undefined,
pair: { layout: doc },
replica: val.replicas[i]
});
@@ -273,7 +263,7 @@ export function computePivotLayout(
const dividers = sortedPivotKeys.map((key, i) =>
({ 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, [], childPairs.filter(c => !filterDocs.includes(c.layout)));
+ return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
}
function toNumber(val: FieldResult<Field>) {
@@ -283,14 +273,13 @@ function toNumber(val: FieldResult<Field>) {
export function computeTimelineLayout(
poolData: Map<string, PoolData>,
pivotDoc: Doc,
- filterDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
) {
const fieldKey = "data";
const pivotDateGroups = new Map<number, Doc[]>();
- const docMap = new Map<string, ViewDefBounds>();
+ const docMap = new Map<string, PoolData>();
const groupNames: ViewDefBounds[] = [];
const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
@@ -306,11 +295,11 @@ export function computeTimelineLayout(
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])));
+ childPairs.forEach(pair => {
+ const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey])));
if (!Number.isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
- pivotDateGroups.get(num)!.push(doc);
+ pivotDateGroups.get(num)!.push(pair.layout);
minTime = Math.min(num, minTime);
maxTime = Math.max(num, maxTime);
}
@@ -369,7 +358,7 @@ export function computeTimelineLayout(
}
const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
- return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childPairs.filter(c => !filterDocs.includes(c.layout)));
+ return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
keyDocs.forEach(doc => {
@@ -382,13 +371,11 @@ export function computeTimelineLayout(
wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
}
docMap.set(doc[Id], {
- type: "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 / (Math.max(stack, 1)),
- payload: undefined,
pair: { layout: doc },
replica: ""
});
@@ -397,12 +384,19 @@ export function computeTimelineLayout(
}
}
-function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<string, ViewDefBounds>,
- poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[],
- extraDocs: { data?: Doc, layout: Doc }[]): ViewDefResult[] {
+function normalizeResults(
+ panelDim: number[],
+ fontHeight: number,
+ docMap: Map<string, PoolData>,
+ poolData: Map<string, PoolData>,
+ viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
+ groupNames: ViewDefBounds[],
+ minWidth: number,
+ extras: ViewDefBounds[]
+): ViewDefResult[] {
const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
- const aggBounds = aggregateBounds(docEles.concat(grpEles).filter(e => e.zIndex !== -99), 0, 0);
+ const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0);
aggBounds.r = Math.max(minWidth, 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;
@@ -420,12 +414,11 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
zIndex: newPosRaw.zIndex,
width: (newPosRaw.width || 0) * scale,
height: newPosRaw.height! * scale,
- pair: ele[1].pair!
+ pair: ele[1].pair
};
poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "transform 1s", ...newPos });
}
});
- extraDocs.map(ed => poolData.set(ed.layout[Id], { x: 0, y: 0, zIndex: -99, pair: ed }));
return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
type: gname.type,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5555d9e84..af9d3c5be 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -902,7 +902,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getCalculatedPositions(params: { pair: { layout: Doc, data?: 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, pair: params.pair, transition: "transform 1s" };
+ return { x: 0, y: 0, transition: "transform 1s", ...result, pair: params.pair };
}
const layoutDoc = Doc.Layout(params.pair.layout);
const { x, y, z, color, zIndex } = params.pair.layout;
@@ -954,12 +954,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
engine: (
poolData: Map<string, PoolData>,
pivotDoc: Doc,
- filterDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
- viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]) {
- return engine(poolData, this.props.Document, this.childDocs,
- this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+ viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]
+ ) {
+ return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
doFreeformLayout(poolData: Map<string, PoolData>) {
@@ -977,7 +976,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@computed get doInternalLayoutComputation() {
const newPool = new Map<string, PoolData>();
- const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.();
+ const engine = this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
switch (engine) {
case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
@@ -1001,6 +1000,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements: ViewDefResult[] = computedElementData.slice();
+ const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
elements.push({
ele: <CollectionFreeFormDocumentView
@@ -1009,8 +1009,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
replica={entry[1].replica}
dataProvider={this.childDataProvider}
LayoutDoc={this.childLayoutDocFunc}
- pointerEvents={this.props.viewDefDivClick ? false : undefined}
- jitterRotation={NumCast(this.props.Document.jitterRotation)}
+ pointerEvents={this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true)) ?
+ false : undefined}
+ jitterRotation={NumCast(this.props.Document._jitterRotation)}
fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)}
FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
/>,
@@ -1082,7 +1083,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" });
optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
// layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
- optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" });
+ optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document._jitterRotation = (this.props.Document._jitterRotation ? 0 : 10)), icon: "paint-brush" });
optionItems.push({
description: "Import document", icon: "upload", event: ({ x, y }) => {
const input = document.createElement("input");
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8174ba1..cd8166309 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,12 +1,12 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCast, DataSym, WidthSym, HeightSym, Opt } from "../../../../new_fields/Doc";
import { InkField, InkData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
import { Cast, NumCast, FieldValue, StrCast } from "../../../../new_fields/Types";
import { Utils } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
+import { Docs, DocUtils, DocumentOptions } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
@@ -310,11 +310,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
}
- getCollection = (selected: Doc[], asTemplate: boolean, isBackground?: boolean) => {
+ getCollection = (selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
const bounds = this.Bounds;
// const inkData = this.ink ? this.ink.inkData : undefined;
- const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
- const newCollection = creator(selected, {
+ const newCollection = (creator || Docs.Create.FreeformDocument)(selected, {
x: bounds.left,
y: bounds.top,
_panX: 0,
@@ -338,8 +337,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
selected.forEach(d => this.props.removeDocument(d));
- const newCollection = this.getCollection(selected, false);
- Doc.pileup(newCollection, selected);
+ const newCollection = Doc.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
this.props.addDocument(newCollection);
this.props.selectDocuments([newCollection], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -359,7 +357,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return d;
});
}
- const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t");
+ const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined);
this.props.addDocument(newCollection);
this.props.selectDocuments([newCollection], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -470,7 +468,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
@action
background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
- const newCollection = this.getCollection([], false, true);
+ const newCollection = this.getCollection([], undefined, true);
this.props.addDocument(newCollection);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 18ffc38f5..337274774 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -976,7 +976,8 @@ export namespace Doc {
if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
}
- export function pileup(newCollection: Doc, selected: Doc[]) {
+ export function pileup(selected: Doc[], x: number, y: number) {
+ const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true });
let w = 0, h = 0;
selected.forEach((d, i) => {
Doc.iconify(d);
@@ -993,12 +994,10 @@ export namespace Doc {
newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
newCollection._width = newCollection._height = 110;
//newCollection.borderRounding = "40px";
- newCollection.jitterRotation = 10;
+ newCollection._jitterRotation = 10;
newCollection._backgroundColor = "gray";
-
- newCollection._layoutEngine = "pass";
newCollection.overflow = "visible";
- newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self, this)", { self: Doc.name, this: Doc.name });
+ return newCollection;
}
@@ -1055,28 +1054,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
return docs.length ? new List(docs) : prevValue;
});
Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
-Scripting.addGlobal(function toggleStarburst(self: Doc, tLayout: Doc) {
- if (self._layoutEngine === 'starburst') {
- const defaultSize = 110;
- tLayout.overflow = undefined;
- self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - NumCast(tLayout._starburstPileWidth, defaultSize) / 2;
- self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - NumCast(tLayout._starburstPileHeight, defaultSize) / 2;
- tLayout._width = NumCast(tLayout._starburstPileWidth, defaultSize);
- tLayout._height = NumCast(tLayout._starburstPileHeight, defaultSize);
- self._layoutEngine = 'pass';
- } else {
- const defaultSize = 25;
- tLayout.overflow = 'visible';
- !tLayout._starburstRadius && (tLayout._starburstRadius = 500);
- !tLayout._starburstDocScale && (tLayout._starburstDocScale = 2.5);
- if (self._layoutEngine === 'pass') {
- self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - defaultSize / 2;
- self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - defaultSize / 2;
- tLayout._starburstPileWidth = tLayout[WidthSym]();
- tLayout._starburstPileHeight = tLayout[HeightSym]();
- }
- tLayout._width = tLayout._height = defaultSize;
- self._layoutEngine = 'starburst';
- }
-}); \ No newline at end of file
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file