aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandrewdkim <adkim414@gmail.com>2019-09-17 17:01:39 -0400
committerandrewdkim <adkim414@gmail.com>2019-09-17 17:01:39 -0400
commitcd052ac6bd318a0569c66ca1fba684b7dbdaaca9 (patch)
tree29b3086762f70965f6be2c586180bf3c4c252688
parent02119d9fa648ed530d956889a2244875978ad093 (diff)
parent1310633790e3db50a31a1cc6d357306d7884a053 (diff)
merge
-rw-r--r--src/Utils.ts5
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/DragManager.ts16
-rw-r--r--src/client/util/History.ts4
-rw-r--r--src/client/util/UndoManager.ts2
-rw-r--r--src/client/views/ContextMenu.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx29
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/MainOverlayTextBox.tsx2
-rw-r--r--src/client/views/TemplateMenu.tsx2
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx13
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx19
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx3
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx322
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx49
-rw-r--r--src/client/views/document_templates/image_card/ImageCard.tsx3
-rw-r--r--src/client/views/linking/LinkFollowBox.tsx2
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.scss5
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx109
-rw-r--r--src/client/views/nodes/DocumentView.scss42
-rw-r--r--src/client/views/nodes/DocumentView.tsx421
-rw-r--r--src/client/views/nodes/DragBox.tsx2
-rw-r--r--src/client/views/nodes/IconBox.tsx53
-rw-r--r--src/client/views/nodes/ImageBox.tsx5
-rw-r--r--src/client/views/pdf/Page.tsx2
-rw-r--r--src/client/views/search/SearchItem.tsx2
-rw-r--r--src/new_fields/Doc.ts13
33 files changed, 548 insertions, 606 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 3921a49c3..415023ac4 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -37,6 +37,7 @@ export class Utils {
public static prepend(extension: string): string {
return window.location.origin + extension;
}
+
public static CorsProxy(url: string): string {
return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
}
@@ -239,6 +240,10 @@ export function timenow() {
return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
}
+export function percent2frac(percent: string) {
+ return Number(percent.substr(0, percent.length - 1)) / 100;
+}
+
export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); }
export function returnTrue() { return true; }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e7ac1e321..673acfbaf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -65,7 +65,7 @@ export interface DocumentOptions {
panY?: number;
page?: number;
scale?: number;
- layout?: string;
+ layout?: string | Doc;
isTemplate?: boolean;
templates?: List<string>;
viewType?: number;
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 4c9c9c17c..252accefa 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -32,7 +32,7 @@ export function SetupDrag(
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
let doc = await docFunc();
- var dragData = new DragManager.DocumentDragData([doc], [undefined]);
+ var dragData = new DragManager.DocumentDragData([doc]);
dragData.dropAction = dropAction;
dragData.moveDocument = moveFunc;
dragData.options = options;
@@ -76,7 +76,7 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num
if (draggeddoc) {
let moddrag = await Cast(draggeddoc.annotationOn, Doc);
let dragdocs = moddrag ? [moddrag] : [draggeddoc];
- let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs);
+ let dragData = new DragManager.DocumentDragData(dragdocs);
dragData.moveDocument = moveLinkedDocument;
DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
handlers: {
@@ -107,7 +107,7 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n
if (doc) moddrag.push(doc);
}
let dragdocs = moddrag.length ? moddrag : draggedDocs;
- let dragData = new DragManager.DocumentDragData(dragdocs, dragdocs);
+ let dragData = new DragManager.DocumentDragData(dragdocs);
dragData.moveDocument = moveLinkedDocument;
DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, {
handlers: {
@@ -201,15 +201,13 @@ export namespace DragManager {
export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
export class DocumentDragData {
- constructor(dragDoc: Doc[], dragDataDocs: (Doc | undefined)[]) {
+ constructor(dragDoc: Doc[]) {
this.draggedDocuments = dragDoc;
- this.draggedDataDocs = dragDataDocs;
this.droppedDocuments = dragDoc;
this.xOffset = 0;
this.yOffset = 0;
}
draggedDocuments: Doc[];
- draggedDataDocs: (Doc | undefined)[];
droppedDocuments: Doc[];
xOffset: number;
yOffset: number;
@@ -253,7 +251,7 @@ export namespace DragManager {
}
export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize?: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
- let dragData = new DragManager.DocumentDragData([], [undefined]);
+ let dragData = new DragManager.DocumentDragData([]);
runInAction(() => StartDragFunctions.map(func => func()));
StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
(dropData: { [id: string]: any }) => {
@@ -363,8 +361,6 @@ export namespace DragManager {
const docs: Doc[] =
dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
- const datadocs: (Doc | undefined)[] =
- dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : [];
let dragElements = eles.map(ele => {
const w = ele.offsetWidth,
h = ele.offsetHeight;
@@ -449,7 +445,7 @@ export namespace DragManager {
pageY: e.pageY,
preventDefault: emptyFunction,
button: 0
- }, docs, datadocs);
+ }, docs);
}
//TODO: Why can't we use e.movementX and e.movementY?
let moveX = e.pageX - lastX;
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index e9ff21b22..67c8e931d 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -52,7 +52,9 @@ export namespace HistoryUtil {
}
export function getState(): ParsedUrl {
- return copyState(history.state);
+ let state = copyState(history.state);
+ state.initializers = state.initializers || {};
+ return state;
}
// export function addHandler(handler: (state: ParsedUrl | null) => void) {
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 156390fd3..7abb9d1ee 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -127,7 +127,7 @@ export namespace UndoManager {
export function StartBatch(batchName: string): Batch {
batchCounter++;
- if (batchCounter > 0) {
+ if (batchCounter > 0 && currentBatch === undefined) {
currentBatch = [];
}
return new Batch(batchName);
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 68b97f2b6..5d452e72e 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -248,7 +248,7 @@ export class ContextMenu extends React.Component {
e.preventDefault();
} else if (e.key === "Enter" || e.key === "Tab") {
const item = this.flatItems[this.selectedIndex];
- item && item.event();
+ item && item.event({ x: this.pageX, y: this.pageY });
this.closeMenu();
e.preventDefault();
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ac103b2ea..7829bd7f1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -16,7 +16,7 @@ import { undoBatch, UndoManager } from "../util/UndoManager";
import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
import { CollectionView } from "./collections/CollectionView";
import './DocumentDecorations.scss';
-import { DocumentView, PositionDocument } from "./nodes/DocumentView";
+import { DocumentView } from "./nodes/DocumentView";
import { FieldView } from "./nodes/FieldView";
import { FormattedTextBox, GoogleRef } from "./nodes/FormattedTextBox";
import { IconBox } from "./nodes/IconBox";
@@ -32,6 +32,9 @@ import { CurrentUserUtils } from '../../server/authentication/models/current_use
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
import { ObjectField } from '../../new_fields/ObjectField';
import { DocServer } from '../DocServer';
+import { CompileScript } from '../util/Scripting';
+import { ComputedField } from '../../new_fields/ScriptField';
+import { PositionDocument } from './nodes/CollectionFreeFormDocumentView';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -253,7 +256,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let dragDocView = SelectionManager.SelectedDocuments()[0];
const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
- let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc ? dv.props.DataDoc : dv.props.Document));
+ let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
dragData.xOffset = xoff;
dragData.yOffset = yoff;
dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
@@ -358,15 +361,15 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (this._iconDoc && selectedDocs.length === 1 && this._removeIcon) {
selectedDocs[0].props.removeDocument && selectedDocs[0].props.removeDocument(this._iconDoc);
}
- if (!this._removeIcon) {
- if (selectedDocs.length === 1) {
- this.getIconDoc(selectedDocs[0]).then(icon => selectedDocs[0].toggleMinimized());
- } else if (Math.abs(e.pageX - this._downX) < Utils.DRAG_THRESHOLD &&
- Math.abs(e.pageY - this._downY) < Utils.DRAG_THRESHOLD) {
- let docViews = SelectionManager.ViewsSortedVertically();
- let topDocView = docViews[0];
- topDocView.props.Document.subBulletDocs = new List<Doc>(docViews.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
- }
+ if (!this._removeIcon && selectedDocs.length === 1) { // if you click on the top-left button when just 1 doc is selected, then collapse it. not sure why we don't do it for multiple selections
+ this.getIconDoc(selectedDocs[0]).then(async icon => {
+ let minimizedDoc = await Cast(selectedDocs[0].props.Document.minimizedDoc, Doc);
+ if (minimizedDoc) {
+ let scrpt = selectedDocs[0].props.ScreenToLocalTransform().scale(selectedDocs[0].props.ContentScaling()).inverse().transformPoint(
+ NumCast(minimizedDoc.x) - NumCast(selectedDocs[0].Document.x), NumCast(minimizedDoc.y) - NumCast(selectedDocs[0].Document.y));
+ selectedDocs[0].collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
+ }
+ });
}
this._removeIcon = false;
}
@@ -378,8 +381,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let doc = selected[0].props.Document;
let iconDoc = Docs.Create.IconDocument(layoutString);
iconDoc.isButton = true;
- iconDoc.proto!.title = selected.length > 1 ? "-multiple-.icon" : StrCast(doc.title) + ".icon";
- iconDoc.labelField = selected.length > 1 ? undefined : this._fieldKey;
+
+ IconBox.AutomaticTitle(iconDoc);
//iconDoc.proto![this._fieldKey] = selected.length > 1 ? "collection" : undefined;
iconDoc.proto!.isMinimized = false;
iconDoc.width = Number(MINIMIZED_ICON_SIZE);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f9ee22f61..ba125d6e5 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -166,7 +166,7 @@ export default class KeyManager {
break;
case "o":
let target = SelectionManager.SelectedDocuments()[0];
- target && target.fullScreenClicked();
+ target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target)
break;
case "r":
preventDefault = false;
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 71fb2707d..cd8423a12 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -102,7 +102,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) {
document.removeEventListener("pointermove", this.textBoxMove);
document.removeEventListener('pointerup', this.textBoxUp);
- let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]);
+ let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document]);
const [left, top] = this._textXf().inverse().transformPoint(0, 0);
dragData.xOffset = e.clientX - left;
dragData.yOffset = e.clientY - top;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index af3fcaa24..d57a72dad 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -67,7 +67,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
setTimeout(() => {
let newDocView = DocumentManager.Instance.getDocumentView(topDoc);
if (newDocView) {
- let de = new DragManager.DocumentDragData([topDoc], [undefined]);
+ let de = new DragManager.DocumentDragData([topDoc]);
de.moveDocument = topDocView.props.moveDocument;
let xf = newDocView.ContentDiv!.getBoundingClientRect();
DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, {
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b7036b3ff..a54718e9e 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -141,17 +141,16 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
return false;
}
+ // this is called with the document that was dragged and the collection to move it into.
+ // if the target collection is the same as this collection, then the move will be allowed.
+ // otherwise, the document being moved must be able to be removed from its container before
+ // moving it into the target.
@action.bound
moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- let self = this;
- let targetDataDoc = this.props.Document;
- if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) {
+ if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
return true;
}
- if (this.removeDocument(doc)) {
- return addDocument(doc);
- }
- return false;
+ return this.removeDocument(doc) ? addDocument(doc) : false;
}
render() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 166fa0811..a350cfcc5 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -66,15 +66,15 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
hack: boolean = false;
undohack: any = null;
- public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs: (Doc | undefined)[] = []) {
+ public StartOtherDrag(e: any, dragDocs: Doc[]) {
let config: any;
if (dragDocs.length === 1) {
- config = CollectionDockingView.makeDocumentConfig(dragDocs[0], dragDataDocs[0]);
+ config = CollectionDockingView.makeDocumentConfig(dragDocs[0], undefined);
} else {
config = {
type: 'row',
content: dragDocs.map((doc, i) => {
- CollectionDockingView.makeDocumentConfig(doc, dragDataDocs[i]);
+ CollectionDockingView.makeDocumentConfig(doc, undefined);
})
};
}
@@ -84,14 +84,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
dragSource.destroy();
});
dragSource._dragListener.onMouseDown(e);
- // dragSource.destroy();
- // this.hack = true;
- // this.undohack = UndoManager.StartBatch("goldenDrag");
- // dragDocs.map((dragDoc, i) =>
- // this.AddRightSplit(dragDoc, dragDataDocs[i], true).contentItems[0].tab._dragListener.
- // onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
+ @undoBatch
@action
public OpenFullScreen(docView: DocumentView) {
let document = Doc.MakeAlias(docView.props.Document);
@@ -107,6 +102,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._maximizedSrc = docView;
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
+ SelectionManager.DeselectAll();
}
public CloseFullScreen = () => {
@@ -175,6 +171,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
+ @undoBatch
@action
public AddRightSplit = (document: Doc, dataDoc: Doc | undefined, minimize: boolean = false) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
@@ -213,6 +210,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
+
+ @undoBatch
@action
public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
Doc.GetProto(document).lastOpened = new DateField;
@@ -412,7 +411,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
e => {
e.preventDefault();
e.stopPropagation();
- DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc], [dataDoc]), e.clientX, e.clientY, {
+ DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
handlers: { dragComplete: emptyFunction },
hideSource: false
});
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 17a3f4f7c..3452e8702 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -172,7 +172,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
let onItemDown = (e: React.PointerEvent) => {
if (fieldIsDoc) {
SetupDrag(this._focusRef, () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
- this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument, this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
+ this._document[props.fieldKey] instanceof Doc ? (doc: Doc, target: Doc, addDoc: (newDoc: Doc) => any) => addDoc(doc) : this.props.moveDocument,
+ this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
}
};
let onPointerEnter = (e: React.PointerEvent): void => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1a84f94c8..25d3bd128 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,7 @@ import { listSpec } from "../../../new_fields/Schema";
import { Docs, DocumentOptions } from "../../documents/Documents";
import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { Gateway } from "../../northstar/manager/Gateway";
-import { SetupDrag, DragManager } from "../../util/DragManager";
+import { DragManager } from "../../util/DragManager";
import { CompileScript, ts, Transformer } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 185bec7a2..34f2652ff 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -183,7 +183,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
if (compiled.compiled) {
let scriptField = new ScriptField(compiled);
alias.viewSpecScript = scriptField;
- let dragData = new DragManager.DocumentDragData([alias], [alias.proto]);
+ let dragData = new DragManager.DocumentDragData([alias]);
DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e6fadd71e..de26ac856 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -53,8 +53,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
+ get childLayoutPairs() {
+ return this.childDocs.map(cd => Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, cd)).filter(pair => pair.layout).map(pair => ({ layout: pair.layout!, data: pair.data! }));
+ }
get childDocs() {
- let self = this;
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
let docs = DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
@@ -129,9 +131,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return moved || added;
}, false);
} else if (de.data.moveDocument) {
- let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
- added = movedDocs.reduce((added: boolean, d) =>
- de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
+ let movedDocs = de.data.draggedDocuments;// de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
+ // note that it's possible the drag function might create a drop document that's not the same as the
+ // original dragged document. So we explicitly call addDocument() with a droppedDocument and
+ added = movedDocs.reduce((added: boolean, d, i) =>
+ de.data.moveDocument(d, this.props.Document, (doc: Doc) => this.props.addDocument(de.data.droppedDocuments[i])) || added, false);
} else {
added = de.data.droppedDocuments.reduce((added: boolean, d) => {
let moved = this.props.addDocument(d);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6217ef859..40bea2f9d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -347,7 +347,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
- return <div className="bullet" title="view inline" onClick={action(() => this.treeViewOpen = !this.treeViewOpen)} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
+ return <div className="bullet" title="view inline" onClick={action((e: React.MouseEvent) => { this.treeViewOpen = !this.treeViewOpen; e.stopPropagation(); })} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
{<FontAwesomeIcon icon={!this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down")} />}
</div>;
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index bce4eb427..548f663ec 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -95,8 +95,9 @@ export class CollectionView extends React.Component<FieldViewProps> {
}
subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" });
+ subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
subItems.push({
- description: "Stacking", event: () => {
+ description: "Stacking (AutoHeight)", event: () => {
this.props.Document.viewType = CollectionViewType.Stacking;
this.props.Document.autoHeight = true
}, icon: "ellipsis-v"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0025634a6..6013998d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -52,6 +52,9 @@ export const panZoomSchema = createSchema({
scale: "number",
arrangeScript: ScriptField,
arrangeInit: ScriptField,
+ useClusters: "boolean",
+ isRuleProvider: "boolean",
+ fitToBox: "boolean"
});
export interface ViewDefBounds {
@@ -183,9 +186,10 @@ const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema)
export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _lastX: number = 0;
private _lastY: number = 0;
+ private _inkKey = "ink"; // the document key used to store ink annotation strokes
private get _pwidth() { return this.props.PanelWidth(); }
private get _pheight() { return this.props.PanelHeight(); }
- private _timelineRef = React.createRef<Timeline>();
+ private _timelineRef = React.createRef<Timeline>();
private inkKey = "ink";
private _childLayoutDisposer?: IReactionDisposer;
private _childDisposer?: IReactionDisposer;
@@ -245,7 +249,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return res;
}
- @computed get fitToBox() { return this.props.fitToBox || this.props.Document.fitToBox; }
+ @computed get fitToBox() { return this.props.fitToBox || this.Document.fitToBox; }
@computed get nativeWidth() { return this.fitToBox ? 0 : this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.fitToBox ? 0 : this.Document.nativeHeight || 0; }
public get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt')
@@ -268,7 +272,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
DateCast((Cast(a.data_ext, Doc) as Doc).lastModified).date < DateCast((Cast(b.data_ext, Doc) as Doc).lastModified).date ? -1 : 0);
heading = !sorted.length ? Math.max(1, maxHeading) : NumCast(sorted[sorted.length - 1].heading) === 1 ? 2 : NumCast(sorted[sorted.length - 1].heading);
}
- !this.props.Document.isRuleProvider && (newBox.heading = heading);
+ !this.Document.isRuleProvider && (newBox.heading = heading);
this.addDocument(newBox, false);
}
private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
@@ -284,10 +288,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
public getActiveDocuments = () => {
const curPage = FieldValue(this.Document.curPage, -1);
- return this.childDocs.filter(doc => {
- var page = NumCast(doc.page, -1);
+ return this.childLayoutPairs.filter(pair => {
+ var page = NumCast(pair.layout!.page, -1);
return page === curPage || page === -1;
- });
+ }).map(pair => pair.layout);
}
@computed get fieldExtensionDoc() {
@@ -365,7 +369,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
tryDragCluster(e: PointerEvent) {
let probe = this.getTransform().transformPoint(e.clientX, e.clientY);
- let cluster = this.childDocs.reduce((cluster, cd) => {
+ let cluster = this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
let cx = NumCast(cd.x) - this._clusterDistance;
let cy = NumCast(cd.y) - this._clusterDistance;
let cw = NumCast(cd.width) + 2 * this._clusterDistance;
@@ -376,7 +380,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return cluster;
}, -1);
if (cluster !== -1) {
- let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster);
+ let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
// hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
let prevSelected = SelectionManager.SelectedDocuments();
@@ -385,7 +389,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
SelectionManager.DeselectAll();
prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
- let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined));
+ let de = new DragManager.DocumentDragData(eles);
de.moveDocument = this.props.moveDocument;
const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top);
@@ -405,9 +409,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@undoBatch
@action
- updateClusters() {
+ updateClusters(useClusters: boolean) {
+ this.Document.useClusters = useClusters;
this.sets.length = 0;
- this.childDocs.map(c => {
+ this.childLayoutPairs.map(pair => pair.layout).map(c => {
let included = [];
for (let i = 0; i < this.sets.length; i++) {
for (let member of this.sets[i]) {
@@ -435,20 +440,21 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@undoBatch
@action
updateCluster(doc: Doc) {
+ let childLayouts = this.childLayoutPairs.map(pair => pair.layout);
if (this.props.Document.useClusters) {
this.sets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
let preferredInd = NumCast(doc.cluster);
doc.cluster = -1;
this.sets.map((set, i) => set.map(member => {
- if (doc.cluster === -1 && Doc.IndexOf(member, this.childDocs) !== -1 && this.boundsOverlap(doc, member)) {
+ if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && this.boundsOverlap(doc, member)) {
doc.cluster = i;
}
}));
- if (doc.cluster === -1 && preferredInd !== -1 && (!this.sets[preferredInd] || !this.sets[preferredInd].filter(member => Doc.IndexOf(member, this.childDocs) !== -1).length)) {
+ if (doc.cluster === -1 && preferredInd !== -1 && (!this.sets[preferredInd] || !this.sets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
doc.cluster = preferredInd;
}
this.sets.map((set, i) => {
- if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, this.childDocs) !== -1).length) {
+ if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
doc.cluster = i;
}
});
@@ -463,22 +469,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getClusterColor = (doc: Doc) => {
- if (this.props.Document.useClusters) {
- let cluster = NumCast(doc.cluster);
+ let clusterColor = "";
+ let cluster = NumCast(doc.cluster);
+ if (this.Document.useClusters) {
if (this.sets.length <= cluster) {
- setTimeout(() => this.updateCluster(doc), 0);// this.updateClusters(), 0);
- return "";
+ setTimeout(() => this.updateCluster(doc), 0);
+ } else {
+ // choose a cluster color from a palette
+ let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+ clusterColor = colors[cluster % colors.length];
+ let set = this.sets.length > cluster ? this.sets[cluster].filter(s => s.backgroundColor && (s.backgroundColor != s.defaultBackgroundColor)) : undefined;
+ // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
+ set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+ set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
}
- let set = this.sets.length > cluster ? this.sets[cluster] : undefined;
- let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
- let clusterColor = colors[cluster % colors.length];
- set && set.filter(s => !s.isBackground).map(s =>
- s.backgroundColor && s.backgroundColor !== s.defaultBackgroundColor && (clusterColor = StrCast(s.backgroundColor)));
- set && set.filter(s => s.isBackground).map(s =>
- s.backgroundColor && s.backgroundColor !== s.defaultBackgroundColor && (clusterColor = StrCast(s.backgroundColor)));
- return clusterColor;
}
- return "";
+ return clusterColor;
}
@action
@@ -510,7 +516,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
let x = this.Document.panX || 0;
let y = this.Document.panY || 0;
- let docs = this.childDocs || [];
+ let docs = this.childLayoutPairs.map(pair => pair.layout);
let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay) {
PDFMenu.Instance.fadeOut(true);
@@ -557,31 +563,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (BoolCast(this.props.Document.lockedPosition)) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
- return;
- }
-
- let childSelected = this.childDocs.some(doc => {
- var dv = DocumentManager.Instance.getDocumentView(doc);
- return dv && SelectionManager.IsSelected(dv) ? true : false;
- });
- if (!this.props.isSelected() && !childSelected && this.props.renderDepth > 0) {
- return;
}
- e.stopPropagation();
-
- // bcz: this changes the nativewidth/height, but ImageBox will just revert it back to its defaults. need more logic to fix.
- // if (e.ctrlKey && this.props.Document.scrollHeight === undefined) {
- // let deltaScale = (1 - (e.deltaY / coefficient));
- // let nw = this.nativeWidth * deltaScale;
- // let nh = this.nativeHeight * deltaScale;
- // if (nw && nh) {
- // this.props.Document.nativeWidth = nw;
- // this.props.Document.nativeHeight = nh;
- // }
- // e.preventDefault();
- // }
- // else
- {
+ else if (this.props.active()) {
+ e.stopPropagation();
let deltaScale = e.deltaY > 0 ? (1 / 1.1) : 1.1;
if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
deltaScale = 1 / this.zoomScaling();
@@ -615,41 +599,33 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
super.onDrop(e, { x: pt[0], y: pt[1] });
}
- onDragOver = (): void => {
- }
-
bringToFront = (doc: Doc, sendToBack?: boolean) => {
if (sendToBack || doc.isBackground) {
doc.zIndex = 0;
- return;
}
- const docs = this.childDocs;
- docs.slice().sort((doc1, doc2) => {
- if (doc1 === doc) return 1;
- if (doc2 === doc) return -1;
- return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
- }).forEach((doc, index) => doc.zIndex = index + 1);
- doc.zIndex = docs.length + 1;
+ else {
+ const docs = this.childLayoutPairs.map(pair => pair.layout);
+ docs.slice().sort((doc1, doc2) => {
+ if (doc1 === doc) return 1;
+ if (doc2 === doc) return -1;
+ return NumCast(doc1.zIndex) - NumCast(doc2.zIndex);
+ }).forEach((doc, index) => doc.zIndex = index + 1);
+ doc.zIndex = docs.length + 1;
+ }
}
focusDocument = (doc: Doc, willZoom: boolean, scale?: number) => {
- const panX = this.Document.panX;
- const panY = this.Document.panY;
- const id = this.Document[Id];
const state = HistoryUtil.getState();
- state.initializers = state.initializers || {};
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
- if (state.type === "doc" && panX !== undefined && panY !== undefined) {
- const init = state.initializers[id];
+ if (state.type === "doc" && this.Document.panX !== undefined && this.Document.panY !== undefined) {
+ const init = state.initializers![this.Document[Id]];
if (!init) {
- state.initializers[id] = {
- panX, panY
- };
+ state.initializers![this.Document[Id]] = { panX: this.Document.panX, panY: this.Document.panY };
HistoryUtil.pushState(state);
- } else if (init.panX !== panX || init.panY !== panY) {
- init.panX = panX;
- init.panY = panY;
+ } else if (init.panX !== this.Document.panX || init.panY !== this.Document.panY) {
+ init.panX = this.Document.panX;
+ init.panY = this.Document.panY;
HistoryUtil.pushState(state);
}
}
@@ -657,7 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
const newState = HistoryUtil.getState();
- (newState.initializers || (newState.initializers = {}))[id] = { panX: newPanX, panY: newPanY };
+ newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
this.setPan(newPanX, newPanY);
@@ -666,7 +642,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (willZoom) {
this.setScaleToZoom(doc, scale);
}
-
}
setScaleToZoom = (doc: Doc, scale: number = 0.5) => {
@@ -698,8 +673,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
moveDocument: this.props.moveDocument,
- ruleProvider: this.props.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider,
- onClick: this.props.onClick,
+ ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
+ onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
renderDepth: this.props.renderDepth + 1,
PanelWidth: childLayout[WidthSym],
@@ -746,11 +721,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getCalculatedPositions(script: ScriptField, params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, state?: any } {
const result = script.script.run(params);
- if (!result.success) {
- return {};
- }
- let doc = params.doc;
- return result.result === undefined ? { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") } : result.result;
+ return !result.success ? {} : result.result !== undefined ? result.result :
+ { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(params.doc.width, "number"), height: Cast(params.doc.height, "number") };
}
viewDefsToJSX = (views: any[]) => {
@@ -794,12 +766,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const initScript = this.Document.arrangeInit;
const script = this.Document.arrangeScript;
let state: any = undefined;
- let docs = this.childDocs;
- let overlayDocs = DocListCast(this.props.Document.localOverlays);
- overlayDocs && docs.push(...overlayDocs);
+ let pairs = this.childLayoutPairs;
let elements: ViewDefResult[] = [];
if (initScript) {
- const initResult = initScript.script.run({ docs, collection: this.Document });
+ const initResult = initScript.script.run({ docs: pairs.map(pair => pair.layout), collection: this.Document });
if (initResult.success) {
const result = initResult.result;
const { state: scriptState, views } = result;
@@ -807,27 +777,26 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
elements = this.viewDefsToJSX(views);
}
}
- let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
- var page = NumCast(doc.page, -1);
- // if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
- let minim = BoolCast(doc.isMinimized);
- if (minim === undefined || !minim) {
- const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) :
- { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") };
- state = pos.state === undefined ? state : pos.state;
- let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, doc);
- if (pair.layout && !(pair.data instanceof Promise)) {
- prev.push({
- ele: <CollectionFreeFormDocumentView key={doc[Id]}
- ruleProvider={this.props.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
- jitterRotation={NumCast(this.props.Document.jitterRotation)}
- x={script ? pos.x : undefined} y={script ? pos.y : undefined}
- width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
- bounds: { x: pos.x || 0, y: pos.y || 0, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) }
- });
- }
+ let docviews = pairs.reduce((prev, pair) => {
+ var page = NumCast(pair.layout.page, -1);
+ //if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
+ let minim = BoolCast(pair.layout.isMinimized);
+ if (minim === undefined || !minim) {
+ const pos = script ? this.getCalculatedPositions(script, { doc: pair.layout, index: prev.length, collection: this.Document, docs: pairs.map(pair => pair.layout), state }) :
+ { x: Cast(pair.layout.x, "number"), y: Cast(pair.layout.y, "number"), z: Cast(pair.layout.z, "number"), width: Cast(pair.layout.width, "number"), height: Cast(pair.layout.height, "number") };
+ state = pos.state === undefined ? state : pos.state;
+ if (pair.layout && !(pair.data instanceof Promise)) {
+ prev.push({
+ ele: <CollectionFreeFormDocumentView key={pair.layout[Id]}
+ ruleProvider={this.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
+ jitterRotation={NumCast(this.props.Document.jitterRotation)}
+ x={script ? pos.x : undefined} y={script ? pos.y : undefined}
+ width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
+ bounds: { x: pos.x || 0, y: pos.y || 0, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) }
+ });
}
- // }
+ }
+ // }
return prev;
}, elements);
@@ -836,22 +805,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed.struct
get views() {
- let source = this.elements;
- return source.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
+ return this.elements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
}
@computed.struct
get overlayViews() {
return this.elements.filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele);
}
-
@action
onCursorMove = (e: React.PointerEvent) => {
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
- fitToContainer = async () => this.props.Document.fitToBox = !this.fitToBox;
-
arrangeContents = async () => {
const docs = await DocListCastAsync(this.Document[this.props.fieldKey]);
UndoManager.RunInBatch(() => {
@@ -877,111 +842,94 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
autoFormat = () => {
- this.props.Document.isRuleProvider = !this.props.Document.isRuleProvider;
- this.childDocs.map(child => child.heading = undefined);
- this.childDocs.map(child => {
- DocListCast(child.layout instanceof Doc ? child.layout.data : child.data).map(heading => {
- let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
- let disp = (child.data_ext instanceof Doc) && pair.layout && (child.data_ext[`Layout[${pair.layout[Id]}]`] as Doc);
- if (disp && NumCast(disp.heading) > 0) {
- if (disp.backgroundColor !== disp.defaultBackgroundColor) {
- Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(disp.heading)] = disp.backgroundColor;
- }
- }
- if (pair.layout && NumCast(pair.layout.heading) > 0) {
- if (pair.layout.backgroundColor !== pair.layout.defaultBackgroundColor) {
- Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(pair.layout.heading)] = pair.layout.backgroundColor;
- }
+ this.Document.isRuleProvider = !this.Document.isRuleProvider;
+ // find rule colorations when rule providing is turned on by looking at each document to see if it has a coloring -- if so, use it's color as the rule for its associated heading.
+ this.Document.isRuleProvider && this.childLayoutPairs.map(pair =>
+ // iterate over the children of a displayed document (or if the displayed document is a template, iterate over the children of that template)
+ DocListCast(pair.layout.layout instanceof Doc ? pair.layout.layout.data : pair.layout.data).map(heading => {
+ let headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
+ let headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
+ if (headingLayout && NumCast(headingLayout.heading) > 0 && headingLayout.backgroundColor !== headingLayout.defaultBackgroundColor) {
+ Doc.GetProto(this.props.Document)["ruleColor_" + NumCast(headingLayout.heading)] = headingLayout.backgroundColor;
}
- });
- });
+ })
+ );
}
analyzeStrokes = async () => {
- let data = Cast(this.fieldExtensionDoc[this.inkKey], InkField);
- if (!data) {
- return;
+ let data = Cast(this.fieldExtensionDoc[this._inkKey], InkField);
+ if (data) {
+ CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.fieldExtensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
}
- let relevantKeys = ["inkAnalysis", "handwriting"];
- CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.fieldExtensionDoc, relevantKeys, data.inkData);
}
onContextMenu = (e: React.MouseEvent) => {
let layoutItems: ContextMenuProps[] = [];
if (this.childDocs.some(d => BoolCast(d.isTemplate))) {
- layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
+ layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
}
- this._timelineRef.current!.timelineContextMenu(e.nativeEvent);
+ this._timelineRef.current!.timelineContextMenu(e.nativeEvent);
layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
- layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
- layoutItems.push({
- description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`,
- event: async () => {
- Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; // backward compatibility with databases that didn't have a default background color on prototypes
- Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white";
- this.props.Document.useClusters = !this.props.Document.useClusters;
- this.updateClusters();
- },
- icon: !this.props.Document.useClusters ? "braille" : "braille"
- });
- layoutItems.push({ description: `${this.props.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`, event: this.autoFormat, icon: !this.props.Document.isRuleProvider ? "chalkboard" : "chalkboard" });
+ layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: async () => this.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
+ layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
+ layoutItems.push({ description: `${this.Document.isRuleProvider ? "Stop Auto Format" : "Auto Format"}`, event: this.autoFormat, icon: "chalkboard" });
layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" });
layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" });
layoutItems.push({
- description: "Import document", icon: "upload", event: () => {
+ description: "Import document", icon: "upload", event: ({ x, y }) => {
const input = document.createElement("input");
input.type = "file";
input.accept = ".zip";
input.onchange = async _e => {
- const files = input.files;
- if (!files) return;
- const file = files[0];
- let formData = new FormData();
- formData.append('file', file);
- formData.append('remap', "true");
const upload = Utils.prepend("/uploadDoc");
- const response = await fetch(upload, { method: "POST", body: formData });
- const json = await response.json();
- if (json === "error") {
- return;
- }
- const doc = await DocServer.GetRefField(json);
- if (!doc || !(doc instanceof Doc)) {
- return;
+ let formData = new FormData();
+ const file = input.files && input.files[0];
+ if (file) {
+ formData.append('file', file);
+ formData.append('remap', "true");
+ const response = await fetch(upload, { method: "POST", body: formData });
+ const json = await response.json();
+ if (json !== "error") {
+ const doc = await DocServer.GetRefField(json);
+ if (doc instanceof Doc) {
+ const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
+ doc.x = xx, doc.y = yy;
+ this.props.addDocument && this.props.addDocument(doc, false);
+ }
+ }
}
- const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY);
- doc.x = x, doc.y = y;
- this.props.addDocument &&
- this.props.addDocument(doc, false);
};
input.click();
}
});
+ //@ts-ignore
+ let subitems: ContextMenuProps[] =
+ DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({
+ description: (i + 1) + ": " + StrCast(note.title),
+ event: () => console.log("Hi"),
+ icon: "eye"
+ }));
- let noteItems: ContextMenuProps[] = [];
- let notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
- notes.map((node, i) => noteItems.push({ description: (i + 1) + ": " + StrCast(node.title), event: () => this.createText(i), icon: "eye" }));
- layoutItems.push({ description: "Add Note ...", subitems: noteItems, icon: "eye" });
+ layoutItems.push({
+ description: "Add Note ...",
+ subitems: subitems,
+ // subitems: DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({
+ // description: (i + 1) + ": " + StrCast(note.title),
+ // event: ({ x, y }) => this.addLiveTextBox(Docs.Create.TextDocument({ width: 200, height: 100, x: this.getTransform().transformPoint(x, y)[0], y: this.getTransform().transformPoint(x, y)[1], autoHeight: true, layout: note, title: StrCast(note.title) })),
+ // icon: "eye"
+ // })),
+ icon: "eye"
+ });
ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
}
- createText = (noteStyle: number) => {
- let pt = this.getTransform().transformPoint(ContextMenu.Instance.pageX, ContextMenu.Instance.pageY);
- let notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
- let text = Docs.Create.TextDocument({ width: 200, height: 100, x: pt[0], y: pt[1], autoHeight: true, title: StrCast(notes[noteStyle % notes.length].title) });
- text.layout = notes[noteStyle % notes.length];
- this.addLiveTextBox(text);
- }
private childViews = () => [
<CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
...this.views
]
- private overlayChildViews = () => {
- return [...this.overlayViews];
- }
public static AddCustomLayout(doc: Doc, dataKey: string): () => void {
return () => {
@@ -1009,7 +957,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300, title: "Layout Initialization" }, { collection: "Doc", docs: "Doc[]" }, undefined);
addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}");
};
- }
+ }
render() {
this.props.Document.fitX = this.actualContentBounds && this.actualContentBounds.x;
this.props.Document.fitY = this.actualContentBounds && this.actualContentBounds.y;
@@ -1019,22 +967,22 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
return (
<div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onContextMenu={this.onContextMenu}>
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}>
<MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
<CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
<CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
+ <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={this._inkKey} >
{this.childViews}
</InkingCanvas>
</CollectionFreeFormLinksView>
<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
</CollectionFreeFormViewPannableContents>
</MarqueeView>
- <Timeline ref={this._timelineRef} {...this.props}/>
- {this.overlayChildViews()}
+ <Timeline ref={this._timelineRef} {...this.props} />
+ {this.overlayViews}
<CollectionFreeFormOverlayView {...this.props} {...this.getDocumentViewProps(this.props.Document)} />
</div>
);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index cc5e887b2..8decebe0d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -23,6 +23,8 @@ import { SchemaHeaderField, RandomPastel } from "../../../../new_fields/SchemaHe
import { string } from "prop-types";
import { listSpec } from "../../../../new_fields/Schema";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
+import { CompileScript } from "../../../util/Scripting";
+import { ComputedField } from "../../../../new_fields/ScriptField";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -309,13 +311,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : chosenColor,
width: bounds.width,
height: bounds.height,
- title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection",
+ title: "a nested collection",
});
let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
this.marqueeInkDelete(inkData);
- if (e.key === "s") {
+ if (e.key === "s" || e.key === "S") {
selected.map(d => {
this.props.removeDocument(d);
d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
@@ -324,35 +326,22 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
return d;
});
newCollection.chromeStatus = "disabled";
- let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
- newCollection.proto!.summaryDoc = summary;
- selected = [newCollection];
+ let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ Doc.GetProto(newCollection).summaryDoc = summary;
+ Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
newCollection.x = bounds.left + bounds.width;
- summary.proto!.subBulletDocs = new List<Doc>(selected);
- let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
- container.viewType = CollectionViewType.Stacking;
- container.autoHeight = true;
- this.props.addLiveTextDocument(container);
- // });
- } else if (e.key === "S") {
- selected.map(d => {
- this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
- return d;
- });
- newCollection.chromeStatus = "disabled";
- let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
- newCollection.proto!.summaryDoc = summary;
- selected = [newCollection];
- newCollection.x = bounds.left + bounds.width;
- //this.props.addDocument(newCollection, false);
- summary.proto!.summarizedDocs = new List<Doc>(selected);
- summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
- summary.autoHeight = true;
-
- this.props.addLiveTextDocument(summary);
+ let computed = CompileScript(`return summaryTitle(this);`, { params: { this: "Doc" }, typecheck: false });
+ computed.compiled && (Doc.GetProto(newCollection).title = new ComputedField(computed));
+ if (e.key === "s") { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
+ let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+ container.viewType = CollectionViewType.Stacking;
+ container.autoHeight = true;
+ Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "inPlace", or "onRight"
+ this.props.addLiveTextDocument(container);
+ } else if (e.key === "S") { // 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);
+ }
}
else {
this.props.addDocument(newCollection, false);
diff --git a/src/client/views/document_templates/image_card/ImageCard.tsx b/src/client/views/document_templates/image_card/ImageCard.tsx
index 9931515f3..868afc423 100644
--- a/src/client/views/document_templates/image_card/ImageCard.tsx
+++ b/src/client/views/document_templates/image_card/ImageCard.tsx
@@ -1,8 +1,5 @@
import * as React from 'react';
-import { DocComponent } from '../../DocComponent';
import { FieldViewProps } from '../../nodes/FieldView';
-import { createSchema, makeInterface } from '../../../../new_fields/Schema';
-import { createInterface } from 'readline';
import { ImageBox } from '../../nodes/ImageBox';
export default class ImageCard extends React.Component<FieldViewProps> {
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index f8807641b..b1c6c367f 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -172,7 +172,6 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
if (LinkFollowBox.destinationDoc) {
let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc);
view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
- SelectionManager.DeselectAll();
}
}
@@ -188,7 +187,6 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
let view = DocumentManager.Instance.getDocumentView(options.context);
view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view);
this.highlightDoc();
- SelectionManager.DeselectAll();
}
}
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index b6a24b0c8..d711ac284 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -57,7 +57,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
let opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
if (opp) return opp;
}) as Doc[];
- let dragData = new DragManager.DocumentDragData(draggedDocs, draggedDocs.map(d => undefined));
+ let dragData = new DragManager.DocumentDragData(draggedDocs);
DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
handlers: {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
new file mode 100644
index 000000000..de0b00a81
--- /dev/null
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -0,0 +1,5 @@
+.collectionFreeFormDocumentView-container {
+ transform-origin: left top;
+ position: absolute;
+ background-color: transparent;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 4872a7aa1..19d4a6784 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,13 +1,14 @@
-import { computed } from "mobx";
+import { computed, action, observable, reaction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
-import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
-import "./DocumentView.scss";
+import { percent2frac } from "../../../Utils"
+import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView";
+import "./CollectionFreeFormDocumentView.scss";
import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { random } from "animejs";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@@ -17,30 +18,32 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
height?: number;
jitterRotation: number;
}
-
-const schema = createSchema({
+const positionSchema = createSchema({
zIndex: "number",
+ x: "number",
+ y: "number",
+ z: "number",
});
-//TODO Types: The import order is wrong, so positionSchema is undefined
-type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const FreeformDocument = makeInterface(schema, positionSchema);
+export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>;
+export const PositionDocument = makeInterface(documentSchema, positionSchema);
@observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
+ _disposer: IReactionDisposer | undefined = undefined;
@computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
- @computed get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
- @computed get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
- @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
- @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.Document.height || 0; }
- @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); }
- @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); }
- @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); }
+ @computed get X() { return this._animx !== undefined ? this._animx : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
+ @computed get Y() { return this._animy !== undefined ? this._animy : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
+ @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); }
+ @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); }
+ @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
+ @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
+ @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); }
@computed get renderScriptDim() {
if (this.Document.renderScript) {
- let someView = Cast(this.Document.someView, Doc);
- let minimap = Cast(this.Document.minimap, Doc);
+ let someView = Cast(this.props.Document.someView, Doc);
+ let minimap = Cast(this.props.Document.minimap, Doc);
if (someView instanceof Doc && minimap instanceof Doc) {
let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
@@ -52,37 +55,43 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return undefined;
}
- contentScaling = () => this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? this.width / this.nativeWidth : 1;
+ componentWillUnmount() {
+ this._disposer && this._disposer();
+ }
+ componentDidMount() {
+ this._disposer = reaction(() => this.props.Document.iconTarget,
+ () => {
+ const icon = this.props.Document.iconTarget ? Array.from(Cast(this.props.Document.iconTarget, listSpec("number"))!) : undefined;
+ if (icon) {
+ let target = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
+ if (icon[2] === 1) {
+ this._animx = target[0];
+ this._animy = target[1];
+ }
+ setTimeout(action(() => {
+ this._animx = icon[2] === 1 ? this.Document.x : target[0];
+ this._animy = icon[2] === 1 ? this.Document.y : target[1];
+ }), 25);
+ } else {
+ this._animx = this._animy = undefined;
+ }
+ }, { fireImmediately: true });
+ }
+
+ contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1;
panelWidth = () => this.props.PanelWidth();
panelHeight = () => this.props.PanelHeight();
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
- .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth)
-
- animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => {
- this.props.bringToFront(this.props.Document);
- let targetPos = [this.Document.x || 0, this.Document.y || 0];
- let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]);
- DocumentView.animateBetweenIconFunc(this.props.Document,
- this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => {
- let pval = maximizing ?
- [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] :
- [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress];
- this.Document.x = progress === 1 ? targetPos[0] : pval[0];
- this.Document.y = progress === 1 ? targetPos[1] : pval[1];
- });
- }
+ .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth);
borderRounding = () => {
- let ruleProvider = this.props.ruleProvider;
- let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
- let br = StrCast(this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout.borderRounding : this.props.Document.borderRounding);
+ let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+ let br = StrCast(((this.layoutDoc.layout as Doc) || this.Document).borderRounding);
br = !br && ruleRounding ? ruleRounding : br;
if (br.endsWith("%")) {
- let percent = Number(br.substr(0, br.length - 1)) / 100;
let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight));
- let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
- return minDim;
+ return percent2frac(br) * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight()));
}
return undefined;
}
@@ -98,24 +107,23 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
}
+ @observable _animx: number | undefined = undefined;
+ @observable _animy: number | undefined = undefined;
+
render() {
const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
return (
<div className="collectionFreeFormDocumentView-container"
style={{
- transformOrigin: "left top",
- position: "absolute",
- backgroundColor: "transparent",
boxShadow:
this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.layoutDoc.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
- this.clusterColor ? (
- `${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.layoutDoc.isBackground ? `1px 1px 1px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
- transition: hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
+ transition: this.props.Document.isIconAnimating ? "transform .5s" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
zIndex: this.Document.zIndex || 0,
@@ -126,7 +134,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
backgroundColor={this.clusterColorFunc}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
- animateBetweenIcon={this.animateBetweenIcon}
/>
</div>
);
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 7c72fb6e6..63011f271 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,6 +4,8 @@
position: inherit;
top: 0;
left:0;
+ border-radius: inherit;
+
// background: $light-color; //overflow: hidden;
transform-origin: left top;
@@ -30,4 +32,44 @@
}
.documentView-node-topmost {
background: white;
+}
+
+.documentView-styleWrapper {
+ position: absolute;
+ display: inline-block;
+ width:100%;
+ height:100%;
+ pointer-events: none;
+
+ .documentView-styleContentWrapper {
+ width:100%;
+ display: inline-block;
+ position: absolute;
+ }
+ .documentView-titleWrapper {
+ overflow:hidden;
+ color: white;
+ transform-origin: top left;
+ top: 0;
+ height: 25;
+ background: rgba(0, 0, 0, .4);
+ padding: 4px;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: pre;
+ }
+
+ .documentView-searchHighlight {
+ position: absolute;
+ background: yellow;
+ bottom: -20px;
+ border-radius: 5px;
+ transform-origin: bottom left;
+ }
+
+ .documentView-captionWrapper {
+ position: absolute;
+ bottom: 0;
+ transform-origin: bottom left;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2f0dbf0c9..65a8a4234 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -102,35 +102,41 @@ export interface DocumentViewProps {
zoomToScale: (scale: number) => void;
backgroundColor: (doc: Doc) => string | undefined;
getScale: () => number;
- animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
+ animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
ChromeHeight?: () => number;
}
-const schema = createSchema({
- layout: "string",
+export const documentSchema = createSchema({
+ layout: "string", // should also allow Doc but that can't be expressed in the schema
+ title: "string",
nativeWidth: "number",
nativeHeight: "number",
backgroundColor: "string",
opacity: "number",
hidden: "boolean",
onClick: ScriptField,
-});
-
-export const positionSchema = createSchema({
- nativeWidth: "number",
- nativeHeight: "number",
+ ignoreAspect: "boolean",
+ autoHeight: "boolean",
+ isTemplate: "boolean",
+ isButton: "boolean",
+ isBackground: "boolean",
+ ignoreClick: "boolean",
+ type: "string",
+ maximizeLocation: "string",
+ lockedPosition: "boolean",
+ excludeFromLibrary: "boolean",
width: "number",
height: "number",
- x: "number",
- y: "number",
- z: "number",
+ borderRounding: "string",
+ fitToBox: "boolean",
+ searchFields: "string",
+ heading: "number",
+ showCaption: "string",
+ showTitle: "string"
});
-export type PositionDocument = makeInterface<[typeof positionSchema]>;
-export const PositionDocument = makeInterface(positionSchema);
-
-type Document = makeInterface<[typeof schema]>;
-const Document = makeInterface(schema);
+type Document = makeInterface<[typeof documentSchema]>;
+const Document = makeInterface(documentSchema);
@observer
export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
@@ -138,17 +144,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _downY: number = 0;
private _lastTap: number = 0;
private _doubleTap = false;
- private _hitExpander = false;
private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
- _animateToIconDisposer?: IReactionDisposer;
- _reactionDisposer?: IReactionDisposer;
public get ContentDiv() { return this._mainCont.current; }
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@computed get topMost(): boolean { return this.props.renderDepth === 0; }
- screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
@action
componentDidMount() {
@@ -157,48 +159,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
handlers: { drop: this.drop.bind(this) }
});
}
- // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes
- this._reactionDisposer = reaction(() => [DocListCast(this.props.Document.maximizedDocs).map(md => md.title),
- this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""],
- () => {
- let maxDoc = DocListCast(this.props.Document.maximizedDocs);
- if (maxDoc.length === 1 && StrCast(this.props.Document.title).startsWith("-") && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) {
- this.props.Document.proto!.title = "-" + maxDoc[0].title + ".icon";
- }
- let sumDoc = Cast(this.props.Document.summaryDoc, Doc);
- if (sumDoc instanceof Doc && StrCast(this.props.Document.title).startsWith("-")) {
- this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded";
- }
- }, { fireImmediately: true });
- this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) =>
- (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false)
- , { fireImmediately: true });
DocumentManager.Instance.DocumentViews.push(this);
}
- animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => {
- this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) :
- DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing);
- }
-
- public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => {
- setTimeout(() => {
- let now = Date.now();
- let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1;
- doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress;
- doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress;
- cb && cb(progress);
- if (now < stime + 200) {
- DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb);
- }
- else {
- doc.isMinimized = !maximizing;
- doc.isIconAnimating = undefined;
- }
- doc.willMaximize = false;
- },
- 2);
- }
@action
componentDidUpdate() {
this._dropDisposer && this._dropDisposer();
@@ -210,31 +173,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- this._animateToIconDisposer && this._animateToIconDisposer();
this._dropDisposer && this._dropDisposer();
DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
}
- stopPropagation = (e: React.SyntheticEvent) => {
- e.stopPropagation();
- }
-
get dataDoc() {
- if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
- // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
- // has a template layout document, then we will render the template layout but use
- // this document as the data document for the layout.
- return this.props.Document;
- }
+ // bcz: don't think we need this, but left it in in case strange behavior pops up. DocumentContentsView has this functionality
+ // if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
+ // // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
+ // // has a template layout document, then we will render the template layout but use
+ // // this document as the data document for the layout.
+ // return this.props.Document;
+ // }
return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
}
- startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) {
+ startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
if (this._mainCont.current) {
- let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
- let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
- let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected);
+ let dragData = new DragManager.DocumentDragData([this.props.Document]);
const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
dragData.xOffset = xoff;
@@ -249,47 +205,54 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
}
- toggleMinimized = async () => {
- let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc);
- if (minimizedDoc) {
- let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(
- NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y));
- this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs));
- }
- }
- static _undoBatch?: UndoManager.Batch = undefined;
@action
public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => {
SelectionManager.DeselectAll();
if (expandedDocs) {
- if (!DocumentView._undoBatch) {
- DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
- }
let isMinimized: boolean | undefined;
expandedDocs.map(maximizedDoc => {
- let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
- if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
- if (isMinimized === undefined) {
- isMinimized = BoolCast(maximizedDoc.isMinimized);
+ if (isMinimized === undefined) {
+ isMinimized = BoolCast(maximizedDoc.isMinimized);
+ }
+ let w = NumCast(maximizedDoc.width);
+ let h = NumCast(maximizedDoc.height);
+ let iconAnimating = maximizedDoc.isIconAnimating ? Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!) : undefined;
+ if (isMinimized || (iconAnimating && iconAnimating.length && iconAnimating[0] === 0)) {
+ // MAXIMIZE DOC
+ if (maximizedDoc.isMinimized) {
+ maximizedDoc.isIconAnimating = new List<number>([0, 0]);
+ maximizedDoc.isMinimized = false;
}
- maximizedDoc.willMaximize = isMinimized;
- maximizedDoc.isMinimized = false;
- maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]);
+ maximizedDoc.iconTarget = new List<number>([...scrpt, 1]);
+ setTimeout(() => {
+ maximizedDoc.isIconAnimating = new List<number>([w, h]);
+ setTimeout(() => {
+ if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] !== 0) {
+ maximizedDoc.isIconAnimating = undefined;
+ }
+ }, 750);
+ }, 0);
+ } else {
+ maximizedDoc.iconTarget = new List<number>([...scrpt, 0]);
+ // MINIMIZE DOC
+ maximizedDoc.isIconAnimating = new List<number>([0, 0]);
+ setTimeout(() => {
+ if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] === 0) {
+ maximizedDoc.isMinimized = true;
+ maximizedDoc.isIconAnimating = undefined;
+ }
+ }, 750);
}
});
- setTimeout(() => {
- DocumentView._undoBatch && DocumentView._undoBatch.end();
- DocumentView._undoBatch = undefined;
- }, 500);
}
}
onClick = async (e: React.MouseEvent) => {
- if (e.nativeEvent.cancelBubble || SelectionManager.IsSelected(this)) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing.
+ if (e.nativeEvent.cancelBubble) return; // || SelectionManager.IsSelected(this)) -- bcz: needed because EditableView may stopPropagation which won't apparently stop this event from firing.
if (this.onClickHandler && this.onClickHandler.script) {
e.stopPropagation();
- this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
+ this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document });
e.preventDefault();
return;
}
@@ -298,41 +261,34 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (this._doubleTap && this.props.renderDepth) {
e.stopPropagation();
let fullScreenAlias = Doc.MakeAlias(this.props.Document);
- fullScreenAlias.templates = new List<string>();
Doc.UseDetailLayout(fullScreenAlias);
- fullScreenAlias.showCaption = true;
+ fullScreenAlias.showCaption = "caption";
this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
- else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
+ else if (!this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
- if (BoolCast(this.props.Document.ignoreClick)) {
- return;
- }
e.stopPropagation();
SelectionManager.SelectDoc(this, e.ctrlKey);
- let isExpander = (e.target as any).id === "isExpander";
- if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) {
- let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs);
+ if (this.Document.isButton || this.Document.type === DocumentType.BUTTON) {
let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs);
let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs);
let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document);
let expandedDocs: Doc[] = [];
- expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs;
expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
- // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
- if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents
+ // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
+ if (expandedDocs.length) {
SelectionManager.DeselectAll();
- let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
+ let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
if (altKey || ctrlKey) {
- maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
+ maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
if (!maxLocation || maxLocation === "inPlace") {
let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
- let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized), false);
+ let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false);
expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false);
let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView);
if (!hasView) {
@@ -394,7 +350,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.nativeEvent.cancelBubble) return;
this._downX = e.clientX;
this._downY = e.clientY;
- this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
this._hitTemplateDrag = false;
for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
@@ -412,11 +367,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.removeEventListener("pointermove", this.onPointerMove);
}
else if (!e.cancelBubble && this.active) {
- if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
- if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
+ if (!this.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) {
+ if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.Document.lockedPosition)) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag);
+ this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -434,38 +389,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); }
@undoBatch
- fieldsClicked = (): void => {
- let kvp = Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 });
- this.props.addDocTab(kvp, this.dataDoc, "onRight");
- }
-
- @undoBatch
makeNativeViewClicked = (): void => {
makeNativeView(this.props.Document);
}
@undoBatch
makeCustomViewClicked = (): void => {
- this.props.Document.nativeLayout = this.props.Document.layout;
- this.props.Document.nativeType = this.props.Document.type;
- this.props.Document.nonCustomAutoHeight = this.props.Document.autoHeight;
- this.props.Document.nonCustomWidth = this.props.Document.nativeWidth;
- this.props.Document.nonCustomHeight = this.props.Document.nativeHeight;
- this.props.Document.nonCustomNativeWidth = this.props.Document.nativeWidth;
- this.props.Document.nonCustomNativeHeight = this.props.Document.nativeHeight;
- this.props.Document.nonCustomIgnoreAspect = this.props.Document.ignoreAspect;
+ this.props.Document.nativeLayout = this.Document.layout;
+ this.props.Document.nativeType = this.Document.type;
+ this.props.Document.nonCustomAutoHeight = this.Document.autoHeight;
+ this.props.Document.nonCustomWidth = this.Document.width;
+ this.props.Document.nonCustomHeight = this.Document.height;
+ this.props.Document.nonCustomNativeWidth = this.Document.nativeWidth;
+ this.props.Document.nonCustomNativeHeight = this.Document.nativeHeight;
+ this.props.Document.nonCustomIgnoreAspect = this.Document.ignoreAspect;
PromiseValue(Cast(this.props.Document.customLayout, Doc)).then(custom => {
if (custom) {
- this.props.Document.type = DocumentType.TEMPLATE;
+ this.Document.type = DocumentType.TEMPLATE;
this.props.Document.layout = custom;
- !custom.nativeWidth && (this.props.Document.nativeWidth = 0);
- !custom.nativeHeight && (this.props.Document.nativeHeight = 0);
- !custom.nativeWidth && (this.props.Document.ignoreAspect = true);
- this.props.Document.autoHeight = this.props.Document.autoHeight;
- this.props.Document.width = this.props.Document.customWidth;
- this.props.Document.height = this.props.Document.customHeight;
- this.props.Document.nativeWidth = this.props.Document.customNativeWidth;
- this.props.Document.nativeHeight = this.props.Document.customNativeHeight;
- this.props.Document.ignoreAspect = this.props.Document.ignoreAspect;
+ !custom.nativeWidth && (this.Document.nativeWidth = 0);
+ !custom.nativeHeight && (this.Document.nativeHeight = 0);
+ !custom.nativeWidth && (this.Document.ignoreAspect = true);
+ this.Document.autoHeight = BoolCast(this.Document.customAutoHeight);
+ this.Document.width = NumCast(this.props.Document.customWidth);
+ this.Document.height = NumCast(this.props.Document.customHeight);
+ this.Document.nativeWidth = NumCast(this.props.Document.customNativeWidth);
+ this.Document.nativeHeight = NumCast(this.props.Document.customNativeHeight);
+ this.Document.ignoreAspect = BoolCast(this.Document.customIgnoreAspect);
this.props.Document.customAutoHeight = undefined;
this.props.Document.customWidth = undefined;
this.props.Document.customHeight = undefined;
@@ -473,21 +422,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.Document.customNativeHeight = undefined;
this.props.Document.customIgnoreAspect = undefined;
} else {
- let options = { title: "data", width: NumCast(this.props.Document.width), x: -NumCast(this.props.Document.width) / 2, y: -NumCast(this.props.Document.height) / 2, };
- let fieldTemplate = this.props.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
- this.props.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
+ let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, };
+ let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) :
+ this.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) :
Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
- fieldTemplate.backgroundColor = StrCast(this.props.Document.backgroundColor);
+ fieldTemplate.backgroundColor = this.Document.backgroundColor;
fieldTemplate.heading = 1;
fieldTemplate.autoHeight = true;
- let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: StrCast(this.Document.title) + "layout", width: NumCast(this.props.Document.width) + 20, height: Math.max(100, NumCast(this.props.Document.height) + 45) });
+ let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: this.Document.title + "layout", width: (this.Document.width || 0) + 20, height: Math.max(100, (this.Document.height || 0) + 45) });
let proto = Doc.GetProto(docTemplate);
Doc.MakeMetadataFieldTemplate(fieldTemplate, proto, true);
Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false);
- Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.props.Document.layout;
+ Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.Document.layout;
}
});
}
@@ -501,21 +450,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
} else {
doc.isButton = true;
}
-
- // if (doc.isButton) {
- // if (!doc.nativeWidth) {
- // doc.nativeWidth = this.props.Document[WidthSym]();
- // doc.nativeHeight = this.props.Document[HeightSym]();
- // }
- // } else {
- // doc.nativeWidth = doc.nativeHeight = undefined;
- // }
- }
-
- @undoBatch
- public fullScreenClicked = (): void => {
- CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this);
- SelectionManager.DeselectAll();
}
@undoBatch
@@ -576,10 +510,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
freezeNativeDimensions = (): void => {
- let proto = this.props.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
- this.props.Document.autoHeight = proto.autoHeight = false;
- proto.ignoreAspect = !BoolCast(proto.ignoreAspect);
- if (!BoolCast(proto.ignoreAspect) && !proto.nativeWidth) {
+ let proto = this.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document);
+ proto.autoHeight = this.Document.autoHeight = false;
+ proto.ignoreAspect = !proto.ignoreAspect;
+ if (!proto.ignoreAspect && !proto.nativeWidth) {
proto.nativeWidth = this.props.PanelWidth();
proto.nativeHeight = this.props.PanelHeight();
}
@@ -588,12 +522,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
makeIntoPortal = (): void => {
if (!DocListCast(this.props.Document.links).find(doc => {
- if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
+ if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.Document.title + ".portal") return true;
return false;
})) {
- let portalID = (this.props.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+ let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, "");
DocServer.GetRefField(portalID).then(existingPortal => {
- let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: portalID });
+ let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID });
DocUtils.MakeLink(this.props.Document, portal, undefined, portalID);
Doc.GetProto(this.props.Document).isButton = true;
})
@@ -651,15 +585,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const cm = ContextMenu.Instance;
let subitems: ContextMenuProps[] = [];
- subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" });
- subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
- subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
- subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
- subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
+ subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" });
+ subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" });
+ subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" });
+ subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
-
let existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
@@ -676,7 +609,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}, icon: "window-restore"
});
onClicks.push({ description: this.layoutDoc.ignoreClick ? "Select" : "Do Nothing", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.props.Document.isButton || this.props.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
onClicks.push({
description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => {
@@ -688,20 +621,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let existing = ContextMenu.Instance.findByDescription("Layout...");
let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
- layoutItems.push({ description: this.props.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
+ layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
if (this.props.DataDoc) {
layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" })
}
layoutItems.push({ description: `${this.layoutDoc.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.layoutDoc.chromeStatus = (this.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
layoutItems.push({ description: `${this.layoutDoc.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" });
- layoutItems.push({ description: this.props.Document.ignoreAspect || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+ layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
layoutItems.push({ description: this.layoutDoc.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.layoutDoc.lockedPosition) ? "unlock" : "lock" });
layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
- if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
+ if (this.props.Document.detailedLayout && !this.Document.isTemplate) {
layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
}
- if (this.props.Document.type !== DocumentType.COL && this.props.Document.type !== DocumentType.TEMPLATE) {
+ if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) {
layoutItems.push({ description: "Use Custom Layout", event: this.makeCustomViewClicked, icon: "concierge-bell" });
} else if (this.props.Document.nativeLayout) {
layoutItems.push({ description: "Use Native Layout", event: this.makeNativeViewClicked, icon: "concierge-bell" });
@@ -733,7 +666,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
});
- cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, StrCast(this.props.Document.title), this.props.addDocument, this.props.removeDocument), icon: "file" });
+ cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
type User = { email: string, userDocumentId: string };
let usersMenu: ContextMenuProps[] = [];
@@ -813,7 +746,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
select={this.select}
onClick={this.onClickHandler}
layoutKey={"layout"}
- fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox}
+ fitToBox={this.Document.fitToBox ? true : this.props.fitToBox}
DataDoc={this.dataDoc} />);
}
@@ -829,55 +762,68 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return (showTitle ? 25 : 0) + 1;// bcz: why 8??
}
- get layoutDoc() {
+ get layoutDoc(): Document {
// if this document's layout field contains a document (ie, a rendering template), then we will use that
// to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
- return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
+ return Document(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document);
}
-
render() {
- let ruleProvider = this.props.ruleProvider;
- let ruleColor = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleColor_" + NumCast(this.props.Document.heading)]) : undefined;
- let ruleRounding = ruleProvider ? StrCast(Doc.GetProto(ruleProvider)["ruleRounding_" + NumCast(this.props.Document.heading)]) : undefined;
- let colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
- let clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
-
- let backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
+ const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
+ const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
+ const colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor;
+ const clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground;
+ const backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ?
this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) :
ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc);
- let foregroundColor = StrCast(this.layoutDoc.color);
- var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
- var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
- let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
- let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
- let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
- let templates = Cast(this.layoutDoc.templates, listSpec("string"));
- if (!showOverlays && templates instanceof List) {
- templates.map(str => {
- if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
- if (!showCaption && str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption";
- });
- }
- let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
- let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
- let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding, ruleRounding);
- let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
- let searchHighlight = (!this.props.Document.search_fields ? (null) :
- <div key="search" style={{ position: "absolute", background: "yellow", bottom: "-20px", borderRadius: "5px", transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
- {StrCast(this.props.Document.search_fields)}
+
+ const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
+ const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+ const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
+ const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.layoutDoc.showTitle;
+ const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.layoutDoc.showCaption;
+ const showTextTitle = showTitle && StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined;
+ const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+ const borderRounding = this.Document.borderRounding || ruleRounding;
+ const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
+ const iconAnimating = this.Document.isIconAnimating ? Array.from(Cast(this.Document.isIconAnimating, listSpec("number"))!) : undefined;
+ const searchHighlight = (!this.Document.searchFields ? (null) :
+ <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+ {this.Document.searchFields}
+ </div>);
+ const captionView = (!showCaption ? (null) :
+ <div className="documentView-captionWrapper" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+ <FormattedTextBox {...this.props}
+ onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue}
+ isSelected={this.isSelected} focus={emptyFunction} select={this.select}
+ fieldExt={""} hideOnLeave={true} fieldKey={showCaption}
+ />
+ </div>);
+ const titleView = (!showTitle ? (null) :
+ <div className="documentView-titleWrapper" style={{
+ position: showTextTitle ? "relative" : "absolute",
+ pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
+ width: `${100 * this.props.ContentScaling()}%`,
+ transform: `scale(${1 / this.props.ContentScaling()})`
+ }}>
+ <EditableView
+ contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]}
+ display={"block"} height={72} fontSize={12}
+ GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle])}
+ SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle] = value) ? true : true}
+ />
</div>);
return (
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
+ transition: iconAnimating ? "transform .5s" : StrCast(this.layoutDoc.transition),
pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
- color: foregroundColor,
+ color: StrCast(this.layoutDoc.color),
outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined,
- borderRadius: "inherit",
background: backgroundColor,
width: nativeWidth,
height: nativeHeight,
@@ -888,37 +834,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
>
{!showTitle && !showCaption ?
- this.props.Document.search_fields ? <div>
- {this.contents}
- {searchHighlight}
- </div> :
- this.contents :
- <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
- <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}>
+ this.Document.searchFields ?
+ (<div className="documentView-searchWrapper">
+ {this.contents}
+ {searchHighlight}
+ </div>)
+ :
+ this.contents
+ :
+ <div className="documentView-styleWrapper" >
+ <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 29px)" : "100%", top: showTextTitle ? "29px" : undefined }}>
{this.contents}
</div>
- {!showTitle ? (null) :
- <div style={{
- position: showTextTitle ? "relative" : "absolute", top: 0, padding: "4px", textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
- pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all",
- overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
- transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
- }}>
- <EditableView
- contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]}
- display={"block"}
- height={72}
- fontSize={12}
- GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])}
- SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true}
- />
- </div>
- }
- {!showCaption ? (null) :
- <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
- <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
- </div>
- }
+ {titleView}
+ {captionView}
{searchHighlight}
</div>
}
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 1f2c88086..067d47de4 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -55,7 +55,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
let doc = res !== undefined && res.success ?
res.result as Doc :
Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
- doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY);
+ doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
}
e.stopPropagation();
e.preventDefault();
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
index 7e78ec684..92cb5a9c9 100644
--- a/src/client/views/nodes/IconBox.tsx
+++ b/src/client/views/nodes/IconBox.tsx
@@ -12,6 +12,8 @@ import { IconField } from "../../../new_fields/IconField";
import { ContextMenu } from "../ContextMenu";
import Measure from "react-measure";
import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
+import { Scripting, CompileScript } from "../../util/Scripting";
+import { ComputedField } from "../../../new_fields/ScriptField";
library.add(faCaretUp);
@@ -27,6 +29,26 @@ export class IconBox extends React.Component<FieldViewProps> {
@computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
@computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
+ public static summaryTitleScript(inputDoc: Doc) {
+ const sumDoc = Cast(inputDoc.summaryDoc, Doc) as Doc;
+ if (sumDoc && StrCast(sumDoc.title).startsWith("-")) {
+ return sumDoc.title + ".expanded";
+ }
+ return "???";
+ }
+ public static titleScript(inputDoc: Doc) {
+ const maxDoc = DocListCast(inputDoc.maximizedDocs);
+ if (maxDoc.length === 1) {
+ return maxDoc[0].title + ".icon";
+ }
+ return maxDoc.length > 1 ? "-multiple-.icon" : "???";
+ }
+
+ public static AutomaticTitle(doc: Doc) {
+ let computed = CompileScript(`return iconTitle(this);`, { params: { this: "Doc" }, typecheck: false });
+ computed.compiled && (Doc.GetProto(doc).title = new ComputedField(computed));
+ }
+
public static DocumentIcon(layout: string) {
let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
layout.indexOf("ImageBox") !== -1 ? faImage :
@@ -38,35 +60,20 @@ export class IconBox extends React.Component<FieldViewProps> {
}
setLabelField = (): void => {
- this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel);
- }
- setUseOwnTitleField = (): void => {
- this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle);
+ this.props.Document.hideLabel = !this.props.Document.hideLabel;
}
specificContextMenu = (): void => {
- ContextMenu.Instance.addItem({
- description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon",
- event: this.setLabelField,
- icon: "tag"
- });
- let maxDocs = DocListCast(this.props.Document.maximizedDocs);
- if (maxDocs.length === 1 && !BoolCast(this.props.Document.hideLabel)) {
- ContextMenu.Instance.addItem({
- description: BoolCast(this.props.Document.useOwnTitle) ? "Use target title for label" : "Use own title label",
- event: this.setUseOwnTitleField,
- icon: "text-height"
- });
+ let cm = ContextMenu.Instance;
+ cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
+ if (!this.props.Document.hideLabel) {
+ cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
}
}
@observable _panelWidth: number = 0;
@observable _panelHeight: number = 0;
render() {
- let labelField = StrCast(this.props.Document.labelField);
- let hideLabel = BoolCast(this.props.Document.hideLabel);
- let maxDocs = DocListCast(this.props.Document.maximizedDocs);
- let firstDoc = maxDocs.length ? maxDocs[0] : undefined;
- let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle) ? firstDoc[labelField] : this.props.Document.title);
+ let label = this.props.Document.hideLabel ? "" : this.props.Document.title;
return (
<div className="iconBox-container" onContextMenu={this.specificContextMenu}>
{this.minimizedIcon}
@@ -82,4 +89,6 @@ export class IconBox extends React.Component<FieldViewProps> {
</Measure>
</div>);
}
-} \ No newline at end of file
+}
+Scripting.addGlobal(function iconTitle(doc: any) { return IconBox.titleScript(doc); });
+Scripting.addGlobal(function summaryTitle(doc: any) { return IconBox.summaryTitleScript(doc); }); \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 95f304641..beccce9dd 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -376,7 +376,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
// let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight());
// let w = bptX - sptX;
- let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx
let nativeWidth = FieldValue(this.Document.nativeWidth, pw);
let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
@@ -402,11 +401,11 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
return (
- <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
+ <div className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
onPointerDown={this.onPointerDown}
onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
<div id="cf">
- <img id={id}
+ <img
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 856e883e7..533247170 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -137,7 +137,7 @@ export default class Page extends React.Component<IPageProps> {
view.nativeWidth = this.props.Document.nativeWidth;
view.startY = marquee.top + this.props.getScrollFromPage(this.props.page);
view.width = this.props.Document[WidthSym]();
- DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view], [undefined]), 0, 0);
+ DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0);
}
@action
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index cd3dd912c..510672788 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -270,7 +270,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
e.stopPropagation();
const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
- DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, {
+ DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
handlers: { dragComplete: emptyFunction },
hideSource: false,
});
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 302bfc745..e3b5f78a7 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -143,8 +143,14 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [WidthSym] = () => NumCast(this[SelfProxy].width); // bcz: is this the right way to access width/height? it didn't work with : this.width
- public [HeightSym] = () => NumCast(this[SelfProxy].height);
+ public [WidthSym] = () => {
+ let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
+ return iconAnimating ? iconAnimating[0] : NumCast(this[SelfProxy].width);
+ }
+ public [HeightSym] = () => {
+ let iconAnimating = this[SelfProxy].isIconAnimating ? Array.from(Cast(this[SelfProxy].isIconAnimating, listSpec("number"))!) : undefined;
+ return iconAnimating ? iconAnimating[1] : NumCast(this[SelfProxy].height);
+ }
[ToScriptString]() {
return "invalid";
@@ -412,6 +418,9 @@ export namespace Doc {
}
export function MakeAlias(doc: Doc) {
let alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+ if (alias.layout instanceof Doc) {
+ alias.layout = Doc.MakeAlias(alias.layout as Doc);
+ }
let aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
let script = `return renameAlias(self, ${aliasNumber})`;
//let script = "StrCast(self.title).replace(/\\([0-9]*\\)/, \"\") + `(${n})`";