aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2020-02-22 12:11:17 -0500
committerSam Wilkins <samwilkins333@gmail.com>2020-02-22 12:11:17 -0500
commitf0dde351fcb6f8ba1d0e329375e0d86c20162603 (patch)
tree6238496b1539bdc8453e031d90578abe93fd1835 /src/client/views/collections
parentf15269bf5b1006bc8169892940dcf027ff6c023d (diff)
parent14a357133b223ecb9a156e6b8b6dfbbe72f14dc5 (diff)
updated from master
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss1
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx20
-rw-r--r--src/client/views/collections/CollectionDockingView.scss45
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx135
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx14
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx11
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx5
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx4
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx7
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx12
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx27
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx16
-rw-r--r--src/client/views/collections/CollectionSubView.tsx306
-rw-r--r--src/client/views/collections/CollectionTreeView.scss9
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx117
-rw-r--r--src/client/views/collections/CollectionView.tsx50
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss3
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx9
-rw-r--r--src/client/views/collections/ParentDocumentSelector.scss9
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx27
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx48
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx137
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx30
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx18
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx7
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx29
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx29
29 files changed, 564 insertions, 572 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index ad369bbff..fd1296286 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,6 +1,7 @@
.collectionCarouselView-outer {
background: gray;
+ height : 100%;
.collectionCarouselView-caption {
margin-left: 10%;
margin-right: 10%;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index f462e20b4..a0cb1fe19 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -12,7 +12,6 @@ import { CollectionSubView } from './CollectionSubView';
import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
import { Doc } from '../../../new_fields/Doc';
import { FormattedTextBox } from '../nodes/FormattedTextBox';
-import { ContextMenuProps } from '../ContextMenuItem';
import { ContextMenu } from '../ContextMenu';
import { ObjectField } from '../../../new_fields/ObjectField';
@@ -21,19 +20,14 @@ const CarouselDocument = makeInterface(documentSchema);
@observer
export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
- @observable public addMenuToggle = React.createRef<HTMLInputElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
- componentWillUnmount() {
- this._dropDisposer && this._dropDisposer();
- }
+ componentWillUnmount() { this._dropDisposer?.(); }
- componentDidMount() {
- }
protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
- this._dropDisposer && this._dropDisposer();
+ this._dropDisposer?.();
if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
}
}
@@ -50,18 +44,18 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
@computed get content() {
const index = NumCast(this.layoutDoc._itemIndex);
return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) :
- <div>
- <div className="collectionCarouselView-image">
+ <>
+ <div className="collectionCarouselView-image" key="image">
<ContentFittingDocumentView {...this.props}
Document={this.childLayoutPairs[index].layout}
DataDocument={this.childLayoutPairs[index].data}
PanelHeight={this.panelHeight}
getTransform={this.props.ScreenToLocalTransform} />
</div>
- <div className="collectionCarouselView-caption" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}>
+ <div className="collectionCarouselView-caption" key="caption" style={{ background: this.props.backgroundColor?.(this.props.Document) }}>
<FormattedTextBox key={index} {...this.props} Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"}></FormattedTextBox>
</div>
- </div>
+ </>;
}
@computed get buttons() {
return <>
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index f518ef8fb..2fafcecb2 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,8 +1,34 @@
@import "../../views/globalCssVariables.scss";
-.lm_active .messageCounter {
- color: white;
- background: #999999;
+.lm_title {
+ margin-top: 3px;
+ background: black;
+ border-radius: 5px;
+ border: solid 1px dimgray;
+ border-width: 2px 2px 0px;
+ height: 20px;
+ transform: translate(0px, -3px);
+}
+.lm_title_wrap {
+ overflow: hidden;
+ height: 19px;
+ margin-top: -3px;
+ display:inline-block;
+}
+.lm_active .lm_title {
+ border: solid 1px lightgray;
+}
+.lm_header .lm_tab .lm_close_tab {
+ position: absolute;
+ text-align: center;
+}
+
+.lm_header .lm_tab {
+ padding-right : 20px;
+}
+
+.lm_popout {
+ display:none;
}
.messageCounter {
@@ -26,9 +52,20 @@
top: 0;
left: 0;
// overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu)
-
+ .collectionDockingView-gear {
+ padding-left: 5px;
+ height: 15px;
+ width: 18px;
+ display: inline-block;
+ margin: auto;
+ }
.collectionDockingView-dragAsDocument {
touch-action: none;
+ position: absolute;
+ padding-left: 5px;
+ display: inline-block;
+ width: 100%;
+ height: 100%;
}
.lm_content {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cb413b3e3..83dbb4263 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,26 +1,27 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faFile } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, Lambda, observable, reaction, computed, runInAction, trace } from "mobx";
+import { action, computed, Lambda, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import Measure from "react-measure";
import * as GoldenLayout from "../../../client/goldenLayout";
import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
+import { Doc, DocListCast, Field, Opt, DataSym } from "../../../new_fields/Doc";
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { FieldId } from "../../../new_fields/RefField";
-import { listSpec } from "../../../new_fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { TraceMobx } from '../../../new_fields/util';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from "../../../Utils";
+import { emptyFunction, returnOne, returnTrue, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from "../../util/DragManager";
+import { Scripting } from '../../util/Scripting';
import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from "../../util/UndoManager";
@@ -28,14 +29,8 @@ import { MainView } from '../MainView';
import { DocumentView } from "../nodes/DocumentView";
import "./CollectionDockingView.scss";
import { SubCollectionViewProps } from "./CollectionSubView";
+import { DockingViewButtonSelector } from './ParentDocumentSelector';
import React = require("react");
-import { ButtonSelector } from './ParentDocumentSelector';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { ComputedField } from '../../../new_fields/ScriptField';
-import { InteractionUtils } from '../../util/InteractionUtils';
-import { TraceMobx } from '../../../new_fields/util';
-import { Scripting } from '../../util/Scripting';
-import { PresElementBox } from '../presentationview/PresElementBox';
library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
@@ -43,7 +38,7 @@ const _global = (window /* browser */ || global /* node */) as any;
export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
@observable public static Instances: CollectionDockingView[] = [];
@computed public static get Instance() { return CollectionDockingView.Instances[0]; }
- public static makeDocumentConfig(document: Doc, dataDoc: Doc | undefined, width?: number, libraryPath?: Doc[]) {
+ public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
@@ -51,8 +46,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
width: width,
props: {
documentId: document[Id],
- dataDocumentId: dataDoc && dataDoc[Id] !== document[Id] ? dataDoc[Id] : "",
- libraryPath: libraryPath ? libraryPath.map(d => d[Id]) : []
+ libraryPath: libraryPath?.map(d => d[Id])
//collectionDockingView: CollectionDockingView.Instance
}
};
@@ -81,12 +75,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
public StartOtherDrag(e: any, dragDocs: Doc[]) {
let config: any;
if (dragDocs.length === 1) {
- config = CollectionDockingView.makeDocumentConfig(dragDocs[0], undefined);
+ config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
} else {
config = {
type: 'row',
content: dragDocs.map((doc, i) => {
- CollectionDockingView.makeDocumentConfig(doc, undefined);
+ CollectionDockingView.makeDocumentConfig(doc);
})
};
}
@@ -102,10 +96,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
const document = Doc.MakeAlias(docView.props.Document);
- const dataDoc = docView.props.DataDoc;
const newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+ content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
};
const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
this._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -177,7 +170,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
@undoBatch
@action
- public static ReplaceRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[], addToSplit?: boolean): boolean {
+ public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
if (!CollectionDockingView.Instance) return false;
const instance = CollectionDockingView.Instance;
let retVal = false;
@@ -185,7 +178,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
- const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath);
+ const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
child.addChild(newItemStackConfig, undefined);
!addToSplit && child.contentItems[0].remove();
instance.layoutChanged(document);
@@ -193,7 +186,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
- const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath);
+ const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
child.addChild(newItemStackConfig, undefined);
!addToSplit && child.contentItems[j].remove();
instance.layoutChanged(document);
@@ -215,12 +208,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
//
@undoBatch
@action
- public static AddRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
+ public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
if (!CollectionDockingView.Instance) return false;
const instance = CollectionDockingView.Instance;
const newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+ content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
};
const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -250,18 +243,18 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
//
@undoBatch
@action
- public static UseRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[], shiftKey?: boolean) {
+ public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
document.isDisplayPanel = true;
- if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, dataDoc, libraryPath, shiftKey)) {
- CollectionDockingView.AddRightSplit(document, dataDoc, libraryPath);
+ if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
+ CollectionDockingView.AddRightSplit(document, libraryPath);
}
}
@undoBatch
@action
- public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined, libraryPath?: Doc[]) => {
+ public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
Doc.GetProto(document).lastOpened = new DateField;
- const docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument, undefined, libraryPath);
+ const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
if (stack === undefined) {
let stack: any = this._goldenLayout.root;
while (!stack.isStack) {
@@ -384,16 +377,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
});
window.addEventListener("pointerup", onPointerUp);
const className = (e.target as any).className;
- if (className === "messageCounter") {
- e.stopPropagation();
- e.preventDefault();
- const x = e.clientX;
- const y = e.clientY;
- const docid = (e.target as any).DashDocId;
- const tab = (e.target as any).parentElement as HTMLElement;
- DocServer.GetRefField(docid).then(action(async (sourceDoc: Opt<Field>) =>
- (sourceDoc instanceof Doc) && DragManager.StartLinkTargetsDrag(tab, x, y, sourceDoc)));
- }
if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
this._flush = true;
}
@@ -435,24 +418,27 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
tabCreated = async (tab: any) => {
+ tab.titleElement[0].Tab = tab;
if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
if (tab.contentItem.config.fixed) {
tab.contentItem.parent.config.fixed = true;
}
const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
- const dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
if (doc instanceof Doc) {
- const dragSpan = document.createElement("span");
- dragSpan.style.position = "relative";
- dragSpan.style.bottom = "6px";
- dragSpan.style.paddingLeft = "4px";
- dragSpan.style.paddingRight = "2px";
+ //tab.titleElement[0].outerHTML = `<input class='lm_title' style="background:black" value='${doc.title}' />`;
+ tab.titleElement[0].onclick = (e: any) => tab.titleElement[0].focus();
+ tab.titleElement[0].onchange = (e: any) => {
+ tab.titleElement[0].size = e.currentTarget.value.length + 1;
+ Doc.GetProto(doc).title = e.currentTarget.value, true;
+ };
+ tab.titleElement[0].size = StrCast(doc.title).length + 1;
+ tab.titleElement[0].value = doc.title;
const gearSpan = document.createElement("span");
+ gearSpan.className = "collectionDockingView-gear";
gearSpan.style.position = "relative";
gearSpan.style.paddingLeft = "0px";
gearSpan.style.paddingRight = "12px";
- const upDiv = document.createElement("span");
const stack = tab.contentItem.parent;
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: any) => {
@@ -470,24 +456,19 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
e.stopPropagation();
const dragData = new DragManager.DocumentDragData([doc]);
dragData.dropAction = doc.dropAction === "alias" ? "alias" : doc.dropAction === "copy" ? "copy" : undefined;
- DragManager.StartDocumentDrag([dragSpan], dragData, e.clientX, e.clientY);
- }}>
- <FontAwesomeIcon icon="file" size="lg" />
- </span>, dragSpan);
- ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
- tab.reactComponents = [dragSpan, gearSpan, upDiv];
- tab.element.append(dragSpan);
+ DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
+ }}><DockingViewButtonSelector Document={doc} Stack={stack} /></span>, gearSpan);
+ tab.reactComponents = [gearSpan];
tab.element.append(gearSpan);
- tab.element.append(upDiv);
- tab.reactionDisposer = reaction(() => [doc.title, Doc.IsBrushedDegree(doc)], () => {
- tab.titleElement[0].textContent = doc.title, { fireImmediately: true };
- tab.titleElement[0].style.outline = `${["transparent", "white", "white"][Doc.IsBrushedDegreeUnmemoized(doc)]} ${["none", "dashed", "solid"][Doc.IsBrushedDegreeUnmemoized(doc)]} 1px`;
+ tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
+ tab.titleElement[0].textContent = title, { fireImmediately: true };
+ tab.titleElement[0].style.padding = degree ? 0 : 2;
+ tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
});
//TODO why can't this just be doc instead of the id?
tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
}
}
- tab.titleElement[0].Tab = tab;
tab.closeElement.off('click') //unbind the current click handler
.click(async function () {
tab.reactionDisposer && tab.reactionDisposer();
@@ -523,7 +504,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
stack.header.element[0].style.backgroundColor = DocServer.Control.isReadOnly() ? "#228540" : undefined;
stack.header.element.on('mousedown', (e: any) => {
if (e.target === stack.header.element[0] && e.button === 1) {
- this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }), undefined);
+ this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }));
}
});
@@ -596,9 +577,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
interface DockedFrameProps {
documentId: FieldId;
- dataDocumentId: FieldId;
glContainer: any;
libraryPath: (FieldId[]);
+ backgroundColor?: (doc: Doc) => string | undefined;
//collectionDockingView: CollectionDockingView
}
@observer
@@ -608,7 +589,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
@observable private _panelWidth = 0;
@observable private _panelHeight = 0;
@observable private _document: Opt<Doc>;
- @observable private _dataDoc: Opt<Doc>;
@observable private _isActive: boolean = false;
get _stack(): any {
@@ -616,12 +596,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
constructor(props: any) {
super(props);
- DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
- this._document = f as Doc;
- if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) {
- DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
- }
- }));
+ DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
this.props.libraryPath && this.setupLibraryPath();
}
@@ -645,7 +620,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
pinDoc.presentationTargetDoc = doc;
Doc.AddDocToList(curPres, "data", pinDoc);
if (!DocumentManager.Instance.getDocumentView(curPres)) {
- CollectionDockingView.AddRightSplit(curPres, undefined);
+ CollectionDockingView.AddRightSplit(curPres);
}
}
}
@@ -693,8 +668,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth;
panelHeight = () => this._panelHeight;
- nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
- nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
+ nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
+ nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
contentScaling = () => {
if (this.layoutDoc!.type === DocumentType.PDF) {
@@ -720,19 +695,19 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
return Transform.Identity();
}
- get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
+ get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; }
- addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string, libraryPath?: Doc[]) => {
+ addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
SelectionManager.DeselectAll();
if (doc.dockingConfig) {
return MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
- return CollectionDockingView.AddRightSplit(doc, dataDoc, libraryPath);
+ return CollectionDockingView.AddRightSplit(doc, libraryPath);
} else if (location === "close") {
return CollectionDockingView.CloseRightSplit(doc);
} else {
- return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc, libraryPath);
+ return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
}
}
@@ -740,7 +715,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
TraceMobx();
if (!this._document) return (null);
const document = this._document;
- const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc;
+ const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) && this._document[DataSym];// document.layout instanceof Doc ? document : this._dataDoc;
return <DocumentView key={document[Id]}
LibraryPath={this._libraryPath}
Document={document}
@@ -756,7 +731,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
ContainingCollectionView={undefined}
@@ -777,5 +752,5 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
</div >);
}
}
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc, undefined); });
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, undefined, shiftKey); });
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); });
+Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 67062ae41..4a14a30cd 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -38,8 +38,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
componentDidMount() {
// is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported).
- this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0),
- () => this.props.Document._width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
+ this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.linearViewIsExpanded ? 1 : 0),
+ () => this.props.Document._width = 5 + (this.props.Document.linearViewIsExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
{ fireImmediately: true }
);
@@ -67,11 +67,11 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this._dropDisposer && this._dropDisposer();
if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
}
}
- public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+ public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
dimension = () => NumCast(this.props.Document._height); // 2 * the padding
getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
@@ -84,8 +84,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
const guid = Utils.GenerateGuid();
return <div className="collectionLinearView-outer">
<div className="collectionLinearView" ref={this.createDashEventsTarget} >
- <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
- onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
+ <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+ onChange={action((e: any) => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
<label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
<div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25) }}>
@@ -97,7 +97,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
style={{
width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
- height: nested && pair.layout.isExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
+ height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
}} >
<DocumentView
Document={pair.layout}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index e25a2f5eb..c9b7ca221 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Doc } from "../../../new_fields/Doc";
@@ -16,10 +16,12 @@ import { CompileScript } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
-import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
library.add(faPalette);
@@ -80,7 +82,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
const key = StrCast(this.props.parent.props.Document.sectionFilter);
const castedValue = this.getValue(this._heading);
de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
- this.props.parent.drop(e, de);
+ this.props.parent.onInternalDrop(e, de);
e.stopPropagation();
}
});
@@ -257,7 +259,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@computed get contentLayout() {
const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap))));
- const style = this.props.parent; const collapsed = this._collapsed;
+ const style = this.props.parent;
+ const collapsed = this._collapsed;
const chromeStatus = this.props.parent.props.Document._chromeStatus;
const newEditableViewProps = {
GetValue: () => "",
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index caffa7eb1..851fded71 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -35,9 +35,10 @@ export interface CellProps {
Document: Doc;
fieldKey: string;
renderDepth: number;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+ addDocTab: (document: Doc, where: string) => boolean;
pinToPres: (document: Doc) => void;
- moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined,
+ addDocument: (document: Doc) => boolean) => boolean;
isFocused: boolean;
changeFocusedCellByIndex: (row: number, col: number) => void;
setIsEditing: (isEditing: boolean) => void;
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 92dc8780e..c585506b3 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -5,11 +5,13 @@ import "./CollectionSchemaView.scss";
import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Flyout, anchorPoints } from "../DocumentDecorations";
import { ColumnType } from "./CollectionSchemaView";
import { faFile } from "@fortawesome/free-regular-svg-icons";
import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 153bbd410..670d6dbb2 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -3,9 +3,9 @@ import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
import "./CollectionSchemaView.scss";
import { Transform } from "../../util/Transform";
import { Doc } from "../../../new_fields/Doc";
-import { DragManager, SetupDrag } from "../../util/DragManager";
+import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast, FieldValue, StrCast } from "../../../new_fields/Types";
import { ContextMenu } from "../ContextMenu";
import { action } from "mobx";
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -135,6 +135,7 @@ export interface MovableRowProps {
rowFocused: boolean;
textWrapRow: (doc: Doc) => void;
rowWrapped: boolean;
+ dropAction: string;
}
export class MovableRow extends React.Component<MovableRowProps> {
@@ -219,7 +220,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (!doc) return <></>;
const reference = React.createRef<HTMLDivElement>();
- const onItemDown = SetupDrag(reference, () => doc, this.move);
+ const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
let className = "collectionSchema-row";
if (this.props.rowFocused) className += " row-focused";
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index fa8be5177..9486d195a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -14,7 +14,6 @@ import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { ComputedField } from "../../../new_fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
import { Gateway } from "../../northstar/manager/Gateway";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
@@ -175,7 +174,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
moveDocument={this.props.moveDocument}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
active={this.props.active}
- onDrop={this.onDrop}
+ onDrop={this.onExternalDrop}
addDocTab={this.props.addDocTab}
pinToPres={this.props.pinToPres}
isSelected={this.props.isSelected}
@@ -199,7 +198,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
render() {
return <div className="collectionSchemaView-container">
- <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
+ <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.onExternalDrop(e, {})} ref={this.createTarget}>
{this.schemaTable}
</div>
{this.dividerDragger}
@@ -225,7 +224,7 @@ export interface SchemaTableProps {
ScreenToLocalTransform: () => Transform;
active: (outsideReaction: boolean) => boolean;
onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
+ addDocTab: (document: Doc, where: string) => boolean;
pinToPres: (document: Doc) => void;
isSelected: (outsideReaction?: boolean) => boolean;
isFocused: (document: Doc) => boolean;
@@ -409,7 +408,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
rowInfo,
rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document),
textWrapRow: this.toggleTextWrapRow,
- rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1
+ rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
+ dropAction: StrCast(this.props.Document.childDropAction)
};
}
@@ -692,7 +692,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
// ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" });
- ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" })
+ ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
}
}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7d1f2c284..a1cc21319 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import Switch from 'rc-switch';
-import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Doc, HeightSym, WidthSym, DataSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
@@ -38,7 +38,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@observable _scroll = 0; // used to force the document decoration to update when scrolling
@computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
@computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
- @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
+ @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
@computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
@computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
@@ -62,7 +62,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
- {this.getDisplayDoc(d, Cast(d.resolvedDataDoc, Doc, null) || this.props.DataDoc, dxf, width)}
+ {this.getDisplayDoc(d, this.props.DataDoc, dxf, width)}
</div>;
});
}
@@ -158,7 +158,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
const height = () => this.getDocHeight(doc);
return <ContentFittingDocumentView
Document={doc}
- DataDocument={dataDoc}
+ DataDocument={doc[DataSym] !== doc && doc[DataSym]}
+ backgroundColor={this.props.backgroundColor}
LayoutDoc={this.props.childLayoutTemplate}
LibraryPath={this.props.LibraryPath}
renderDepth={this.props.renderDepth + 1}
@@ -184,17 +185,17 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (!d) return 0;
const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const nw = NumCast(layoutDoc._nativeWidth);
- return Math.min(nw && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
}
getDocHeight(d?: Doc) {
if (!d) return 0;
- let layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+ const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const nw = NumCast(layoutDoc._nativeWidth);
const nh = NumCast(layoutDoc._nativeHeight);
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
- if (!layoutDoc.ignoreAspect && !layoutDoc._fitWidth && nw && nh) {
+ if (!layoutDoc._fitWidth && nw && nh) {
const aspect = nw && nh ? nh / nw : 1;
- if (!(!layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
+ if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
@@ -233,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
const where = [de.x, de.y];
let targInd = -1;
let plusOne = 0;
@@ -247,7 +248,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
plusOne = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0;
}
});
- if (super.drop(e, de)) {
+ if (super.onInternalDrop(e, de)) {
const newDoc = de.complete.docDragData.droppedDocuments[0];
const docs = this.childDocList;
if (docs) {
@@ -263,7 +264,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
@undoBatch
@action
- onDrop = async (e: React.DragEvent): Promise<void> => {
+ onExternalDrop = async (e: React.DragEvent): Promise<void> => {
const where = [e.clientX, e.clientY];
let targInd = -1;
this._docXfs.map((cd, i) => {
@@ -273,7 +274,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
targInd = i;
}
});
- super.onDrop(e, {}, () => {
+ super.onExternalDrop(e, {}, () => {
if (targInd !== -1) {
const newDoc = this.childDocs[this.childDocs.length - 1];
const docs = this.childDocList;
@@ -404,7 +405,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
transformOrigin: "top left",
}}
onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
- onDrop={this.onDrop.bind(this)}
+ onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
onWheel={e => this.props.active() && e.stopPropagation()} >
{this.renderedSections}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 3fc05c6b7..8c23ecd49 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -8,20 +8,22 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
import { RichTextField } from "../../../new_fields/RichTextField";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast } from "../../../new_fields/Types";
+import { NumCast, StrCast, Cast } from "../../../new_fields/Types";
import { ImageField } from "../../../new_fields/URLField";
import { TraceMobx } from "../../../new_fields/util";
-import { Docs } from "../../documents/Documents";
+import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
-import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
library.add(faPalette);
@@ -71,7 +73,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
else {
de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
}
- this.props.parent.drop(e, de);
+ this.props.parent.onInternalDrop(e, de);
e.stopPropagation();
}
});
@@ -266,8 +268,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
-
const dataDoc = this.props.parent.props.DataDoc || this.props.parent.Document;
+
+ DocUtils.addDocumentCreatorMenuItems(this.props.parent.props.addDocument, this.props.parent.props.addDocument, x, y);
+
Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof (dataDoc[fieldKey]) === "string").map(fieldKey =>
docItems.push({
description: ":" + fieldKey, event: () => {
@@ -360,7 +364,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
`Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
style={{
width: "100%",
- background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey",
+ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "inherit",
color: "grey"
}}>
<EditableView {...headerEditableViewProps} />
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 343be1b58..27a1c6367 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, trace } from "mobx";
+import { action, computed, IReactionDisposer, reaction } from "mobx";
import * as rp from 'request-promise';
import CursorField from "../../../new_fields/CursorField";
import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
@@ -25,6 +25,7 @@ import { ImageUtils } from "../../util/Import & Export/ImageUtils";
import { Networking } from "../../Network";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
import { InteractionUtils } from "../../util/InteractionUtils";
+import { Upload } from "../../../server/SharedMediaTypes";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc) => boolean;
@@ -40,6 +41,8 @@ export interface CollectionViewProps extends FieldViewProps {
export interface SubCollectionViewProps extends CollectionViewProps {
CollectionView: Opt<CollectionView>;
children?: never | (() => JSX.Element[]) | React.ReactNode;
+ overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explict list (see LinkBox)
+ ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox)
isAnnotationOverlay?: boolean;
annotationsKey: string;
layoutEngine?: () => string;
@@ -56,7 +59,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
this.gestureDisposer?.();
this.multiTouchDisposer?.();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
}
@@ -66,23 +69,24 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
componentDidMount() {
- this._childLayoutDisposer = reaction(() => [this.childDocs, (Cast(this.props.Document.childLayout, Doc) as Doc)?.[Id]],
- (args) => {
- const childLayout = Cast(this.props.Document.childLayout, Doc);
+ this._childLayoutDisposer = reaction(() => ({ childDocs: this.childDocs, childLayout: Cast(this.props.Document.childLayout, Doc) }),
+ ({ childDocs, childLayout }) => {
if (childLayout instanceof Doc) {
- this.childDocs.map(doc => {
+ childDocs.map(doc => {
doc.layout_fromParent = childLayout;
doc.layoutKey = "layout_fromParent";
});
}
else if (!(childLayout instanceof Promise)) {
- this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout"));
+ childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout"));
}
}, { fireImmediately: true });
}
componentWillUnmount() {
- this._childLayoutDisposer && this._childLayoutDisposer();
+ this.gestureDisposer?.();
+ this.multiTouchDisposer?.();
+ this._childLayoutDisposer?.();
}
@computed get dataDoc() {
@@ -95,11 +99,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
// to its children which may be templates.
// If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey'
@computed get dataField() {
- const { annotationsKey, fieldKey } = this.props;
- if (annotationsKey) {
- return this.dataDoc[fieldKey + "-" + annotationsKey];
- }
- return this.dataDoc[fieldKey];
+ return this.dataDoc[this.props.fieldKey + (this.props.annotationsKey ? "-" + this.props.annotationsKey : "")];
}
get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
@@ -155,19 +155,11 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
- protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
const docDragData = de.complete.docDragData;
(this.props.Document.dropConverter instanceof ScriptField) &&
this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this
- if (docDragData && !docDragData.applyAsTemplate) {
- if (de.altKey && docDragData.draggedDocuments.length) {
- this.childDocs.map(doc => {
- doc.layout_fromParent = docDragData.draggedDocuments[0];
- doc.layoutKey = "layout_fromParent";
- });
- e.stopPropagation();
- return true;
- }
+ if (docDragData) {
let added = false;
if (this.props.Document._freezeOnDrop) {
de.complete.docDragData?.droppedDocuments.forEach(drop => Doc.freezeNativeDimensions(drop, drop[WidthSym](), drop[HeightSym]()));
@@ -194,166 +186,172 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
- protected async onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
+ protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
if (e.ctrlKey) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
return;
}
- const html = e.dataTransfer.getData("text/html");
- const text = e.dataTransfer.getData("text/plain");
+
+ const { dataTransfer } = e;
+ const html = dataTransfer.getData("text/html");
+ const text = dataTransfer.getData("text/plain");
if (text && text.startsWith("<div")) {
return;
}
+
e.stopPropagation();
e.preventDefault();
-
- if (html && FormattedTextBox.IsFragment(html)) {
- const href = FormattedTextBox.GetHref(html);
- if (href) {
- const docid = FormattedTextBox.GetDocFromUrl(href);
- if (docid) { // prosemirror text containing link to dash document
- DocServer.GetRefField(docid).then(f => {
- if (f instanceof Doc) {
- if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
- (f instanceof Doc) && this.props.addDocument(f);
- }
- });
- } else {
- this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
- }
- } else if (text) {
- this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
- }
+ const { addDocument } = this.props;
+ if (!addDocument) {
+ alert("this.props.addDocument does not exist. Aborting drop operation.");
return;
}
- if (html && !html.startsWith("<a")) {
- const tags = html.split("<");
- if (tags[0] === "") tags.splice(0, 1);
- const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
- if (img) {
- const split = img.split("src=\"")[1].split("\"")[0];
- let source = split;
- if (split.startsWith("data:image") && split.includes("base64")) {
- const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
- source = Utils.prepend(accessPaths.agnostic.client);
+
+ if (html) {
+ if (FormattedTextBox.IsFragment(html)) {
+ const href = FormattedTextBox.GetHref(html);
+ if (href) {
+ const docid = FormattedTextBox.GetDocFromUrl(href);
+ if (docid) { // prosemirror text containing link to dash document
+ DocServer.GetRefField(docid).then(f => {
+ if (f instanceof Doc) {
+ if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
+ (f instanceof Doc) && addDocument(f);
+ }
+ });
+ } else {
+ addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
+ }
+ } else if (text) {
+ addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
}
- const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
- ImageUtils.ExtractExif(doc);
- this.props.addDocument(doc);
return;
- } else {
- const path = window.location.origin + "/doc/";
- if (text.startsWith(path)) {
- const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
- DocServer.GetRefField(docid).then(f => {
- if (f instanceof Doc) {
- if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
- (f instanceof Doc) && this.props.addDocument(f);
- }
- });
+ }
+ if (!html.startsWith("<a")) {
+ const tags = html.split("<");
+ if (tags[0] === "") tags.splice(0, 1);
+ const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
+ if (img) {
+ const split = img.split("src=\"")[1].split("\"")[0];
+ let source = split;
+ if (split.startsWith("data:image") && split.includes("base64")) {
+ const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+ source = Utils.prepend(accessPaths.agnostic.client);
+ }
+ const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
+ ImageUtils.ExtractExif(doc);
+ addDocument(doc);
+ return;
} else {
- const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300, documentText: text });
- this.props.addDocument(htmlDoc);
+ const path = window.location.origin + "/doc/";
+ if (text.startsWith(path)) {
+ const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ DocServer.GetRefField(docid).then(f => {
+ if (f instanceof Doc) {
+ if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
+ (f instanceof Doc) && this.props.addDocument(f);
+ }
+ });
+ } else {
+ const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 });
+ Doc.GetProto(htmlDoc)["data-text"] = text;
+ this.props.addDocument(htmlDoc);
+ }
+ return;
}
- return;
}
}
- if (text && text.indexOf("www.youtube.com/watch") !== -1) {
- const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
- this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5 }));
- return;
- }
- let matches: RegExpExecArray | null;
- if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
- const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." });
- const proto = newBox.proto!;
- const documentId = matches[2];
- proto[GoogleRef] = documentId;
- proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
- proto.backgroundColor = "#eeeeff";
- this.props.addDocument(newBox);
- // const parent = Docs.Create.StackingDocument([newBox], { title: `Google Doc Import (${documentId})` });
- // CollectionDockingView.Instance.AddRightSplit(parent, undefined);
- // proto.height = parent[HeightSym]();
- return;
- }
- if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) {
- const albums = await GooglePhotos.Transactions.ListAlbums();
- const albumId = matches[3];
- const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
- console.log(mediaItems);
- return;
+
+ if (text) {
+ if (text.includes("www.youtube.com/watch")) {
+ const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/");
+ addDocument(Docs.Create.VideoDocument(url, {
+ ...options,
+ title: url,
+ _width: 400,
+ _height: 315,
+ _nativeWidth: 600,
+ _nativeHeight: 472.5
+ }));
+ return;
+ }
+ let matches: RegExpExecArray | null;
+ if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) {
+ const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." });
+ const proto = newBox.proto!;
+ const documentId = matches[2];
+ proto[GoogleRef] = documentId;
+ proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs...";
+ proto.backgroundColor = "#eeeeff";
+ addDocument(newBox);
+ return;
+ }
+ if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) {
+ const albumId = matches[3];
+ const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
+ console.log(mediaItems);
+ return;
+ }
}
+
const { items } = e.dataTransfer;
const { length } = items;
- if (length) {
- const batch = UndoManager.StartBatch("collection view drop");
- const promises: Promise<void>[] = [];
- // tslint:disable-next-line:prefer-for-of
- for (let i = 0; i < length; i++) {
- const item = e.dataTransfer.items[i];
- if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
- let str: string;
- const prom = new Promise<string>(resolve => item.getAsString(resolve))
- .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
- .then(result => {
- const type = result["content-type"];
- if (type) {
- Docs.Get.DocumentFromType(type, str, options)
- .then(doc => doc && this.props.addDocument(doc));
- }
- });
- promises.push(prom);
- }
- const type = item.type;
- if (item.kind === "file") {
- const file = item.getAsFile();
- const formData = new FormData();
-
- if (!file || !file.type) {
- continue;
- }
+ const files: File[] = [];
+ const generatedDocuments: Doc[] = [];
+ if (!length) {
+ alert("No uploadable content found.");
+ return;
+ }
- formData.append('file', file);
- const dropFileName = file ? file.name : "-empty-";
- promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => {
- results.map(action((result: any) => {
- const { accessPaths, nativeWidth, nativeHeight, contentSize } = result;
- if (Object.keys(accessPaths).length) {
- const full = { ...options, _width: 300, title: dropFileName };
- const pathname = Utils.prepend(accessPaths.agnostic.client);
- Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
- if (doc) {
- const proto = Doc.GetProto(doc);
- proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
- nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
- nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
- contentSize && (proto.contentSize = contentSize);
- this.props?.addDocument(doc);
- }
- });
- } else {
- alert("Upload failed...");
- }
- }));
- }));
+ const batch = UndoManager.StartBatch("collection view drop");
+ for (let i = 0; i < length; i++) {
+ const item = e.dataTransfer.items[i];
+ if (item.kind === "string" && item.type.includes("uri")) {
+ const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
+ const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+ if (type) {
+ const doc = await Docs.Get.DocumentFromType(type, stringContents, options);
+ doc && generatedDocuments.push(doc);
}
}
-
- if (promises.length) {
- Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
- } else {
- if (text && !text.includes("https://")) {
- this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
- }
- batch.end();
+ if (item.kind === "file") {
+ const file = item.getAsFile();
+ file && file.type && files.push(file);
+ }
+ }
+ for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
+ if (result instanceof Error) {
+ alert(`Upload failed: ${result.message}`);
+ return;
}
+ const full = { ...options, _width: 300, title: name };
+ const pathname = Utils.prepend(result.accessPaths.agnostic.client);
+ const doc = await Docs.Get.DocumentFromType(type, pathname, full);
+ if (!doc) {
+ continue;
+ }
+ const proto = Doc.GetProto(doc);
+ proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ if (Upload.isImageInformation(result)) {
+ proto["data-nativeWidth"] = result.nativeWidth;
+ proto["data-nativeHeight"] = result.nativeHeight;
+ proto.contentSize = result.contentSize;
+ }
+ generatedDocuments.push(doc);
+ }
+ if (generatedDocuments.length) {
+ generatedDocuments.forEach(addDocument);
+ completed && completed();
} else {
- alert("No uploadable content found.");
+ if (text && !text.includes("https://")) {
+ addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+ }
}
+ batch.end();
}
}
+
return CollectionSubView;
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2fa6813d7..6ebe81545 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -63,7 +63,9 @@
font-size: 8pt;
margin-left: 3px;
display: none;
- background: lightgray;
+}
+.collectionTreeView-keyHeader:hover {
+ background: #797777;
}
.collectionTreeView-subtitle {
@@ -84,9 +86,11 @@
.treeViewItem-openRight {
display: none;
height: 17px;
- background: gray;
width: 15px;
}
+.treeViewItem-openRight:hover {
+ background: #797777;
+}
.treeViewItem-border {
display: inherit;
@@ -101,7 +105,6 @@
.treeViewItem-openRight {
display: inline-block;
height: 17px;
- background: #a8a7a7;
width: 15px;
// display: inline;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e57003f82..7eeeb6ff1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,7 +3,7 @@ import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
@@ -34,7 +34,6 @@ import "./CollectionTreeView.scss";
import React = require("react");
import { CollectionViewType } from './CollectionView';
import { RichTextField } from '../../../new_fields/RichTextField';
-import { ObjectField } from '../../../new_fields/ObjectField';
export interface TreeViewProps {
@@ -47,7 +46,7 @@ export interface TreeViewProps {
deleteDoc: (doc: Doc) => boolean;
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
+ addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
panelWidth: () => number;
panelHeight: () => number;
@@ -56,14 +55,16 @@ export interface TreeViewProps {
indentDocument?: () => void;
outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
+ backgroundColor?: (doc: Doc) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
treeViewId: Doc;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
treeViewHideHeaderFields: () => boolean;
- preventTreeViewOpen: boolean;
+ treeViewPreventOpen: boolean;
renderedIds: string[];
onCheckedClick?: ScriptField;
+ ignoreFields?: string[];
}
library.add(faTrashAlt);
@@ -84,11 +85,10 @@ library.add(faPlus, faMinus);
*
* special fields:
* treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * preventTreeViewOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
* treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
*/
class TreeView extends React.Component<TreeViewProps> {
- static loadId = "";
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
@@ -97,8 +97,8 @@ class TreeView extends React.Component<TreeViewProps> {
get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
- @computed get treeViewOpen() { return (!this.props.preventTreeViewOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
+ set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
+ @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
@computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@@ -128,7 +128,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
@undoBatch delete = () => this.props.deleteDoc(this.props.document);
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath);
+ @undoBatch openRight = () => this.props.addDocTab(this.props.containingCollection.childDropAction === "alias" ? Doc.MakeAlias(this.props.document) : this.props.document, "onRight", this.props.libraryPath);
@undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
@undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => {
return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
@@ -171,7 +171,7 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline-block"}
- editing={this.dataDoc[Id] === TreeView.loadId}
+ editing={this.dataDoc[Id] === EditableView.loadId}
contents={StrCast(this.props.document[key])}
height={12}
fontStyle={style}
@@ -180,30 +180,30 @@ class TreeView extends React.Component<TreeViewProps> {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
- const layoutDoc = this.props.document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layout_custom)) : undefined;
- const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- TreeView.loadId = doc[Id];
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ EditableView.loadId = doc[Id];
return this.props.addDocument(doc);
})}
OnTab={undoBatch((shift?: boolean) => {
- TreeView.loadId = this.dataDoc[Id];
+ EditableView.loadId = this.dataDoc[Id];
shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document).
Doc.UnBrushDoc(this.props.document);
Doc.BrushDoc(this.props.document);
- TreeView.loadId = "";
+ EditableView.loadId = "";
}, 0);
})}
/>)
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view
+ const sort = this.props.document[`${this.fieldKey}-sortAscending`];
if (this.props.document === CurrentUserUtils.UserDocument.recentlyClosed) {
ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.GetProto(CurrentUserUtils.UserDocument.recentlyClosed as Doc).data = new List<Doc>(), icon: "plus" });
} else if (this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
- ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab", this.props.libraryPath), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight", this.props.libraryPath), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab", this.props.libraryPath), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.props.document)), icon: "camera" });
}
@@ -212,7 +212,9 @@ class TreeView extends React.Component<TreeViewProps> {
ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
}
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: (sort ? "Sort Descending" : (sort === false ? "Unsort" : "Sort Ascending")), event: () => this.props.document[`${this.fieldKey}-sortAscending`] = (sort ? false : (sort === false ? undefined : true)), icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
e.stopPropagation();
@@ -229,7 +231,7 @@ class TreeView extends React.Component<TreeViewProps> {
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceDocument;
const destDoc = this.props.document;
- DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
+ DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree drop link");
e.stopPropagation();
}
if (de.complete.docDragData) {
@@ -283,6 +285,7 @@ class TreeView extends React.Component<TreeViewProps> {
const rows: JSX.Element[] = [];
for (const key of Object.keys(ids).slice().sort()) {
+ if (this.props.ignoreFields?.includes(key)) continue;
const contents = doc[key];
let contentElement: (JSX.Element | null)[] | JSX.Element = [];
@@ -291,13 +294,13 @@ class TreeView extends React.Component<TreeViewProps> {
const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick);
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
+ this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.ignoreFields);
} else {
contentElement = <EditableView
key="editableView"
- contents={contents !== undefined ? contents.toString() : "null"}
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
height={13}
fontSize={12}
GetValue={() => Field.toKeyValueString(doc, key)}
@@ -334,9 +337,9 @@ class TreeView extends React.Component<TreeViewProps> {
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
- [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)}
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
+ [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.ignoreFields)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
@@ -350,6 +353,7 @@ class TreeView extends React.Component<TreeViewProps> {
DataDocument={this.templateDataDoc}
LibraryPath={emptyPath}
renderDepth={this.props.renderDepth + 1}
+ backgroundColor={this.props.backgroundColor}
fitToBox={this.boundsOfCollectionDocument !== undefined}
PanelWidth={this.docWidth}
PanelHeight={this.docHeight}
@@ -386,7 +390,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
- return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "black"), opacity: 0.4 }}>
+ return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: 0.4 }}>
{<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
</div>;
}
@@ -417,7 +421,6 @@ class TreeView extends React.Component<TreeViewProps> {
return <>
<div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
style={{
- color: this.props.document.isMinimized ? "red" : "black",
background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
fontWeight: this.props.document.searchMatch ? "bold" : undefined,
outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
@@ -456,8 +459,9 @@ class TreeView extends React.Component<TreeViewProps> {
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
dropAction: dropActionType,
- addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
+ addDocTab: (doc: Doc, where: string) => boolean,
pinToPres: (document: Doc) => void,
+ backgroundColor: undefined | ((document: Doc) => string | undefined),
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
@@ -465,10 +469,11 @@ class TreeView extends React.Component<TreeViewProps> {
ChromeHeight: undefined | (() => number),
renderDepth: number,
treeViewHideHeaderFields: () => boolean,
- preventTreeViewOpen: boolean,
+ treeViewPreventOpen: boolean,
renderedIds: string[],
libraryPath: Doc[] | undefined,
- onCheckedClick: ScriptField | undefined
+ onCheckedClick: ScriptField | undefined,
+ ignoreFields: string[] | undefined
) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
@@ -476,10 +481,8 @@ class TreeView extends React.Component<TreeViewProps> {
}
const docs = childDocs.slice();
- const dataExtension = containingCollection[key + "_ext"] as Doc;
- const ascending = dataExtension && BoolCast(dataExtension.sortAscending, null);
+ const ascending = containingCollection?.[key + "-sortAscending"];
if (ascending !== undefined) {
-
const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
const reN = /[0-9]*$/;
const aA = a.replace(reN, ""); // get rid of trailing numbers
@@ -563,6 +566,7 @@ class TreeView extends React.Component<TreeViewProps> {
renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
+ backgroundColor={backgroundColor}
panelWidth={rowWidth}
panelHeight={rowHeight}
ChromeHeight={ChromeHeight}
@@ -575,8 +579,9 @@ class TreeView extends React.Component<TreeViewProps> {
parentKey={key}
active={active}
treeViewHideHeaderFields={treeViewHideHeaderFields}
- preventTreeViewOpen={preventTreeViewOpen}
- renderedIds={renderedIds} />;
+ treeViewPreventOpen={treeViewPreventOpen}
+ renderedIds={renderedIds}
+ ignoreFields={ignoreFields} />;
});
}
}
@@ -591,7 +596,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer && this.treedropDisposer();
if (this._mainEle = ele) {
- this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+ this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
}
}
@@ -602,13 +607,24 @@ export class CollectionTreeView extends CollectionSubView(Document) {
@action
remove = (document: Document): boolean => {
- const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const children = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
return true;
}
return false;
}
+ @action
+ addDoc = (doc: Document, relativeTo: Opt<Doc>, before?: boolean): boolean => {
+ const doAddDoc = () =>
+ Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ if (this.props.Document.resolvedDataDoc instanceof Promise) {
+ this.props.Document.resolvedDataDoc.then(resolved => doAddDoc());
+ } else {
+ doAddDoc();
+ }
+ return true;
+ }
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
@@ -624,7 +640,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
const layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
@@ -688,7 +704,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
}
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
- onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
+ onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {});
@computed get renderClearButton() {
return <div id="toolbar" key="toolbar">
@@ -701,17 +717,19 @@ export class CollectionTreeView extends CollectionSubView(Document) {
render() {
const dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
- const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
- return !this.childDocs ? (null) : (
+ const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+ return !childDocs ? (null) : (
<div className="collectionTreeView-dropTarget" id="body"
- style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
+ style={{ background: this.props.backgroundColor?.(this.props.Document), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
{(this.props.Document.treeViewHideTitle ? (null) : <EditableView
contents={this.dataDoc.title}
+ editing={false}
display={"block"}
maxHeight={72}
height={"auto"}
@@ -719,18 +737,17 @@ export class CollectionTreeView extends CollectionSubView(Document) {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.dataDoc, "title", value, false);
- const layoutDoc = this.props.Document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layout_custom)) : undefined;
- const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- TreeView.loadId = doc[Id];
- Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ EditableView.loadId = doc[Id];
+ this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
})} />)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
- TreeView.GetChildElements(this.childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
+ TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields),
- BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
+ BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick), this.props.ignoreFields)
}
</ul>
</div >
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ad39b69d8..157229ec8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -38,6 +38,7 @@ import { CollectionViewBaseChrome } from './CollectionViewChromes';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { List } from '../../../new_fields/List';
+import { SubCollectionViewProps } from './CollectionSubView';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
@@ -89,11 +90,9 @@ export interface CollectionRenderProps {
export class CollectionView extends Touchable<FieldViewProps> {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
- private _reactionDisposer: IReactionDisposer | undefined;
private _isChildActive = false; //TODO should this be observable?
@observable private _isLightboxOpen = false;
@observable private _curLightboxImg = 0;
- @observable private _collapsed = true;
@observable private static _safeMode = false;
public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
@@ -110,30 +109,13 @@ export class CollectionView extends Touchable<FieldViewProps> {
return viewField;
}
- componentDidMount = () => {
- this._reactionDisposer = reaction(() => StrCast(this.props.Document._chromeStatus),
- () => {
- // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
- // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar.
- const chromeStatus = this.props.Document._chromeStatus;
- if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed" || chromeStatus === "replaced")) {
- runInAction(() => this._collapsed = true);
- }
- });
- }
-
- componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
-
- // bcz: Argh? What's the height of the collection chromes??
- chromeHeight = () => (this.props.Document._chromeStatus === "enabled" ? -60 : 0);
-
active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); };
@action.bound
addDocument(doc: Doc): boolean {
- const targetDataDoc = this.props.Document.resolvedDataDoc && !this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document[DataSym]);
+ const targetDataDoc = this.props.Document[DataSym];
targetDataDoc[this.props.fieldKey] = new List<Doc>([...DocListCast(targetDataDoc[this.props.fieldKey]), doc]); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
// Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
@@ -143,16 +125,17 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
removeDocument(doc: Doc): boolean {
+ const targetDataDoc = this.props.Document[DataSym];
const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- const targetDataDoc = this.props.Document.resolvedDataDoc && !this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document[DataSym]);
- const value = Cast(targetDataDoc[this.props.fieldKey], listSpec(Doc), []);
+ const value = DocListCast(targetDataDoc[this.props.fieldKey]);
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
ContextMenu.Instance.clearItems();
if (index !== -1) {
value.splice(index, 1);
+ targetDataDoc[this.props.fieldKey] = new List<Doc>(value);
return true;
}
return false;
@@ -178,7 +161,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- const props = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ const props: SubCollectionViewProps = { ...this.props, ...renderProps, CollectionView: this, annotationsKey: "" };
switch (type) {
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
@@ -198,7 +181,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action
private collapse = (value: boolean) => {
- this._collapsed = value;
this.props.Document._chromeStatus = value ? "collapsed" : "enabled";
}
@@ -246,22 +228,27 @@ export class CollectionView extends Touchable<FieldViewProps> {
const layoutItems = existing && "subitems" in existing ? existing.subitems : [];
layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
if (this.props.Document.childLayout instanceof Doc) {
- layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, undefined, "onRight"), icon: "project-diagram" });
+ layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
}
if (this.props.Document.childDetailed instanceof Doc) {
- layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, undefined, "onRight"), icon: "project-diagram" });
+ layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, "onRight"), icon: "project-diagram" });
}
!existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
- const more = ContextMenu.Instance.findByDescription("More...");
- const moreItems = more && "subitems" in more ? more.subitems : [];
- moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
- !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+ const open = ContextMenu.Instance.findByDescription("Open...");
+ const openItems = open && "subitems" in open ? open.subitems : [];
+ !open && ContextMenu.Instance.addItem({ description: "Open...", subitems: openItems, icon: "hand-point-right" });
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Edit onChildClick script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Child Clicked...", this.props.Document, "onChildClick", obj.x, obj.y) });
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
+
+ const more = ContextMenu.Instance.findByDescription("More...");
+ const moreItems = more && "subitems" in more ? more.subitems : [];
+ moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
+ !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+
}
}
@@ -293,7 +280,8 @@ export class CollectionView extends Touchable<FieldViewProps> {
return (<div className={"collectionView"}
style={{
pointerEvents: this.props.Document.isBackground ? "none" : "all",
- boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined : `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
+ boxShadow: this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
+ `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31)" : "#9c9396"} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
}}
onContextMenu={this.onContextMenu}>
{this.showIsTagged()}
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 517f467b7..0c96a662c 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -2,7 +2,8 @@
@import '~js-datepicker/dist/datepicker.min.css';
.collectionViewChrome-cont {
- position: relative;
+ position: absolute;
+ width:100%;
opacity: 0.9;
z-index: 9001;
transition: top .5s;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index a21e78188..49a9e8200 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -179,7 +179,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action closeViewSpecs = () => {
this._viewSpecsOpen = false;
document.removeEventListener("pointerdown", this.closeViewSpecs);
- };
+ }
@action
openDatePicker = (e: React.PointerEvent) => {
@@ -257,6 +257,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
}
subChrome = () => {
+ const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+ if (collapsed) return null;
switch (this.props.type) {
case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
@@ -368,7 +370,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
return (
<div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined }}>
- <div className="collectionViewChrome">
+ <div className="collectionViewChrome" style={{ border: "unset" }}>
<div className="collectionViewBaseChrome">
<button className="collectionViewBaseChrome-collapse"
style={{
@@ -384,6 +386,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
className="collectionViewBaseChrome-viewPicker"
onPointerDown={stopPropagation}
onChange={this.viewChanged}
+ style={{ display: collapsed ? "none" : undefined }}
value={NumCast(this.props.CollectionView.props.Document._viewType)}>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema</option>
@@ -438,7 +441,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
</div>
</div>
</div>
- <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
+ <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
<div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
<div className="commandEntry-drop">
<FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss
index a266861bd..4e704b58f 100644
--- a/src/client/views/collections/ParentDocumentSelector.scss
+++ b/src/client/views/collections/ParentDocumentSelector.scss
@@ -35,6 +35,10 @@
pointer-events: all;
position: relative;
display: inline-block;
+ svg {
+ width:20px !important;
+ height:20px;
+ }
}
.parentDocumentSelector-metadata {
pointer-events: auto;
@@ -46,8 +50,7 @@
div {
overflow: visible !important;
}
- position: absolute;
display: inline-block;
- padding-left: 5px;
- padding-right: 5px;
+ width:100%;
+ height:100%;
} \ No newline at end of file
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 115f8d633..43ba5c614 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -11,21 +11,20 @@ import { CollectionViewType } from "./CollectionView";
import { DocumentButtonBar } from "../DocumentButtonBar";
import { DocumentManager } from "../../util/DocumentManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
+import { faCog, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
import { library } from "@fortawesome/fontawesome-svg-core";
-import { MetadataEntryMenu } from "../MetadataEntryMenu";
import { DocumentView } from "../nodes/DocumentView";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faEdit);
+library.add(faCog);
type SelectorProps = {
Document: Doc,
Views: DocumentView[],
Stack?: any,
- addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void
+ addDocTab(doc: Doc, location: string): void
};
@observer
@@ -61,7 +60,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
col._panX = newPanX;
col._panY = newPanY;
}
- this.props.addDocTab(col, undefined, "inTab"); // bcz: dataDoc?
+ this.props.addDocTab(col, "inTab"); // bcz: dataDoc?
};
}
@@ -79,13 +78,12 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
export class ParentDocSelector extends React.Component<SelectorProps> {
render() {
const flyout = (
- <div className="parentDocumentSelector-flyout" style={{}} title=" ">
+ <div className="parentDocumentSelector-flyout" title=" ">
<SelectorContextMenu {...this.props} />
</div>
);
- return <div title="Tap to View Contexts/Metadata" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP}
- content={flyout}>
+ return <div title="Show Contexts" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
<span className="parentDocumentSelector-button" >
<FontAwesomeIcon icon={faChevronCircleUp} size={"lg"} />
</span>
@@ -95,14 +93,9 @@ export class ParentDocSelector extends React.Component<SelectorProps> {
}
@observer
-export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any }> {
+export class DockingViewButtonSelector extends React.Component<{ Document: Doc, Stack: any }> {
@observable hover = false;
- @action
- onPointerDown = (e: React.PointerEvent) => {
- this.hover = !this.hover;
- e.stopPropagation();
- }
customStylesheet(styles: any) {
return {
...styles,
@@ -120,9 +113,9 @@ export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any
<DocumentButtonBar views={[view]} stack={this.props.Stack} />
</div>
);
- return <span title="Tap for menu" onPointerDown={e => e.stopPropagation()} className="buttonSelector">
+ return <span title="Tap for menu, drag tab as document" onPointerDown={e => !this.props.Stack && e.stopPropagation()} className="buttonSelector">
<Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} stylesheet={this.customStylesheet}>
- <FontAwesomeIcon icon={faEdit} size={"sm"} />
+ <FontAwesomeIcon icon={"cog"} size={"sm"} />
</Flyout>
</span>;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d363770bf..7e37f646d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -27,15 +27,15 @@ export interface ViewDefBounds {
}
export interface PoolData {
- x?: number,
- y?: number,
- z?: number,
- zIndex?: number,
- width?: number,
- height?: number,
- color?: string,
- transition?: string,
- highlight?: boolean,
+ x?: number;
+ y?: number;
+ z?: number;
+ zIndex?: number;
+ width?: number;
+ height?: number;
+ color?: string;
+ transition?: string;
+ highlight?: boolean;
}
export interface ViewDefResult {
@@ -63,16 +63,16 @@ function toLabel(target: FieldResult<Field>) {
*/
function getTextWidth(text: string, font: string): number {
// re-use canvas object for better performance
- var canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
- var context = canvas.getContext("2d");
+ const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
+ const context = canvas.getContext("2d");
context.font = font;
- var metrics = context.measureText(text);
+ const metrics = context.measureText(text);
return metrics.width;
}
-interface pivotColumn {
- docs: Doc[],
- filters: string[]
+interface PivotColumn {
+ docs: Doc[];
+ filters: string[];
}
@@ -86,7 +86,7 @@ export function computePivotLayout(
viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
) {
const fieldKey = "data";
- const pivotColumnGroups = new Map<FieldResult<Field>, pivotColumn>();
+ const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
const pivotFieldKey = toLabel(pivotDoc._pivotField);
for (const doc of filterDocs) {
@@ -123,7 +123,7 @@ export function computePivotLayout(
const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`;
const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number);
const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
- let maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
+ const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
const colWidth = panelDim[0] / pivotColumnGroups.size;
const colHeight = panelDim[1] - max_text;
@@ -223,7 +223,7 @@ export function computeTimelineLayout(
const findStack = (time: number, stack: number[]) => {
const index = stack.findIndex(val => val === undefined || val < x);
return index === -1 ? stack.length : index;
- }
+ };
let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
@@ -266,7 +266,7 @@ export function computeTimelineLayout(
}
const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
- let stacking: number[] = [];
+ const stacking: number[] = [];
let zind = 0;
sortedKeys.forEach(key => {
if (curTime !== undefined && curTime > prevKey && curTime <= key) {
@@ -289,7 +289,7 @@ export function computeTimelineLayout(
groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childDocs.filter(c => !filterDocs.includes(c)));
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -314,7 +314,7 @@ export function computeTimelineLayout(
function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<Doc, ViewDefBounds>,
poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[],
- extraDocs: Doc[]) {
+ extraDocs: Doc[]):ViewDefResult[] {
const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as ViewDefBounds);
@@ -341,8 +341,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
});
extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 }));
- return {
- elements: viewDefsToJSX(extras.concat(groupNames).map(gname => ({
+ return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
type: gname.type,
text: gname.text,
x: gname.x * scale,
@@ -352,8 +351,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
height: gname.height === -1 ? 1 : Math.max(fontHeight, (gname.height || 0) * scale),
fontSize: gname.fontSize,
payload: gname.payload
- })))
- };
+ })));
}
export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index f04b79ea4..1038347d4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -95,10 +95,15 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2 = [bpt.point.x, bpt.point.y];
const aActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
const bActive = this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document);
- return !aActive && !bActive ? (null) :
+ const text = StrCast(this.props.A.props.Document.linkRelationship);
+ return !aActive && !bActive ? (null) : (<>
+ <text x={(pt1[0] + pt2[0]) / 2} y={(pt1[1] + pt2[1]) / 2}>
+ {text !== "-ungrouped-" ? text : ""}
+ </text>
<line key="linkLine" className="collectionfreeformlinkview-linkLine"
style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
x1={`${pt1[0]}`} y1={`${pt1[1]}`}
- x2={`${pt2[0]}`} y2={`${pt2[1]}`} />;
+ x2={`${pt2[0]}`} y2={`${pt2[1]}`} />
+ </>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7f1817a15..4458c7dcf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -10,7 +10,7 @@ import { Id } from "../../../../new_fields/FieldSymbols";
import { InkTool } from "../../../../new_fields/InkField";
import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, BoolCast, StrCast } from "../../../../new_fields/Types";
import { TraceMobx } from "../../../../new_fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -72,6 +72,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _hitCluster = false;
private _layoutComputeReaction: IReactionDisposer | undefined;
private _layoutPoolData = observable.map<string, any>();
+ private _cachedPool: Map<string, any> = new Map();
public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@@ -110,27 +111,27 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
}
- public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+ public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
public getActiveDocuments = () => {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
}
@action
- onDrop = (e: React.DragEvent): Promise<void> => {
+ onExternalDrop = (e: React.DragEvent): Promise<void> => {
const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- return super.onDrop(e, { x: pt[0], y: pt[1] });
+ return super.onExternalDrop(e, { x: pt[0], y: pt[1] });
}
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (this.props.Document.isBackground) return false;
const xf = this.getTransform();
const xfo = this.getTransformOverlay();
const [xp, yp] = xf.transformPoint(de.x, de.y);
const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
- if (super.drop(e, de)) {
+ if (super.onInternalDrop(e, de)) {
if (de.complete.docDragData) {
if (de.complete.docDragData.droppedDocuments.length) {
const firstDoc = de.complete.docDragData.droppedDocuments[0];
@@ -206,6 +207,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@undoBatch
+ @action
updateClusters(useClusters: boolean) {
this.props.Document.useClusters = useClusters;
this._clusterSets.length = 0;
@@ -243,7 +245,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
}
childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
- childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster?.toString());
}
}
@@ -279,16 +280,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getClusterColor = (doc: Doc) => {
- let clusterColor = "";
+ let clusterColor = this.props.backgroundColor?.(doc);
const cluster = NumCast(doc.cluster);
if (this.Document.useClusters) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => this.updateCluster(doc), 0);
} else {
// choose a cluster color from a palette
- const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+ const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
clusterColor = colors[cluster % colors.length];
- const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
// 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));
@@ -432,19 +433,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let x = this.Document._panX || 0;
let y = this.Document._panY || 0;
- const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout);
+ const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout);
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
if (!this.isAnnotationOverlay && docs.length && this.childDataProvider(docs[0])) {
PDFMenu.Instance.fadeOut(true);
- const minx = this.childDataProvider(docs[0]).x;//docs.length ? NumCast(docs[0].x) : 0;
- const miny = this.childDataProvider(docs[0]).y;//docs.length ? NumCast(docs[0].y) : 0;
- const maxx = this.childDataProvider(docs[0]).width + minx;//docs.length ? NumCast(docs[0].width) + minx : minx;
- const maxy = this.childDataProvider(docs[0]).height + miny;//docs.length ? NumCast(docs[0].height) + miny : miny;
- const ranges = docs.filter(doc => doc).filter(doc => this.childDataProvider(doc)).reduce((range, doc) => {
- const x = this.childDataProvider(doc).x;//NumCast(doc.x);
- const y = this.childDataProvider(doc).y;//NumCast(doc.y);
- const xe = this.childDataProvider(doc).width + x;//x + NumCast(layoutDoc.width);
- const ye = this.childDataProvider(doc).height + y; //y + NumCast(layoutDoc.height);
+ const minx = this.childDataProvider(docs[0]).x;
+ const miny = this.childDataProvider(docs[0]).y;
+ const maxx = this.childDataProvider(docs[0]).width + minx;
+ const maxy = this.childDataProvider(docs[0]).height + miny;
+ const ranges = docs.filter(doc => doc && this.childDataProvider(doc)).reduce((range, doc) => {
+ const x = this.childDataProvider(doc).x;
+ const y = this.childDataProvider(doc).y;
+ const xe = this.childDataProvider(doc).width + x;
+ const ye = this.childDataProvider(doc).height + y;
return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]],
[range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]];
}, [[minx, maxx], [miny, maxy]]);
@@ -709,6 +710,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
@computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+ backgroundHalo = () => BoolCast(this.Document.useClusters);
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
@@ -728,6 +730,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
ContainingCollectionDoc: this.props.Document,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
+ backgroundHalo: this.backgroundHalo,
parentActive: this.props.active,
bringToFront: this.bringToFront,
zoomToScale: this.zoomToScale,
@@ -741,9 +744,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return { ...result, transition: "transform 1s" };
}
const layoutDoc = Doc.Layout(params.doc);
+ const { x, y, z, color, zIndex } = params.doc;
return {
- x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), color: Cast(params.doc.color, "string"),
- zIndex: Cast(params.doc.zIndex, "number"), width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number")
+ x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
+ width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number")
};
}
@@ -755,55 +759,46 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
(this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload });
}
private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
- const x = Cast(viewDef.x, "number");
- const y = Cast(viewDef.y, "number");
- const z = Cast(viewDef.z, "number");
- const highlight = Cast(viewDef.highlight, "boolean");
- const zIndex = Cast(viewDef.zIndex, "number");
- const color = Cast(viewDef.color, "string");
- const width = Cast(viewDef.width, "number", null);
- const height = Cast(viewDef.height, "number", null);
+ const { x, y, z } = viewDef;
+ const color = StrCast(viewDef.color);
+ const width = Cast(viewDef.width, "number");
+ const height = Cast(viewDef.height, "number");
+ const transform = `translate(${x}px, ${y}px)`;
if (viewDef.type === "text") {
const text = Cast(viewDef.text, "string"); // don't use NumCast, StrCast, etc since we want to test for undefined below
const fontSize = Cast(viewDef.fontSize, "number");
return [text, x, y].some(val => val === undefined) ? undefined :
{
- ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color}
- style={{ width, height, color, fontSize, transform: `translate(${x}px, ${y}px)` }}>
+ ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
{text}
</div>,
bounds: viewDef
};
} else if (viewDef.type === "div") {
- const backgroundColor = Cast(viewDef.color, "string");
return [x, y].some(val => val === undefined) ? undefined :
{
ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z} onClick={e => this.onViewDefDivClick(e, viewDef)}
- style={{ width, height, backgroundColor, transform: `translate(${x}px, ${y}px)` }} />,
+ style={{ width, height, backgroundColor: color, transform }} />,
bounds: viewDef
};
}
}
childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) {
- if (!doc) {
- console.log(doc);
- }
return this._layoutPoolData.get(doc[Id]);
}.bind(this));
- doTimelineLayout(poolData: Map<string, any>) {
+ doTimelineLayout(poolData: Map<string, PoolData>) {
return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
- doPivotLayout(poolData: Map<string, any>) {
+ doPivotLayout(poolData: Map<string, PoolData>) {
return computePivotLayout(poolData, this.props.Document, this.childDocs, this.filterDocs,
this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
- _cachedPool: Map<string, any> = new Map();
- doFreeformLayout(poolData: Map<string, any>) {
+ doFreeformLayout(poolData: Map<string, PoolData>) {
const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
const state = initResult && initResult.success ? initResult.result.scriptState : undefined;
@@ -813,7 +808,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
poolData.set(pair.layout[Id], pos);
});
- return { elements: elements };
+ return elements;
}
@computed get doInternalLayoutComputation() {
@@ -828,25 +823,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get filterDocs() {
const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
- const clusters: { [key: string]: { [value: string]: string } } = {};
+ const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
for (let i = 0; i < docFilters.length; i += 3) {
const [key, value, modifiers] = docFilters.slice(i, i + 3);
- const cluster = clusters[key];
- if (!cluster) {
- const child: { [value: string]: string } = {};
- child[value] = modifiers;
- clusters[key] = child;
- } else {
- cluster[value] = modifiers;
+ if (!filterFacets[key]) {
+ filterFacets[key] = {};
}
+ filterFacets[key][value] = modifiers;
}
const filteredDocs = docFilters.length ? this.childDocs.filter(d => {
- for (const key of Object.keys(clusters)) {
- const cluster = clusters[key];
- const satisfiesFacet = Object.keys(cluster).some(inner => {
- const modifier = cluster[inner];
- return (modifier === "x") !== Doc.matchFieldValue(d, key, inner);
- });
+ for (const facetKey of Object.keys(filterFacets)) {
+ const facet = filterFacets[facetKey];
+ const satisfiesFacet = Object.keys(facet).some(value =>
+ (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value));
if (!satisfiesFacet) {
return false;
}
@@ -867,7 +856,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
});
return rangeFilteredDocs;
}
- childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null) as Doc;
+ childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
runInAction(() =>
@@ -880,8 +869,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}));
this._cachedPool.clear();
Array.from(newPool.keys()).forEach(k => this._cachedPool.set(k, newPool.get(k)));
- this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair =>
- computedElementData.elements.push({
+ const elements: ViewDefResult[] = computedElementData.slice();
+ this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair =>
+ elements.push({
ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} {...this.getChildDocumentViewProps(pair.layout, pair.data)}
dataProvider={this.childDataProvider}
LayoutDoc={this.childLayoutDocFunc}
@@ -890,14 +880,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
bounds: this.childDataProvider(pair.layout)
}));
- return computedElementData;
+ return elements;
}
componentDidMount() {
super.componentDidMount();
- this._layoutComputeReaction = reaction(
- () => (this.doLayoutComputation),
- (computation) => this._layoutElements = computation?.elements.slice() || [],
+ this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
+ (elements) => this._layoutElements = elements || [],
{ fireImmediately: true, name: "doLayout" });
}
componentWillUnmount() {
@@ -977,6 +966,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// }
onContextMenu = (e: React.MouseEvent) => {
+ if (this.props.children && this.props.annotationsKey) return;
const layoutItems: ContextMenuProps[] = [];
layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
@@ -1014,19 +1004,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
});
- layoutItems.push({
- description: "Add Note ...",
- subitems: DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({
- description: (i + 1) + ": " + StrCast(note.title),
- event: (args: { x: number, y: number }) => this.addLiveTextBox(Docs.Create.TextDocument("", { _width: 200, _height: 100, x: this.getTransform().transformPoint(args.x, args.y)[0], y: this.getTransform().transformPoint(args.x, args.y)[1], _autoHeight: true, layout: note, title: StrCast(note.title) })),
- icon: "eye"
- })) as ContextMenuProps[],
- icon: "eye"
- });
ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" });
}
-
private childViews = () => {
const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
return [
@@ -1035,13 +1015,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
];
}
- // @observable private _palette?: JSX.Element;
-
children = () => {
const eles: JSX.Element[] = [];
eles.push(...this.childViews());
- // this._palette && (eles.push(this._palette));
- // this.currentStroke && (eles.push(this.currentStroke));
eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
return eles;
}
@@ -1074,11 +1050,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
// if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
return <div className={"collectionfreeformview-container"}
ref={this.createDashEventsTarget}
onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
style={{
pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
transform: this.contentScaling ? `scale(${this.contentScaling})` : "",
@@ -1086,7 +1061,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
- {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ?
+ {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
</div>;
@@ -1100,7 +1075,7 @@ interface CollectionFreeFormOverlayViewProps {
@observer
class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{
render() {
- return this.props.elements().filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele);
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele);
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4b0855635..3cfefc25e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -3,22 +3,21 @@ import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../../new_fields/Doc";
import { InkField } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { ComputedField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, NumCast } from "../../../../new_fields/Types";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
import { Utils } from "../../../../Utils";
-import { Docs } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
+import { ContextMenu } from "../../ContextMenu";
import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionViewType } from "../CollectionView";
+import { SubCollectionViewProps } from "../CollectionSubView";
+import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import "./MarqueeView.scss";
import React = require("react");
-import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
-import { SubCollectionViewProps } from "../CollectionSubView";
+import { CollectionView } from "../CollectionView";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -66,7 +65,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
//make textbox and add it to this collection
// tslint:disable-next-line:prefer-const
let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
- if (e.key === "q" && e.ctrlKey) {
+ if (e.key === ":") {
+ DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y);
+
+ ContextMenu.Instance.displayMenu(this._downX, this._downY);
+ } else if (e.key === "q" && e.ctrlKey) {
e.preventDefault();
(async () => {
const text: string = await navigator.clipboard.readText();
@@ -308,8 +311,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
y: bounds.top,
_panX: 0,
_panY: 0,
- backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
- defaultBackgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
+ backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : undefined,
_width: bounds.width,
_height: bounds.height,
_LODdisable: true,
@@ -353,8 +355,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.page = -1;
return d;
});
- const summary = Docs.Create.TextDocument("", { x: bounds.left, y: bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "-summary-" });
+ const portal = Doc.MakeAlias(summary);
Doc.GetProto(summary)["data-annotations"] = new List<Doc>(selected);
+ Doc.GetProto(summary).layout_portal = CollectionView.LayoutString("data-annotations");
+ summary._backgroundColor = "#e2ad32";
+ portal.layoutKey = "layout_portal";
+ DocUtils.MakeLink({ doc: summary, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, "portal link", "portal link");
+
this.props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 7d8de0db4..82175c0b5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
import { Doc } from '../../../../new_fields/Doc';
import { documentSchema } from '../../../../new_fields/documentSchemas';
import { makeInterface } from '../../../../new_fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../new_fields/Types';
+import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -13,6 +13,7 @@ import { CollectionSubView } from '../CollectionSubView';
import "./collectionMulticolumnView.scss";
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
+import { List } from '../../../../new_fields/List';
type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
const MulticolumnDocument = makeInterface(documentSchema);
@@ -189,8 +190,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (super.drop(e, de)) {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (super.onInternalDrop(e, de)) {
de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
d.dimUnit = "*";
d.dimMagnitude = 1;
@@ -207,13 +208,14 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
{...this.props}
Document={layout}
DataDocument={layout.resolvedDataDoc as Doc}
+ backgroundColor={this.props.backgroundColor}
CollectionDoc={this.props.Document}
PanelWidth={width}
PanelHeight={height}
getTransform={dxf}
onClick={this.onChildClickHandler}
renderDepth={this.props.renderDepth + 1}
- />
+ />;
}
/**
* @returns the resolved list of rendered child documents, displayed
@@ -221,13 +223,17 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
*/
@computed
private get contents(): JSX.Element[] | null {
- const { childLayoutPairs } = this;
+ // bcz: feels like a hack ... trying to show something useful when there's no list document in the data field of a templated object
+ const expanded = Cast(this.props.Document.expandedTemplate, Doc, null);
+ let { childLayoutPairs } = this.dataDoc[this.props.fieldKey] instanceof List || !expanded ? this : { childLayoutPairs: [] } as { childLayoutPairs: { layout: Doc, data: Doc }[] };
+ const replaced = !childLayoutPairs.length && !Cast(expanded?.layout, Doc, null) && expanded;
+ childLayoutPairs = childLayoutPairs.length || !replaced ? childLayoutPairs : [{ layout: replaced, data: replaced }];
const { Document, PanelHeight } = this.props;
const collector: JSX.Element[] = [];
for (let i = 0; i < childLayoutPairs.length; i++) {
const { layout } = childLayoutPairs[i];
const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin));
- const width = () => this.lookupPixels(layout);
+ const width = () => expanded ? this.props.PanelWidth() : this.lookupPixels(layout);
const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
collector.push(
<div className={"document-wrapper"}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index a7e2c5707..79fb195e8 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -20,7 +20,7 @@
}
.multiRowResizer {
- cursor: ew-resize;
+ cursor: ns-resize;
transition: 0.5s opacity ease;
display: flex;
flex-direction: row;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index ec05443e5..5e59f8237 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -190,8 +190,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
@undoBatch
@action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (super.drop(e, de)) {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (super.onInternalDrop(e, de)) {
de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
d.dimUnit = "*";
d.dimMagnitude = 1;
@@ -208,13 +208,14 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
{...this.props}
Document={layout}
DataDocument={layout.resolvedDataDoc as Doc}
+ backgroundColor={this.props.backgroundColor}
CollectionDoc={this.props.Document}
PanelWidth={width}
PanelHeight={height}
getTransform={dxf}
onClick={this.onChildClickHandler}
renderDepth={this.props.renderDepth + 1}
- />
+ />;
}
/**
* @returns the resolved list of rendered child documents, displayed
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 6b89402e6..2cbeb3526 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -4,6 +4,7 @@ import { observable, action } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast } from "../../../../new_fields/Types";
import { DimUnit } from "./CollectionMulticolumnView";
+import { UndoManager } from "../../../util/UndoManager";
interface ResizerProps {
width: number;
@@ -12,30 +13,24 @@ interface ResizerProps {
toRight?: Doc;
}
-enum ResizeMode {
- Global = "blue",
- Pinned = "red",
- Undefined = "black"
-}
-
const resizerOpacity = 1;
@observer
export default class ResizeBar extends React.Component<ResizerProps> {
@observable private isHoverActive = false;
@observable private isResizingActive = false;
- @observable private resizeMode = ResizeMode.Undefined;
+ private _resizeUndo?: UndoManager.Batch;
@action
- private registerResizing = (e: React.PointerEvent<HTMLDivElement>, mode: ResizeMode) => {
+ private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
- this.resizeMode = mode;
window.removeEventListener("pointermove", this.onPointerMove);
window.removeEventListener("pointerup", this.onPointerUp);
window.addEventListener("pointermove", this.onPointerMove);
window.addEventListener("pointerup", this.onPointerUp);
this.isResizingActive = true;
+ this._resizeUndo = UndoManager.StartBatch("multcol resizing");
}
private onPointerMove = ({ movementX }: PointerEvent) => {
@@ -49,7 +44,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementX) / scale);
}
- if (this.resizeMode === ResizeMode.Pinned && toWiden) {
+ if (toWiden) {
const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementX) / scale);
}
@@ -79,11 +74,12 @@ export default class ResizeBar extends React.Component<ResizerProps> {
@action
private onPointerUp = () => {
- this.resizeMode = ResizeMode.Undefined;
this.isResizingActive = false;
this.isHoverActive = false;
window.removeEventListener("pointermove", this.onPointerMove);
window.removeEventListener("pointerup", this.onPointerUp);
+ this._resizeUndo?.end();
+ this._resizeUndo = undefined;
}
render() {
@@ -97,16 +93,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
onPointerEnter={action(() => this.isHoverActive = true)}
onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
>
- <div
- className={"multiColumnResizer-hdl"}
- onPointerDown={e => this.registerResizing(e, ResizeMode.Pinned)}
- style={{ backgroundColor: this.resizeMode }}
- />
- <div
- className={"multiColumnResizer-hdl"}
- onPointerDown={e => this.registerResizing(e, ResizeMode.Global)}
- style={{ backgroundColor: this.resizeMode }}
- />
+ <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
</div>
);
}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index d00939b26..9df8cc3e2 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -4,6 +4,7 @@ import { observable, action } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast } from "../../../../new_fields/Types";
import { DimUnit } from "./CollectionMultirowView";
+import { UndoManager } from "../../../util/UndoManager";
interface ResizerProps {
height: number;
@@ -12,30 +13,24 @@ interface ResizerProps {
toBottom?: Doc;
}
-enum ResizeMode {
- Global = "blue",
- Pinned = "red",
- Undefined = "black"
-}
-
const resizerOpacity = 1;
@observer
export default class ResizeBar extends React.Component<ResizerProps> {
@observable private isHoverActive = false;
@observable private isResizingActive = false;
- @observable private resizeMode = ResizeMode.Undefined;
+ private _resizeUndo?: UndoManager.Batch;
@action
- private registerResizing = (e: React.PointerEvent<HTMLDivElement>, mode: ResizeMode) => {
+ private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
- this.resizeMode = mode;
window.removeEventListener("pointermove", this.onPointerMove);
window.removeEventListener("pointerup", this.onPointerUp);
window.addEventListener("pointermove", this.onPointerMove);
window.addEventListener("pointerup", this.onPointerUp);
this.isResizingActive = true;
+ this._resizeUndo = UndoManager.StartBatch("multcol resizing");
}
private onPointerMove = ({ movementY }: PointerEvent) => {
@@ -49,7 +44,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const scale = StrCast(toNarrow.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
toNarrow.dimMagnitude = Math.max(0.05, NumCast(toNarrow.dimMagnitude, 1) - Math.abs(movementY) / scale);
}
- if (this.resizeMode === ResizeMode.Pinned && toWiden) {
+ if (toWiden) {
const scale = StrCast(toWiden.dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
toWiden.dimMagnitude = Math.max(0.05, NumCast(toWiden.dimMagnitude, 1) + Math.abs(movementY) / scale);
}
@@ -79,11 +74,12 @@ export default class ResizeBar extends React.Component<ResizerProps> {
@action
private onPointerUp = () => {
- this.resizeMode = ResizeMode.Undefined;
this.isResizingActive = false;
this.isHoverActive = false;
window.removeEventListener("pointermove", this.onPointerMove);
window.removeEventListener("pointerup", this.onPointerUp);
+ this._resizeUndo?.end();
+ this._resizeUndo = undefined;
}
render() {
@@ -97,16 +93,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
onPointerEnter={action(() => this.isHoverActive = true)}
onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
>
- <div
- className={"multiRowResizer-hdl"}
- onPointerDown={e => this.registerResizing(e, ResizeMode.Pinned)}
- style={{ backgroundColor: this.resizeMode }}
- />
- <div
- className={"multiRowResizer-hdl"}
- onPointerDown={e => this.registerResizing(e, ResizeMode.Global)}
- style={{ backgroundColor: this.resizeMode }}
- />
+ <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
</div>
);
}