aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-11-19 11:28:49 -0500
committerSam Wilkins <samwilkins333@gmail.com>2019-11-19 11:28:49 -0500
commit3f86ea5556ab7a1fc13cd9c74ef4305f2f5cd778 (patch)
tree4d13f3401ecc80fb5bce22d2a3382545102fda7f /src/client/views/collections
parent611bb858265b6667f2b7db858d183cea16f273aa (diff)
parentd259cf7b16e64794bc12ae420f9b512a75eee46c (diff)
merged with master
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx192
-rw-r--r--src/client/views/collections/CollectionDockingView.scss73
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx43
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx173
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx1
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss27
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx425
-rw-r--r--src/client/views/collections/CollectionStackingView.scss41
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx71
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx6
-rw-r--r--src/client/views/collections/CollectionStaffView.scss13
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx61
-rw-r--r--src/client/views/collections/CollectionSubView.tsx38
-rw-r--r--src/client/views/collections/CollectionTreeView.scss2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx155
-rw-r--r--src/client/views/collections/CollectionView.scss (renamed from src/client/views/collections/CollectionBaseView.scss)2
-rw-r--r--src/client/views/collections/CollectionView.tsx220
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx78
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx84
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx69
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx510
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx46
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss1
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx355
30 files changed, 1348 insertions, 1358 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
deleted file mode 100644
index 7798964ea..000000000
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ /dev/null
@@ -1,192 +0,0 @@
-import { action, computed, observable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { Id } from '../../../new_fields/FieldSymbols';
-import { List } from '../../../new_fields/List';
-import { listSpec } from '../../../new_fields/Schema';
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from '../../../new_fields/Types';
-import { DocumentManager } from '../../util/DocumentManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { ContextMenu } from '../ContextMenu';
-import { FieldViewProps } from '../nodes/FieldView';
-import './CollectionBaseView.scss';
-import { DateField } from '../../../new_fields/DateField';
-import { ImageField } from '../../../new_fields/URLField';
-
-export enum CollectionViewType {
- Invalid,
- Freeform,
- Schema,
- Docking,
- Tree,
- Stacking,
- Masonry,
- Pivot,
- Linear,
-}
-
-export namespace CollectionViewType {
-
- const stringMapping = new Map<string, CollectionViewType>([
- ["invalid", CollectionViewType.Invalid],
- ["freeform", CollectionViewType.Freeform],
- ["schema", CollectionViewType.Schema],
- ["docking", CollectionViewType.Docking],
- ["tree", CollectionViewType.Tree],
- ["stacking", CollectionViewType.Stacking],
- ["masonry", CollectionViewType.Masonry],
- ["pivot", CollectionViewType.Pivot],
- ["linear", CollectionViewType.Linear]
- ]);
-
- export const valueOf = (value: string) => {
- return stringMapping.get(value.toLowerCase());
- };
-
-}
-
-export interface CollectionRenderProps {
- addDocument: (document: Doc) => boolean;
- removeDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
- active: () => boolean;
- whenActiveChanged: (isActive: boolean) => void;
-}
-
-export interface CollectionViewProps extends FieldViewProps {
- onContextMenu?: (e: React.MouseEvent) => void;
- children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null | (JSX.Element | null)[];
- className?: string;
- contentRef?: React.Ref<HTMLDivElement>;
-}
-
-@observer
-export class CollectionBaseView extends React.Component<CollectionViewProps> {
- @observable private static _safeMode = false;
- static InSafeMode() { return this._safeMode; }
- static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
- get collectionViewType(): CollectionViewType | undefined {
- let Document = this.props.Document;
- let viewField = Cast(Document.viewType, "number");
- if (CollectionBaseView._safeMode) {
- if (viewField === CollectionViewType.Freeform) {
- return CollectionViewType.Tree;
- }
- if (viewField === CollectionViewType.Invalid) {
- return CollectionViewType.Freeform;
- }
- }
- if (viewField !== undefined) {
- return viewField;
- } else {
- return CollectionViewType.Invalid;
- }
- }
-
- @computed get dataDoc() { return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
- @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; }
-
- active = (): boolean => {
- var isSelected = this.props.isSelected();
- return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
- }
-
- //TODO should this be observable?
- private _isChildActive = false;
- whenActiveChanged = (isActive: boolean) => {
- this._isChildActive = isActive;
- this.props.whenActiveChanged(isActive);
- }
-
- @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); }
-
- @action.bound
- addDocument(doc: Doc): boolean {
- if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
- Doc.GetProto(doc).annotationOn = this.props.Document;
- }
- let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
- let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
- Doc.AddDocToList(targetDataDoc, targetField, doc);
- Doc.GetProto(doc).lastOpened = new DateField;
- return true;
- }
-
- @action.bound
- removeDocument(doc: Doc): boolean {
- let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
- docView && SelectionManager.DeselectDoc(docView);
- //TODO This won't create the field if it doesn't already exist
- let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
- let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
- let value = Cast(targetDataDoc[targetField], listSpec(Doc), []);
- 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);
- PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => {
- if (Doc.AreProtosEqual(annotationOn, FieldValue(Cast(this.dataDoc.extendsDoc, Doc)))) {
- Doc.GetProto(doc).annotationOn = undefined;
- }
- });
-
- if (index !== -1) {
- value.splice(index, 1);
-
- // SelectionManager.DeselectAll()
- ContextMenu.Instance.clearItems();
- return true;
- }
- return false;
- }
-
- // this is called with the document that was dragged and the collection to move it into.
- // if the target collection is the same as this collection, then the move will be allowed.
- // otherwise, the document being moved must be able to be removed from its container before
- // moving it into the target.
- @action.bound
- moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
- if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
- return true;
- }
- return this.removeDocument(doc) ? addDocument(doc) : false;
- }
-
- showIsTagged = () => {
- const children = DocListCast(this.props.Document.data);
- const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
- const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
- if (allTagged) {
- return (
- <img
- id={"google-tags"}
- src={"/assets/google_tags.png"}
- />
- );
- }
- return (null);
- }
-
- render() {
- const props: CollectionRenderProps = {
- addDocument: this.addDocument,
- removeDocument: this.removeDocument,
- moveDocument: this.moveDocument,
- active: this.active,
- whenActiveChanged: this.whenActiveChanged,
- };
- const viewtype = this.collectionViewType;
- return (
- <div id="collectionBaseView"
- style={{
- pointerEvents: this.props.Document.isBackground ? "none" : "all",
- boxShadow: this.props.Document.isBackground || viewtype === CollectionViewType.Linear ? undefined : `#9c9396 ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`
- }}
- className={this.props.className || "collectionView-cont"}
- onContextMenu={this.props.onContextMenu} ref={this.props.contentRef}>
- {this.showIsTagged()}
- {viewtype !== undefined ? this.props.children(viewtype, props) : (null)}
- </div>
- );
- }
-
-}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 6f5abd05b..bcdc9c97e 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,12 +1,13 @@
@import "../../views/globalCssVariables.scss";
-.lm_active .messageCounter{
- color:white;
+.lm_active .messageCounter {
+ color: white;
background: #999999;
}
+
.messageCounter {
- width:18px;
- height:20px;
+ width: 18px;
+ height: 20px;
text-align: center;
border-radius: 20px;
margin-left: 5px;
@@ -18,22 +19,33 @@
.collectiondockingview-container {
width: 100%;
- height:100%;
+ height: 100%;
border-style: solid;
border-width: $COLLECTION_BORDER_WIDTH;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
+
+ .collectionDockingView-dragAsDocument {
+ touch-action: none;
+ }
+
+ .lm_content {
+ background: white;
+ }
+
.lm_controls>li {
opacity: 0.6;
transform: scale(1.2);
}
+
.lm_maximised .lm_controls .lm_maximise {
opacity: 1;
transform: scale(0.8);
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKElEQVR4nGP8////fwYCgImQAgYGBgYWKM2IR81/okwajIpgvsMbVgAwgQYRVakEKQAAAABJRU5ErkJggg==) !important;
}
+
.flexlayout__layout {
left: 0;
top: 0;
@@ -42,17 +54,21 @@
position: absolute;
overflow: hidden;
}
+
.flexlayout__splitter {
background-color: black;
}
+
.flexlayout__splitter:hover {
background-color: #333;
}
+
.flexlayout__splitter_drag {
border-radius: 5px;
background-color: #444;
z-index: 1000;
}
+
.flexlayout__outline_rect {
position: absolute;
cursor: move;
@@ -62,6 +78,7 @@
z-index: 1000;
box-sizing: border-box;
}
+
.flexlayout__outline_rect_edge {
cursor: move;
border: 2px solid #b7d1b5;
@@ -70,12 +87,14 @@
z-index: 1000;
box-sizing: border-box;
}
+
.flexlayout__edge_rect {
position: absolute;
z-index: 1000;
box-shadow: inset 0 0 5px rgba(0, 0, 0, .2);
background-color: lightgray;
}
+
.flexlayout__drag_rect {
position: absolute;
cursor: move;
@@ -94,11 +113,13 @@
padding: 10px;
word-wrap: break-word;
}
+
.flexlayout__tabset {
overflow: hidden;
background-color: #222;
box-sizing: border-box;
}
+
.flexlayout__tab {
overflow: auto;
position: absolute;
@@ -106,6 +127,7 @@
background-color: #222;
color: black;
}
+
.flexlayout__tab_button {
cursor: pointer;
padding: 2px 8px 3px 8px;
@@ -117,28 +139,35 @@
vertical-align: top;
box-sizing: border-box;
}
+
.flexlayout__tab_button--selected {
color: #ddd;
background-color: #222;
}
+
.flexlayout__tab_button--unselected {
color: gray;
}
+
.flexlayout__tab_button_leading {
display: inline-block;
}
+
.flexlayout__tab_button_content {
display: inline-block;
}
+
.flexlayout__tab_button_textbox {
float: left;
border: none;
color: lightgreen;
background-color: #222;
}
+
.flexlayout__tab_button_textbox:focus {
outline: none;
}
+
.flexlayout__tab_button_trailing {
display: inline-block;
margin-left: 5px;
@@ -146,10 +175,12 @@
width: 8px;
height: 8px;
}
+
.flexlayout__tab_button:hover .flexlayout__tab_button_trailing,
.flexlayout__tab_button--selected .flexlayout__tab_button_trailing {
background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
}
+
.flexlayout__tab_button_overflow {
float: left;
width: 20px;
@@ -162,6 +193,7 @@
font-family: Arial, sans-serif;
background: transparent url("../../../../node_modules/flexlayout-react/images/more.png") no-repeat left;
}
+
.flexlayout__tabset_header {
position: absolute;
left: 0;
@@ -172,6 +204,7 @@
/*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/
box-sizing: border-box;
}
+
.flexlayout__tab_header_inner {
position: absolute;
left: 0;
@@ -179,6 +212,7 @@
bottom: 0;
width: 10000px;
}
+
.flexlayout__tab_header_outer {
background-color: black;
position: absolute;
@@ -188,12 +222,15 @@
/*height: 100px;*/
overflow: hidden;
}
+
.flexlayout__tabset-selected {
background-image: linear-gradient(#111, #444);
}
+
.flexlayout__tabset-maximized {
background-image: linear-gradient(#666, #333);
}
+
.flexlayout__tab_toolbar {
position: absolute;
display: flex;
@@ -203,6 +240,7 @@
bottom: 0;
right: 0;
}
+
.flexlayout__tab_toolbar_button-min {
width: 20px;
height: 20px;
@@ -210,6 +248,7 @@
outline-width: 0;
background: transparent url("../../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center;
}
+
.flexlayout__tab_toolbar_button-max {
width: 20px;
height: 20px;
@@ -217,14 +256,18 @@
outline-width: 0;
background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center;
}
+
.flexlayout__popup_menu {}
+
.flexlayout__popup_menu_item {
padding: 2px 10px 2px 10px;
color: #ddd;
}
+
.flexlayout__popup_menu_item:hover {
background-color: #444444;
}
+
.flexlayout__popup_menu_container {
box-shadow: inset 0 0 5px rgba(0, 0, 0, .15);
border: 1px solid #555;
@@ -233,33 +276,39 @@
position: absolute;
z-index: 1000;
}
+
.flexlayout__border_top {
background-color: black;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
overflow: hidden;
}
+
.flexlayout__border_bottom {
background-color: black;
border-top: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
+
.flexlayout__border_left {
background-color: black;
border-right: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
+
.flexlayout__border_right {
background-color: black;
border-left: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
+
.flexlayout__border_inner_bottom {
display: flex;
}
+
.flexlayout__border_inner_left {
position: absolute;
white-space: nowrap;
@@ -267,6 +316,7 @@
transform-origin: top right;
transform: rotate(-90deg);
}
+
.flexlayout__border_inner_right {
position: absolute;
white-space: nowrap;
@@ -274,6 +324,7 @@
transform-origin: top left;
transform: rotate(90deg);
}
+
.flexlayout__border_button {
background-color: #222;
color: white;
@@ -285,29 +336,36 @@
vertical-align: top;
box-sizing: border-box;
}
+
.flexlayout__border_button--selected {
color: #ddd;
background-color: #222;
}
+
.flexlayout__border_button--unselected {
color: gray;
}
+
.flexlayout__border_button_leading {
float: left;
display: inline;
}
+
.flexlayout__border_button_content {
display: inline-block;
}
+
.flexlayout__border_button_textbox {
float: left;
border: none;
color: green;
background-color: #ddd;
}
+
.flexlayout__border_button_textbox:focus {
outline: none;
}
+
.flexlayout__border_button_trailing {
display: inline-block;
margin-left: 5px;
@@ -315,10 +373,12 @@
width: 8px;
height: 8px;
}
+
.flexlayout__border_button:hover .flexlayout__border_button_trailing,
.flexlayout__border_button--selected .flexlayout__border_button_trailing {
background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center;
}
+
.flexlayout__border_toolbar_left {
position: absolute;
display: flex;
@@ -328,6 +388,7 @@
left: 0;
right: 0;
}
+
.flexlayout__border_toolbar_right {
position: absolute;
display: flex;
@@ -337,6 +398,7 @@
left: 0;
right: 0;
}
+
.flexlayout__border_toolbar_top {
position: absolute;
display: flex;
@@ -346,6 +408,7 @@
bottom: 0;
right: 0;
}
+
.flexlayout__border_toolbar_bottom {
position: absolute;
display: flex;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1f78c8c97..3ff99b9f4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -426,15 +426,17 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
tab.setActive(true);
};
- ReactDOM.render(<span title="Drag as document" onPointerDown={
- e => {
- e.preventDefault();
- e.stopPropagation();
- DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
- handlers: { dragComplete: emptyFunction },
- hideSource: false
- });
- }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
+ ReactDOM.render(<span title="Drag as document"
+ className="collectionDockingView-dragAsDocument"
+ onPointerDown={
+ e => {
+ e.preventDefault();
+ e.stopPropagation();
+ DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, {
+ handlers: { dragComplete: emptyFunction },
+ hideSource: false
+ });
+ }}><FontAwesomeIcon icon="file" size="lg" /></span>, dragSpan);
ReactDOM.render(<ButtonSelector Document={doc} Stack={stack} />, gearSpan);
// ReactDOM.render(<ParentDocSelector Document={doc} addDocTab={(doc, data, where) => {
// where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc);
@@ -615,19 +617,20 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
}
- panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth;
+ get layoutDoc() { return this._document && Doc.Layout(this._document); }
+ 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._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0;
- nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0;
+ 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;
contentScaling = () => {
- if (this._document!.type === DocumentType.PDF) {
- if ((this._document && this._document.fitWidth) ||
- this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) {
- return this._panelWidth / NumCast(this._document!.nativeWidth);
+ if (this.layoutDoc!.type === DocumentType.PDF) {
+ if ((this.layoutDoc && this.layoutDoc.fitWidth) ||
+ this._panelHeight / NumCast(this.layoutDoc!.nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!.nativeWidth)) {
+ return this._panelWidth / NumCast(this.layoutDoc!.nativeWidth);
} else {
- return this._panelHeight / NumCast(this._document!.nativeHeight);
+ return this._panelHeight / NumCast(this.layoutDoc!.nativeHeight);
}
}
const nativeH = this.nativeHeight();
@@ -645,7 +648,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
return Transform.Identity();
}
- get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
addDocTab = (doc: Doc, dataDoc: Opt<Doc>, location: string) => {
SelectionManager.DeselectAll();
@@ -690,11 +693,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
render() {
- return (!this._isActive || !this._document) ? (null) :
+ return (!this._isActive || !this.layoutDoc) ? (null) :
(<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
style={{
transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
- height: this._document && this._document.fitWidth ? undefined : "100%"
+ height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%"
}}>
{this.docView}
</div >);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 1709b9c99..52ebfafd3 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -4,12 +4,12 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, WidthSym } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
+import Measure from "react-measure";
+import { Doc } from "../../../new_fields/Doc";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils, numberRange } from "../../../Utils";
+import { StrCast } from "../../../new_fields/Types";
+import { numberRange } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
@@ -20,7 +20,7 @@ import { anchorPoints, Flyout } from "../DocumentDecorations";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
import "./CollectionStackingView.scss";
-import Measure from "react-measure";
+import { undo } from "prosemirror-history";
library.add(faPalette);
@@ -41,27 +41,28 @@ interface CMVFieldRowProps {
export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
@observable private _background = "inherit";
@observable private _createAliasSelected: boolean = false;
+ @observable private _collapsed: boolean = false;
+ @observable private _headingsHack: number = 1;
+ @observable private _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
+ @observable private _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
- private _dropRef: HTMLDivElement | null = null;
- private dropDisposer?: DragManager.DragDropDisposer;
+ private _dropDisposer?: DragManager.DragDropDisposer;
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
private _contRef: React.RefObject<HTMLDivElement> = React.createRef();
private _sensitivity: number = 16;
+ private _counter: number = 0;
- @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
- @observable _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
createRowDropRef = (ele: HTMLDivElement | null) => {
- this._dropRef = ele;
- this.dropDisposer && this.dropDisposer();
+ this._dropDisposer && this._dropDisposer();
if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
+ this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.rowDrop.bind(this) } });
}
}
getTrueHeight = () => {
- if (this.collapsed) {
+ if (this._collapsed) {
this.props.setDocHeight(this._heading, 20);
} else {
let rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
@@ -75,14 +76,11 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
rowDrop = action((e: Event, de: DragManager.DropEvent) => {
this._createAliasSelected = false;
if (de.data instanceof DragManager.DocumentDragData) {
+ (this.props.parent.Document.dropConverter instanceof ScriptField) &&
+ this.props.parent.Document.dropConverter.script.run({ dragData: de.data });
let key = StrCast(this.props.parent.props.Document.sectionFilter);
let castedValue = this.getValue(this._heading);
- if (castedValue) {
- de.data.droppedDocuments.forEach(d => d[key] = castedValue);
- }
- else {
- de.data.droppedDocuments.forEach(d => d[key] = undefined);
- }
+ de.data.droppedDocuments.forEach(d => d[key] = castedValue);
this.props.parent.drop(e, de);
e.stopPropagation();
}
@@ -90,15 +88,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
getValue = (value: string): any => {
let parsed = parseInt(value);
- if (!isNaN(parsed)) {
- return parsed;
- }
- if (value.toLowerCase().indexOf("true") > -1) {
- return true;
- }
- if (value.toLowerCase().indexOf("false") > -1) {
- return false;
- }
+ if (!isNaN(parsed)) return parsed;
+ if (value.toLowerCase().indexOf("true") > -1) return true;
+ if (value.toLowerCase().indexOf("false") > -1) return false;
return value;
}
@@ -132,12 +124,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
}
}
- @action
- pointerEnteredRow = () => {
- if (SelectionManager.GetIsDragging()) {
- this._background = "#b4b4b4";
- }
- }
+ pointerEnteredRow = action(() => SelectionManager.GetIsDragging() && (this._background = "#b4b4b4"));
@action
pointerLeaveRow = () => {
@@ -155,8 +142,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
return this.props.parent.props.addDocument(newDoc);
}
- @action
- deleteRow = () => {
+ deleteRow = undoBatch(action(() => {
this._createAliasSelected = false;
let key = StrCast(this.props.parent.props.Document.sectionFilter);
this.props.docList.forEach(d => d[key] = undefined);
@@ -164,7 +150,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
let index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject);
this.props.parent.sectionHeaders.splice(index, 1);
}
- }
+ }));
@action
collapseSection = () => {
@@ -206,6 +192,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
document.removeEventListener("pointerup", this.pointerUp);
}
+ @action
headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
@@ -252,48 +239,31 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
);
}
- @action
- toggleAlias = () => {
- this._createAliasSelected = true;
- }
+ toggleAlias = action(() => this._createAliasSelected = true);
+ toggleVisibility = action(() => this._collapsed = !this._collapsed);
renderMenu = () => {
let selected = this._createAliasSelected;
- return (
- <div className="collectionStackingView-optionPicker">
- <div className="optionOptions">
- <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
- </div>
+ return (<div className="collectionStackingView-optionPicker">
+ <div className="optionOptions">
+ <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
+ <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.deleteRow}>Delete</div>
</div>
- );
+ </div>);
}
- @observable private collapsed: boolean = false;
-
- private toggleVisibility = action(() => {
- this.collapsed = !this.collapsed;
- });
-
- @observable _headingsHack: number = 1;
-
handleResize = (size: any) => {
- this.counter += 1;
- if (this.counter !== 1) {
+ if (++this._counter !== 1) {
this.getTrueHeight();
}
}
- private counter: number = 0;
render() {
- let cols = this.props.rows();
let 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))));
let key = StrCast(this.props.parent.props.Document.sectionFilter);
- let templatecols = "";
- let headings = this.props.headings();
let heading = this._heading;
let style = this.props.parent;
- let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
let evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
let headerEditableViewProps = {
GetValue: () => evContents,
@@ -314,45 +284,46 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
toggle: this.toggleVisibility,
color: this._color
};
- let headingView = this.props.headingObject ?
- <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
- <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
- <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
- title={evContents === `NO ${key.toUpperCase()} VALUE` ?
- `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",
- color: "grey"
- }}>
- {<EditableView {...headerEditableViewProps} />}
- {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
- <div className="collectionStackingView-sectionColor">
- <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
- <button className="collectionStackingView-sectionColorButton">
- <FontAwesomeIcon icon="palette" size="lg" />
- </button>
- </ Flyout >
- </div>
- }
- {evContents === `NO ${key.toUpperCase()} VALUE` ?
- (null) :
- <button className="collectionStackingView-sectionDelete" onClick={this.deleteRow}>
- <FontAwesomeIcon icon="trash" size="lg" />
- </button>}
- {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
- <div className="collectionStackingView-sectionOptions">
- <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
- <button className="collectionStackingView-sectionOptionButton">
- <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
- </button>
- </Flyout>
- </div>
- }
- </div>
- </div > : (null);
+ let headingView = this.props.parent.props.Document.miniHeaders ?
+ <div className="collectionStackingView-miniHeader" style={{ width: "100%" }}>
+ {<EditableView {...headerEditableViewProps} />}
+ </div> :
+ this.props.headingObject ?
+ <div className="collectionStackingView-sectionHeader" ref={this._headerRef} >
+ <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
+ title={evContents === `NO ${key.toUpperCase()} VALUE` ?
+ `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",
+ color: "grey"
+ }}>
+ {<EditableView {...headerEditableViewProps} />}
+ {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ <div className="collectionStackingView-sectionColor">
+ <Flyout anchorPoint={anchorPoints.CENTER_RIGHT} content={this.renderColorPicker()}>
+ <button className="collectionStackingView-sectionColorButton">
+ <FontAwesomeIcon icon="palette" size="lg" />
+ </button>
+ </ Flyout >
+ </div>
+ }
+ <button className="collectionStackingView-sectionDelete" onClick={this.collapseSection}>
+ <FontAwesomeIcon icon={this._collapsed ? "chevron-down" : "chevron-up"} size="lg" />
+ </button>
+ {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ <div className="collectionStackingView-sectionOptions">
+ <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
+ <button className="collectionStackingView-sectionOptionButton">
+ <FontAwesomeIcon icon="ellipsis-v" size="lg" />
+ </button>
+ </Flyout>
+ </div>
+ }
+ </div>
+ </div > : (null);
const background = this._background; //to account for observables in Measure
- const collapsed = this.collapsed;
+ const collapsed = this._collapsed;
let chromeStatus = this.props.parent.props.Document.chromeStatus;
return (
<Measure offset onResize={this.handleResize}>
@@ -367,7 +338,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
>
{headingView}
{collapsed ? (null) :
- < div >
+ < div style={{ position: "relative" }}>
<div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`}
ref={this._contRef}
style={{
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 79c032723..54a36f691 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -144,7 +144,6 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
Document: this.props.rowProps.original,
DataDoc: this.props.rowProps.original,
fieldKey: this.props.rowProps.column.id as string,
- fieldExt: "",
ruleProvider: undefined,
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index 39abc41ec..274c8b6d1 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -229,7 +229,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
<div className="collectionSchema-row-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
<ReactTableDefaults.TrComponent>
<div className="row-dragger">
- <div className="row-option" onClick={() => this.props.removeDoc(this.props.rowInfo.original)}><FontAwesomeIcon icon="trash" size="sm" /></div>
+ <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
<div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
</div>
{children}
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index e0cedc210..cb95dcbbc 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -10,6 +10,7 @@
top: 0;
width: 100%;
height: 100%;
+ margin-top: 0;
transition: top 0.5s;
display: flex;
justify-content: space-between;
@@ -38,30 +39,6 @@
}
}
-.collectionSchemaView-previewRegion {
- position: relative;
- background: $light-color;
- height: auto !important;
-
- .collectionSchemaView-previewDoc {
- height: 100%;
- width: 100%;
- position: absolute;
- }
-
- .collectionSchemaView-input {
- position: absolute;
- max-width: 150px;
- width: 100%;
- bottom: 0px;
- }
-
- .documentView-node:first-child {
- position: relative;
- background: $light-color;
- }
-}
-
.ReactTable {
width: 100%;
background: white;
@@ -470,7 +447,7 @@ button.add-column {
overflow: visible;
}
-.sub {
+.reactTable-sub {
padding: 10px 30px;
background-color: rgb(252, 252, 252);
width: calc(100% - 50px);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 3218f630a..ebd47fd19 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,37 +1,34 @@
import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCog, faPlus, faTable, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons';
+import { faCog, faPlus, faSortDown, faSortUp, faTable } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace, untracked } from "mobx";
+import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
-import ReactTable, { CellInfo, ComponentPropsGetterR, Column, RowInfo, ResizedChangeFunction, Resize } from "react-table";
+import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnOne, returnEmptyString } from "../../../Utils";
import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-import { Docs, DocumentOptions } from "../../documents/Documents";
+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 { DragManager } from "../../util/DragManager";
-import { CompileScript, ts, Transformer } from "../../util/Scripting";
+import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
-import { DocumentView } from "../nodes/DocumentView";
+import { CellProps, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CollectionSchemaAddColumnHeader, CollectionSchemaHeader } from "./CollectionSchemaHeaders";
+import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionView } from "./CollectionView";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders";
-import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells";
-import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
-import { DocumentType } from "../../documents/DocumentTypes";
-
+import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
library.add(faCog, faPlus, faSortUp, faSortDown);
library.add(faTable);
@@ -73,20 +70,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
super.CreateDropTarget(ele);
}
- isFocused = (doc: Doc): boolean => {
- if (!this.props.isSelected()) return false;
- return doc === this._focusedTable;
- }
+ isFocused = (doc: Doc): boolean => this.props.isSelected() && doc === this._focusedTable;
- @action
- setFocused = (doc: Doc): void => {
- this._focusedTable = doc;
- }
+ @action setFocused = (doc: Doc) => this._focusedTable = doc;
- @action
- setPreviewDoc = (doc: Doc): void => {
- this.previewDoc = doc;
- }
+ @action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
+
+ @undoBatch
+ @action setPreviewScript = (script: string) => this.previewScript = script
//toggles preview side-panel of schema
@action
@@ -128,17 +119,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
}
- onWheel = (e: React.WheelEvent): void => {
- if (this.props.active()) {
- e.stopPropagation();
- }
- }
-
@computed
get previewDocument(): Doc | undefined {
- let selected = this.previewDoc;
- let pdc = selected ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(selected[this.previewScript], Doc)) : selected) : undefined;
- return pdc;
+ return this.previewDoc ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(this.previewDoc[this.previewScript], Doc)) : this.previewDoc) : undefined;
}
getPreviewTransform = (): Transform => {
@@ -155,7 +138,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
get previewPanel() {
let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
return <div ref={this.createTarget}>
- <CollectionSchemaPreview
+ <ContentFittingDocumentView
Document={layoutDoc}
DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
childDocs={this.childDocs}
@@ -179,63 +162,51 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>;
}
- @undoBatch
- @action
- setPreviewScript = (script: string) => {
- this.previewScript = script;
- }
-
@computed
get schemaTable() {
- return (
- <SchemaTable
- Document={this.props.Document}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- childDocs={this.childDocs}
- CollectionView={this.props.CollectionView}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- fieldKey={this.props.fieldKey}
- renderDepth={this.props.renderDepth}
- moveDocument={this.props.moveDocument}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- active={this.props.active}
- onDrop={this.onDrop}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- isSelected={this.props.isSelected}
- isFocused={this.isFocused}
- setFocused={this.setFocused}
- setPreviewDoc={this.setPreviewDoc}
- deleteDocument={this.props.removeDocument}
- dataDoc={this.props.DataDoc}
- />
- );
+ return <SchemaTable
+ Document={this.props.Document}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ childDocs={this.childDocs}
+ CollectionView={this.props.CollectionView}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ fieldKey={this.props.fieldKey}
+ renderDepth={this.props.renderDepth}
+ moveDocument={this.props.moveDocument}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ active={this.props.active}
+ onDrop={this.onDrop}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ isSelected={this.props.isSelected}
+ isFocused={this.isFocused}
+ setFocused={this.setFocused}
+ setPreviewDoc={this.setPreviewDoc}
+ deleteDocument={this.props.removeDocument}
+ addDocument={this.props.addDocument}
+ dataDoc={this.props.DataDoc}
+ />;
}
@computed
public get schemaToolbar() {
- return (
- <div className="collectionSchemaView-toolbar">
- <div className="collectionSchemaView-toolbar-item">
- <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />Show Preview</div>
- </div>
+ return <div className="collectionSchemaView-toolbar">
+ <div className="collectionSchemaView-toolbar-item">
+ <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />Show Preview</div>
</div>
- );
+ </div>;
}
render() {
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
- return (
- <div className="collectionSchemaView-container" style={{ height: "100%", marginTop: "0", }}>
- <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
- {this.schemaTable}
- </div>
- {this.dividerDragger}
- {!this.previewWidth() ? (null) : this.previewPanel}
+ return <div className="collectionSchemaView-container">
+ <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
+ {this.schemaTable}
</div>
- );
+ {this.dividerDragger}
+ {!this.previewWidth() ? (null) : this.previewPanel}
+ </div>;
}
}
@@ -251,6 +222,7 @@ export interface SchemaTableProps {
fieldKey: string;
renderDepth: number;
deleteDocument: (document: Doc) => boolean;
+ addDocument: (document: Doc) => boolean;
moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
active: () => boolean;
@@ -307,11 +279,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return resized;
}, [] as { id: string, value: number }[]);
}
- @computed get sorted(): { id: string, desc: boolean }[] {
+ @computed get sorted(): SortingRule[] {
return this.columns.reduce((sorted, shf) => {
shf.desc && sorted.push({ id: shf.heading, desc: shf.desc });
return sorted;
- }, [] as { id: string, desc: boolean }[]);
+ }, [] as SortingRule[]);
}
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@@ -321,11 +293,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let tableIsFocused = this.props.isFocused(this.props.Document);
let focusedRow = this._focusedCell.row;
let focusedCol = this._focusedCell.col;
- let isEditable = !this._headerIsEditing;// && this.props.isSelected();
+ let isEditable = !this._headerIsEditing;
- let children = this.childDocs;
-
- if (children.reduce((found, doc) => found || doc.type === "collection", false)) {
+ if (this.childDocs.reduce((found, doc) => found || doc.type === "collection", false)) {
columns.push(
{
expander: true,
@@ -433,26 +403,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
}
- tableRemoveDoc = (document: Doc): boolean => {
-
- let children = this.childDocs;
- if (children.indexOf(document) !== -1) {
- children.splice(children.indexOf(document), 1);
- this.childDocs = children;
- return true;
- }
- return false;
- }
-
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
- const that = this;
- if (!rowInfo) {
- return {};
- }
- return {
+ return !rowInfo ? {} : {
ScreenToLocalTransform: this.props.ScreenToLocalTransform,
addDoc: this.tableAddDoc,
- removeDoc: this.tableRemoveDoc,
+ removeDoc: this.props.deleteDocument,
rowInfo,
rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document),
textWrapRow: this.toggleTextWrapRow,
@@ -461,14 +416,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
- if (!rowInfo) return {};
- if (!column) return {};
+ if (!rowInfo || column) return {};
let row = rowInfo.index;
//@ts-ignore
let col = this.columns.map(c => c.heading).indexOf(column!.id);
let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
- let isEditing = this.props.isFocused(this.props.Document) && this._cellIsEditing;
// TODO: editing border doesn't work :(
return {
style: {
@@ -478,113 +431,68 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- onExpandCollection = (collection: Doc): void => {
- this._openCollections.push(collection[Id]);
- }
-
- @action
onCloseCollection = (collection: Doc): void => {
let index = this._openCollections.findIndex(col => col === collection[Id]);
if (index > -1) this._openCollections.splice(index, 1);
}
- @action
- setCellIsEditing = (isEditing: boolean): void => {
- this._cellIsEditing = isEditing;
- }
-
- @action
- setHeaderIsEditing = (isEditing: boolean): void => {
- this._headerIsEditing = isEditing;
- }
+ @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]);
+ @action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
+ @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
onPointerDown = (e: React.PointerEvent): void => {
this.props.setFocused(this.props.Document);
- if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
- if (this.props.isSelected()) e.stopPropagation();
- }
- }
-
- onWheel = (e: React.WheelEvent): void => {
- if (this.props.active()) {
+ if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected()) {
e.stopPropagation();
}
}
+ @action
onKeyDown = (e: KeyboardEvent): void => {
if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) {
let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
- this.changeFocusedCellByDirection(direction);
+ this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
- let children = this.childDocs;
- const pdoc = FieldValue(children[this._focusedCell.row]);
+ const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
pdoc && this.props.setPreviewDoc(pdoc);
}
}
- @action
- changeFocusedCellByDirection = (direction: string): void => {
- let children = this.childDocs;
+ changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
switch (direction) {
- case "tab":
- if (this._focusedCell.col + 1 === this.columns.length && this._focusedCell.row + 1 === children.length) {
- this._focusedCell = { row: 0, col: 0 };
- } else if (this._focusedCell.col + 1 === this.columns.length) {
- this._focusedCell = { row: this._focusedCell.row + 1, col: 0 };
- } else {
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 };
- }
- break;
- case "right":
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 === this.columns.length ? this._focusedCell.col : this._focusedCell.col + 1 };
- break;
- case "left":
- this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col === 0 ? this._focusedCell.col : this._focusedCell.col - 1 };
- break;
- case "up":
- this._focusedCell = { row: this._focusedCell.row === 0 ? this._focusedCell.row : this._focusedCell.row - 1, col: this._focusedCell.col };
- break;
- case "down":
- this._focusedCell = { row: this._focusedCell.row + 1 === children.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col };
- break;
+ case "tab": return { row: (curRow + 1 === this.childDocs.length ? 0 : curRow + 1), col: curCol + 1 === this.columns.length ? 0 : curCol + 1 };
+ case "right": return { row: curRow, col: curCol + 1 === this.columns.length ? curCol : curCol + 1 };
+ case "left": return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+ case "up": return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+ case "down": return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
}
+ return this._focusedCell;
}
@action
changeFocusedCellByIndex = (row: number, col: number): void => {
- this._focusedCell = { row: row, col: col };
+ if (this._focusedCell.row !== row || this._focusedCell.col !== col) {
+ this._focusedCell = { row: row, col: col };
+ }
this.props.setFocused(this.props.Document);
}
@undoBatch
createRow = () => {
- let children = this.childDocs;
-
- let newDoc = Docs.Create.TextDocument({ width: 100, height: 30 });
- let proto = Doc.GetProto(newDoc);
- proto.title = "";
- children.push(newDoc);
-
- this.childDocs = children;
+ let newDoc = Docs.Create.TextDocument({ title: "", width: 100, height: 30 });
+ this.props.addDocument(newDoc);
}
@undoBatch
@action
createColumn = () => {
let index = 0;
- let columns = this.columns;
- let found = columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
- if (!found) {
- columns.push(new SchemaHeaderField("New field", "#f1efeb"));
- this.columns = columns;
- return;
- }
+ let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
while (found) {
index++;
- found = columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
+ found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
}
- columns.push(new SchemaHeaderField("New field (" + index + ")", "#f1efeb"));
- this.columns = columns;
+ this.columns.push(new SchemaHeaderField(`New field ${index ? "(" + index + ")" : ""}`, "#f1efeb"));
}
@undoBatch
@@ -677,9 +585,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- setColumns = (columns: SchemaHeaderField[]) => {
- this.columns = columns;
- }
+ setColumns = (columns: SchemaHeaderField[]) => this.columns = columns
@undoBatch
reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
@@ -725,11 +631,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let textWrapped = this.textWrappedRows;
let index = textWrapped.findIndex(id => doc[Id] === id);
- if (index > -1) {
- textWrapped.splice(index, 1);
- } else {
- textWrapped.push(doc[Id]);
- }
+ index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
this.textWrappedRows = textWrapped;
}
@@ -759,13 +661,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
expanded={expanded}
resized={this.resized}
onResizedChange={this.onResizedChange}
- SubComponent={hasCollectionChild ?
- row => {
- if (row.original.type === "collection") {
- return <div className="sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>;
- }
- }
- : undefined}
+ SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
+ <div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>}
/>;
}
@@ -881,145 +778,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
render() {
- return (
- <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
- onDrop={(e: React.DragEvent) => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
- {this.reactTable}
- <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
- </div>
- );
- }
-}
-
-
-interface CollectionSchemaPreviewProps {
- Document?: Doc;
- DataDocument?: Doc;
- childDocs?: Doc[];
- renderDepth: number;
- fitToBox?: boolean;
- PanelWidth: () => number;
- PanelHeight: () => number;
- ruleProvider: Doc | undefined;
- focus?: (doc: Doc) => void;
- showOverlays?: (doc: Doc) => { title?: string, caption?: string };
- CollectionView?: CollectionView;
- CollectionDoc?: Doc;
- onClick?: ScriptField;
- getTransform: () => Transform;
- addDocument: (document: Doc) => boolean;
- moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
- removeDocument: (document: Doc) => boolean;
- active: () => boolean;
- whenActiveChanged: (isActive: boolean) => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
- pinToPres: (document: Doc) => void;
- setPreviewScript: (script: string) => void;
- previewScript?: string;
-}
-
-@observer
-export class CollectionSchemaPreview extends React.Component<CollectionSchemaPreviewProps>{
- private dropDisposer?: DragManager.DragDropDisposer;
- _mainCont?: HTMLDivElement;
- private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.PanelWidth()); }
- private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.PanelHeight()); }
- private contentScaling = () => {
- let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth());
- if (wscale * this.nativeHeight > this.props.PanelHeight()) {
- return this.props.PanelHeight() / (this.nativeHeight ? this.nativeHeight : this.props.PanelHeight());
- }
- return wscale;
- }
- protected createDropTarget = (ele: HTMLDivElement) => {
- }
- private createTarget = (ele: HTMLDivElement) => {
- this._mainCont = ele;
- this.dropDisposer && this.dropDisposer();
- if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
- }
- }
-
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.data instanceof DragManager.DocumentDragData) {
- this.props.childDocs && this.props.childDocs.map(otherdoc => {
- let target = Doc.GetProto(otherdoc);
- let layoutNative = Doc.MakeTitled("layoutNative");
- layoutNative.layout = ComputedField.MakeFunction("this.image_data[0]");
- target.layoutNative = layoutNative;
- target.layoutCUstom = target.layout = Doc.MakeDelegate(de.data.draggedDocuments[0]);
- });
- e.stopPropagation();
- }
- return true;
- }
- private PanelWidth = () => this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth();
- private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight();
- private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling());
- get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
- @action
- onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.props.setPreviewScript(e.currentTarget.value);
- }
- @computed get borderRounding() {
- let br = StrCast(this.props.Document!.borderRounding);
- if (br.endsWith("%")) {
- let percent = Number(br.substr(0, br.length - 1)) / 100;
- let nativeDim = Math.min(NumCast(this.props.Document!.nativeWidth), NumCast(this.props.Document!.nativeHeight));
- let minDim = percent * (nativeDim ? nativeDim : Math.min(this.PanelWidth(), this.PanelHeight()));
- return minDim;
- }
- return undefined;
- }
-
-
- render() {
- let input = this.props.previewScript === undefined ? (null) :
- <div ref={this.createTarget}><input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange}
- style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} /></div>;
- return (<div className="collectionSchemaView-previewRegion"
- style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
- {!this.props.Document || !this.props.PanelWidth ? (null) : (
- <div className="collectionSchemaView-previewDoc"
- style={{
- transform: `translate(${this.centeringOffset}px, 0px)`,
- borderRadius: this.borderRounding,
- display: "inline",
- height: this.props.PanelHeight(),
- width: this.props.PanelWidth()
- }}>
- <DocumentView {...this.props}
- DataDoc={this.props.DataDocument}
- Document={this.props.Document}
- fitToBox={this.props.fitToBox}
- onClick={this.props.onClick}
- ruleProvider={this.props.ruleProvider}
- showOverlays={this.props.showOverlays}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- whenActiveChanged={this.props.whenActiveChanged}
- ContainingCollectionView={this.props.CollectionView}
- ContainingCollectionDoc={this.props.CollectionDoc}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- parentActive={this.props.active}
- ScreenToLocalTransform={this.getTransform}
- renderDepth={this.props.renderDepth + 1}
- ContentScaling={this.contentScaling}
- PanelWidth={this.PanelWidth}
- PanelHeight={this.PanelHeight}
- focus={this.props.focus || emptyFunction}
- backgroundColor={returnEmptyString}
- bringToFront={emptyFunction}
- zoomToScale={emptyFunction}
- getScale={returnOne}
- />
- </div>)}
- {input}
- </div>);
+ return <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
+ {this.reactTable}
+ <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index b31f0b8e3..29178b909 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -21,6 +21,10 @@
overflow-y: auto;
flex-wrap: wrap;
transition: top .5s;
+ >div {
+ position: relative;
+ display: block;
+ }
.collectionSchemaView-previewDoc {
height: 100%;
@@ -100,6 +104,7 @@
grid-column-end: span 1;
height: 100%;
margin: auto;
+ display: inline-grid;
}
.collectionStackingView-masonrySection {
@@ -119,7 +124,38 @@
background: red;
}
}
-
+ .collectionStackingView-miniHeader {
+ width: 100%;
+ .editableView-container-editing-oneLine {
+ min-height: 20px;
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ }
+ span::before , span::after{
+ content: "";
+ width: 50%;
+ border-top: dashed gray 1px;
+ position: relative;
+ display: inline-block;
+ }
+ span::before {
+ margin-right: 10px;
+ }
+ span::after{
+ margin-left: 10px;
+ }
+ span {
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ overflow: visible;
+ width: 100%;
+ display: flex;
+ color:gray;
+ align-items: center;
+ }
+ }
.collectionStackingView-sectionHeader {
text-align: center;
margin-left: 2px;
@@ -221,7 +257,6 @@
}
.collectionStackingView-optionPicker {
- width: 78px;
.optionOptions {
display: inline;
@@ -229,10 +264,10 @@
.optionPicker {
cursor: pointer;
- width: 20px;
height: 20px;
border-radius: 10px;
margin: 3px;
+ width:max-content;
&.active {
color: red;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a0dbeeb93..be3bfca0a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -16,7 +16,7 @@ import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { EditableView } from "../EditableView";
-import { CollectionSchemaPreview } from "./CollectionSchemaView";
+import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
@@ -44,12 +44,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
- @computed get showAddAGroup() { return (this.sectionFilter && this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled')); }
+ @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document.chromeStatus !== 'view-mode' && this.props.Document.chromeStatus !== 'disabled')); }
@computed get columnWidth() {
return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin,
this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250));
}
- @computed get NodeWidth() { return this.props.PanelWidth(); }
+ @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
childDocHeight(child: Doc) { return this.getDocHeight(Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, child).layout); }
@@ -57,15 +57,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
this._docXfs.length = 0;
return docs.map((d, i) => {
let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d);
- let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
- let height = () => this.getDocHeight(pair.layout);
+ let layoutDoc = pair.layout ? Doc.Layout(pair.layout) : d;
+ let width = () => Math.min(layoutDoc.nativeWidth && !layoutDoc.ignoreAspect && !this.props.Document.fillColumn ? layoutDoc[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns);
+ let height = () => this.getDocHeight(layoutDoc);
let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(pair.layout!, dref.current!);
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
let style = this.isStackingView ? { width: width(), margin: "auto", 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(pair.layout as Doc, pair.data, dxf, width)}
+ {this.getDisplayDoc(pair.layout || d, pair.data, dxf, width)}
</div>;
});
}
@@ -74,12 +75,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
this._heightMap.set(key, sectionHeight);
}
- get layoutDoc() {
- // if this document's layout field contains a document (ie, a rendering template), then we will use that
- // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string.
- return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document;
- }
-
get Sections() {
if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
@@ -110,23 +105,30 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
componentDidMount() {
- // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported).
+ super.componentDidMount();
this._heightDisposer = reaction(() => {
- if (BoolCast(this.props.Document.autoHeight)) {
+ if (this.props.Document.autoHeight) {
let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]);
if (this.isStackingView) {
- return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght,
- (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin)), 0);
+ let res = this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => {
+ let r1 = Math.max(maxHght,
+ (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => {
+ let val = height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap);
+ return val;
+ }, this.yMargin));
+ return r1;
+ }, 0);
+ return res;
} else {
let sum = Array.from(this._heightMap.values()).reduce((acc: number, curr: number) => acc += curr, 0);
- return this.props.ContentScaling() * (sum + (this.Sections.size ? 85 : -22));
+ return this.props.ContentScaling() * (sum + (this.Sections.size ? (this.props.Document.miniHeaders ? 20 : 85) : -15));
}
}
return -1;
},
(hgt: number) => {
let doc = hgt === -1 ? undefined : this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
- doc && (doc.height = hgt);
+ doc && hgt > 0 && (Doc.Layout(doc).height = hgt);
},
{ fireImmediately: true }
);
@@ -138,6 +140,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
);
}
componentWillUnmount() {
+ super.componentWillUnmount();
this._heightDisposer && this._heightDisposer();
this._sectionFilterDisposer && this._sectionFilterDisposer();
}
@@ -158,17 +161,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
@computed get onClickHandler() { return ScriptCast(this.Document.onChildClick); }
- getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
- let height = () => this.getDocHeight(layoutDoc);
+ getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
+ let layoutDoc = Doc.Layout(doc);
+ let height = () => this.getDocHeight(doc);
let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
- return <CollectionSchemaPreview
- Document={layoutDoc}
+ return <ContentFittingDocumentView
+ Document={doc}
DataDocument={dataDoc}
showOverlays={this.overlays}
renderDepth={this.props.renderDepth}
ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider}
fitToBox={this.props.fitToBox}
- onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler}
+ onClick={layoutDoc.isTemplateDoc ? this.onClickHandler : this.onChildClickHandler}
PanelWidth={width}
PanelHeight={height}
getTransform={finalDxf}
@@ -184,19 +188,21 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
pinToPres={this.props.pinToPres}
setPreviewScript={emptyFunction}
previewScript={undefined}>
- </CollectionSchemaPreview>;
+ </ContentFittingDocumentView>;
}
getDocHeight(d?: Doc) {
if (!d) return 0;
- let nw = NumCast(d.nativeWidth);
- let nh = NumCast(d.nativeHeight);
+ let layoutDoc = Doc.Layout(d);
+ let nw = NumCast(layoutDoc.nativeWidth);
+ let nh = NumCast(layoutDoc.nativeHeight);
let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
- if (!d.ignoreAspect && !d.fitWidth && nw && nh) {
+ if (!layoutDoc.ignoreAspect && !layoutDoc.fitWidth && nw && nh) {
let aspect = nw && nh ? nh / nw : 1;
- if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid);
+ if (!(d.nativeWidth && !layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
- return d.fitWidth ? !d.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin : Math.min(wid * NumCast(d.scrollHeight, NumCast(d.nativeHeight)) / NumCast(d.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : d[HeightSym]();
+ return layoutDoc.fitWidth ? !layoutDoc.nativeHeight ? this.props.PanelHeight() - 2 * this.yMargin :
+ Math.min(wid * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, 1), this.props.PanelHeight() - 2 * this.yMargin) : layoutDoc[HeightSym]();
}
columnDividerDown = (e: React.PointerEvent) => {
@@ -212,7 +218,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
let delta = dragPos - this._columnStart;
this._columnStart = dragPos;
- this.layoutDoc.columnWidth = this.columnWidth + delta;
+ this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
}
@action
@@ -356,7 +362,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
onToggle = (checked: Boolean) => {
- this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus = checked ? "collapsed" : "view-mode");
+ this.props.Document.chromeStatus = checked ? "collapsed" : "view-mode";
}
onContextMenu = (e: React.MouseEvent): void => {
@@ -381,7 +387,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
SetValue: this.addGroup,
contents: "+ ADD A GROUP"
};
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
if (this.sectionFilter) {
let entries = Array.from(this.Sections.entries());
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7e54b0f29..b9d334b10 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.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, trace } from "mobx";
+import { action, observable, trace, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
@@ -204,7 +204,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
document.removeEventListener("pointerup", this.pointerUp);
document.addEventListener("pointerup", this.pointerUp);
}
- this._createAliasSelected = false;
+ runInAction(() => this._createAliasSelected = false);
}
renderColorPicker = () => {
@@ -295,7 +295,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
style={{
width: (style.columnWidth) /
((uniqueHeadings.length +
- ((this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
+ ((this.props.parent.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)
}}>
<div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
{/* the default bucket (no key value) has a tooltip that describes what it is.
diff --git a/src/client/views/collections/CollectionStaffView.scss b/src/client/views/collections/CollectionStaffView.scss
new file mode 100644
index 000000000..493a5f670
--- /dev/null
+++ b/src/client/views/collections/CollectionStaffView.scss
@@ -0,0 +1,13 @@
+.collectionStaffView {
+ .collectionStaffView-staff {
+ width: 100%;
+ margin-top: 100px;
+ margin-bottom: 100px;
+ }
+
+ .collectionStaffView-line {
+ margin: 10px;
+ height: 2px;
+ background: black;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
new file mode 100644
index 000000000..eea05ea61
--- /dev/null
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -0,0 +1,61 @@
+import { CollectionSubView } from "./CollectionSubView";
+import { Transform } from "../../util/Transform";
+import React = require("react")
+import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
+import { Doc, HeightSym } from "../../../new_fields/Doc";
+import { NumCast } from "../../../new_fields/Types";
+import "./CollectionStaffView.scss";
+import { observer } from "mobx-react";
+
+@observer
+export class CollectionStaffView extends CollectionSubView(doc => doc) {
+ private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(0, -this._mainCont.current!.scrollTop);
+ private _mainCont = React.createRef<HTMLDivElement>();
+ private _reactionDisposer: IReactionDisposer | undefined;
+ @observable private _staves = NumCast(this.props.Document.staves);
+
+ componentDidMount = () => {
+ this._reactionDisposer = reaction(
+ () => NumCast(this.props.Document.staves),
+ (staves) => runInAction(() => this._staves = staves)
+ );
+
+ this.props.Document.staves = 5;
+ }
+
+ @computed get fieldExtensionDoc() {
+ return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey);
+ }
+
+ @computed get addStaffButton() {
+ return <div onPointerDown={this.addStaff}>+</div>;
+ }
+
+ @computed get staves() {
+ let staves = [];
+ for (let i = 0; i < this._staves; i++) {
+ let rows = [];
+ for (let j = 0; j < 5; j++) {
+ rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>)
+ }
+ staves.push(<div key={`staff-${i}`} className="collectionStaffView-staff">
+ {rows}
+ </div>);
+ }
+ return staves;
+ }
+
+ @action
+ addStaff = (e: React.PointerEvent) => {
+ this.props.Document.staves = this._staves + 1;
+ }
+
+ render() {
+ return (
+ <div className="collectionStaffView" ref={this._mainCont}>
+ {this.staves}
+ {this.addStaffButton}
+ </div>
+ )
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 306f8e052..1c3ff37ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -32,13 +32,15 @@ export interface CollectionViewProps extends FieldViewProps {
VisibleHeight?: () => number;
chromeCollapsed: boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
- showHiddenControls?: boolean; // hack for showing the undo/redo/ink controls in a linear view -- needs to be redone
+ fieldKey: string;
}
export interface SubCollectionViewProps extends CollectionViewProps {
CollectionView: Opt<CollectionView>;
ruleProvider: Doc | undefined;
children?: never | (() => JSX.Element[]) | React.ReactNode;
+ isAnnotationOverlay?: boolean;
+ annotationsKey: string;
}
export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@@ -57,23 +59,31 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
componentDidMount() {
this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
- async (args) => args[1] instanceof Doc &&
- this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc))));
+ async (args) => {
+ if (args[1] instanceof Doc) {
+ this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), "layoutFromParent"));
+ }
+ else if (!(args[1] instanceof Promise)) {
+ this.childDocs.filter(d => !d.isTemplateField).map(async doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout"));
+ }
+ });
}
componentWillUnmount() {
this._childLayoutDisposer && this._childLayoutDisposer();
}
- // The data field for rendeing this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
+ @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); }
+ @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
+
+ // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
// When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
// to its children which may be templates.
- // The name of the data field comes from fieldExt if it's an extension, or fieldKey otherwise.
+ // 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() {
- return Doc.fieldExtensionDoc(this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt)[this.props.fieldExt || this.props.fieldKey];
+ return this.props.annotationsKey ? (this.extensionDoc ? this.extensionDoc[this.props.annotationsKey] : undefined) : this.dataDoc[this.props.fieldKey];
}
-
get childLayoutPairs() {
return this.childDocs.map(cd => Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, cd)).filter(pair => pair.layout).map(pair => ({ layout: pair.layout!, data: pair.data! }));
}
@@ -121,11 +131,12 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
+ (this.props.Document.dropConverter instanceof ScriptField) &&
+ this.props.Document.dropConverter.script.run({ dragData: de.data });
if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
this.childDocs.map(doc =>
- Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc)
- );
+ Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, "layoutFromParent"));
e.stopPropagation();
return true;
}
@@ -133,11 +144,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
if (de.data.dropAction || de.data.userDropAction) {
added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
} else if (de.data.moveDocument) {
- let movedDocs = de.data.draggedDocuments;// de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
- // note that it's possible the drag function might create a drop document that's not the same as the
- // original dragged document. So we explicitly call addDocument() with a droppedDocument and
+ let movedDocs = de.data.draggedDocuments;
added = movedDocs.reduce((added: boolean, d, i) =>
- de.data.moveDocument(d, this.props.Document, (doc: Doc) => this.props.addDocument(de.data.droppedDocuments[i])) || added, false);
+ de.data.droppedDocuments[i] !== d ? this.props.addDocument(de.data.droppedDocuments[i]) :
+ de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
} else {
added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index ca0c321b7..7d0c900a6 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -10,12 +10,12 @@
width:100%;
position: relative;
top:0;
- padding-top: 20px;
padding-left: 10px;
padding-right: 10px;
background: $light-color-secondary;
font-size: 13px;
overflow: auto;
+ cursor: default;
ul {
list-style: none;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index abaa9662c..0e3f0d1a9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,7 @@ import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types';
-import { emptyFunction, Utils } from '../../../Utils';
+import { emptyFunction, Utils, returnFalse } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from '../../util/DocumentManager';
@@ -23,8 +23,7 @@ import { EditableView } from "../EditableView";
import { MainView } from '../MainView';
import { KeyValueBox } from '../nodes/KeyValueBox';
import { Templates } from '../Templates';
-import { CollectionViewType } from './CollectionBaseView';
-import { CollectionSchemaPreview } from './CollectionSchemaView';
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import React = require("react");
@@ -82,56 +81,46 @@ class TreeView extends React.Component<TreeViewProps> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
- get defaultExpandedView() { return this.childDocs ? this.fieldKey : this.props.document.defaultExpandedView ? StrCast(this.props.document.defaultExpandedView) : ""; }
+ 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
- @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; }
set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = c; }
+ @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || 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.resolvedDataDoc ? this.resolvedDataDoc : this.props.document; }
+ @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@computed get fieldKey() {
- let splits = StrCast(this.props.document.layout).split("fieldKey={\"");
+ let splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\"");
return splits.length > 1 ? splits[1].split("\"")[0] : "data";
}
- @computed get childDocs() {
- let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined;
- return (this.props.dataDoc ? Cast(this.props.dataDoc[this.fieldKey], listSpec(Doc)) : undefined) ||
- (layout ? Cast(layout[this.fieldKey], listSpec(Doc)) : undefined) ||
- Cast(this.props.document[this.fieldKey], listSpec(Doc));
- }
- @computed get childLinks() {
- let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined;
- return (this.props.dataDoc ? Cast(this.props.dataDoc.links, listSpec(Doc)) : undefined) ||
- (layout instanceof Doc ? Cast(layout.links, listSpec(Doc)) : undefined) ||
- Cast(this.props.document.links, listSpec(Doc));
+ childDocList(field: string) {
+ let layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined;
+ return ((this.props.dataDoc ? Cast(this.props.dataDoc[field], listSpec(Doc)) : undefined) ||
+ (layout ? Cast(layout[field], listSpec(Doc)) : undefined) ||
+ Cast(this.props.document[field], listSpec(Doc))) as Doc[];
}
- @computed get resolvedDataDoc() {
- if (this.props.dataDoc === undefined && this.props.document.layout instanceof Doc) {
- // if there is no dataDoc (ie, we're not rendering a template layout), but this document
- // has a template layout document, then we will render the template layout but use
- // this document as the data document for the layout.
+ @computed get childDocs() { return this.childDocList(this.fieldKey); }
+ @computed get childLinks() { return this.childDocList("links"); }
+ @computed get templateDataDoc() {
+ if (this.props.dataDoc === undefined && Doc.LayoutField(this.props.document) !== "string") {
+ // if there is no dataDoc (ie, we're not rendering a template layout), but this document has a layout document (not a layout string),
+ // then we render the layout document as a template and use this document as the data context for the template layout.
return this.props.document;
}
return this.props.dataDoc;
}
@computed get boundsOfCollectionDocument() {
return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 ? undefined :
- Doc.ComputeContentBounds(DocListCast(this.props.document.data));
+ Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
- @undoBatch delete = () => this.props.deleteDoc(this.dataDoc);
- @undoBatch openRight = () => this.props.addDocTab(this.props.document, undefined, "onRight");
+ @undoBatch delete = () => this.props.deleteDoc(this.props.document);
+ @undoBatch openRight = () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight");
@undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete();
@undoBatch move = (doc: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => {
return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc);
}
- @undoBatch @action remove = (document: Document, key: string): boolean => {
- let children = Cast(this.dataDoc[key], listSpec(Doc), []);
- if (children.indexOf(document) !== -1) {
- children.splice(children.indexOf(document), 1);
- return true;
- }
- return false;
+ @undoBatch @action remove = (document: Document, key: string) => {
+ return Doc.RemoveDocFromList(this.dataDoc, key, document);
}
protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -175,9 +164,9 @@ class TreeView extends React.Component<TreeViewProps> {
fontStyle={style}
fontSize={12}
GetValue={() => StrCast(this.props.document[key])}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.dataDoc)[key] = value) ? true : true)}
+ SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
- Doc.GetProto(this.dataDoc)[key] = value;
+ Doc.SetInPlace(this.props.document, key, value, false);
let doc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined;
if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
@@ -187,13 +176,15 @@ class TreeView extends React.Component<TreeViewProps> {
/>)
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 view7
- if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking && this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
+ 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
+ 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.resolvedDataDoc, "inTab"), icon: "folder" });
- ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "inTab"), icon: "folder" });
+ ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.templateDataDoc, "onRight"), icon: "caret-square-right" });
if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) {
- ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document, true)), icon: "camera" });
+ ContextMenu.Instance.addItem({ description: "Focus", event: () => (view => view && view.props.focus(this.props.document, true))(DocumentManager.Instance.getFirstDocumentView(this.dataDoc)), icon: "camera" });
}
ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
} else {
@@ -225,19 +216,16 @@ class TreeView extends React.Component<TreeViewProps> {
if (de.data instanceof DragManager.DocumentDragData) {
e.stopPropagation();
if (de.data.draggedDocuments[0] === this.props.document) return true;
- let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before);
+ let addDoc = (doc: Doc) => this.props.addDocument(doc, undefined, before);
if (inside) {
- let docList = Cast(this.dataDoc.data, listSpec(Doc));
- if (docList !== undefined) {
- addDoc = (doc: Doc) => { docList && docList.push(doc); return true; };
- }
+ addDoc = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) || addDoc(doc);
}
let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments);
return (de.data.dropAction || de.data.userDropAction) ?
- de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false)
- : (de.data.moveDocument) ?
- movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.resolvedDataDoc, addDoc) || added, false)
- : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before), false);
+ de.data.droppedDocuments.reduce((added, d) => addDoc(d) || added, false)
+ : de.data.moveDocument ?
+ movedDocs.reduce((added, d) => de.data.moveDocument(d, undefined, addDoc) || added, false)
+ : de.data.droppedDocuments.reduce((added, d) => addDoc(d), false);
}
return false;
}
@@ -250,20 +238,22 @@ class TreeView extends React.Component<TreeViewProps> {
return finalXf;
}
docWidth = () => {
- let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
- if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
- return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
+ let layoutDoc = Doc.Layout(this.props.document);
+ let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth);
+ if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20));
+ return NumCast(layoutDoc.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20;
}
docHeight = () => {
+ let layoutDoc = Doc.Layout(this.props.document);
let bounds = this.boundsOfCollectionDocument;
return Math.min(this.MAX_EMBED_HEIGHT, (() => {
- let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
+ let aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1);
if (aspect) return this.docWidth() * aspect;
if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return this.props.document.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
- Math.min(this.docWidth() * NumCast(this.props.document.scrollHeight, NumCast(this.props.document.nativeHeight)) / NumCast(this.props.document.nativeWidth,
+ return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
+ Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth,
NumCast(this.props.containingCollection.height)))) :
- NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
+ NumCast(layoutDoc.height) ? NumCast(layoutDoc.height) : 50;
})());
}
@@ -312,22 +302,22 @@ class TreeView extends React.Component<TreeViewProps> {
let docs = expandKey === "links" ? this.childLinks : this.childDocs;
return <ul key={expandKey + "more"}>
{!docs ? (null) :
- TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc,
- this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move,
+ TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
+ this.templateDataDoc, expandKey, 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.renderDepth, this.props.showHeaderFields, this.props.preventTreeViewOpen,
[...this.props.renderedIds, this.props.document[Id]])}
</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}>
- {this.dataDoc ? this.expandedField(this.dataDoc) : (null)}
+ {this.expandedField(this.props.document)}
</div></ul>;
} else {
- let layoutDoc = this.props.document;
+ let layoutDoc = Doc.Layout(this.props.document);
return <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id] + this.props.document.title}>
- <CollectionSchemaPreview
+ <ContentFittingDocumentView
Document={layoutDoc}
- DataDocument={this.resolvedDataDoc}
+ DataDocument={this.templateDataDoc}
renderDepth={this.props.renderDepth}
showOverlays={this.noOverlays}
ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider}
@@ -337,15 +327,14 @@ class TreeView extends React.Component<TreeViewProps> {
getTransform={this.docTransform}
CollectionDoc={this.props.containingCollection}
CollectionView={undefined}
- addDocument={emptyFunction as any}
+ addDocument={returnFalse}
moveDocument={this.props.moveDocument}
- removeDocument={emptyFunction as any}
+ removeDocument={returnFalse}
active={this.props.active}
- whenActiveChanged={emptyFunction as any}
+ whenActiveChanged={emptyFunction}
addDocTab={this.props.addDocTab}
pinToPres={this.props.pinToPres}
- setPreviewScript={emptyFunction}>
- </CollectionSchemaPreview>
+ setPreviewScript={emptyFunction} />
</div>;
}
}
@@ -369,7 +358,7 @@ class TreeView extends React.Component<TreeViewProps> {
onPointerDown={action(() => {
if (this.treeViewOpen) {
this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
- this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" :
+ this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
this.childDocs ? this.fieldKey : "fields";
}
@@ -461,7 +450,7 @@ class TreeView extends React.Component<TreeViewProps> {
let rowWidth = () => panelWidth() - 20;
return docs.map((child, i) => {
- let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
+ const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
if (!pair.layout || pair.data instanceof Promise) {
return (null);
}
@@ -480,11 +469,12 @@ class TreeView extends React.Component<TreeViewProps> {
let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false);
};
+ const childLayout = Doc.Layout(pair.layout);
let rowHeight = () => {
- let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0);
- return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym]();
+ let aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0);
+ return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
};
- return <TreeView
+ return !(child instanceof Doc) ? (null) : <TreeView
document={pair.layout}
dataDoc={pair.data}
containingCollection={containingCollection}
@@ -517,7 +507,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
- @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+ @computed get dataDoc() { return this.props.DataDoc || this.props.Document; }
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this.treedropDisposer && this.treedropDisposer();
@@ -527,6 +517,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
componentWillUnmount() {
+ super.componentWillUnmount();
this.treedropDisposer && this.treedropDisposer();
}
@@ -547,6 +538,11 @@ export class CollectionTreeView extends CollectionSubView(Document) {
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ } else if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.recentlyClosed) {
+ ContextMenu.Instance.addItem({ description: "Clear All", event: () => CurrentUserUtils.UserDocument.recentlyClosed = new List<Doc>(), icon: "plus" });
+ e.stopPropagation();
+ e.preventDefault();
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
let layoutItems: ContextMenuProps[] = [];
layoutItems.push({ description: this.props.Document.preventTreeViewOpen ? "Persist Treeview State" : "Abandon Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
@@ -566,26 +562,25 @@ export class CollectionTreeView extends CollectionSubView(Document) {
}
render() {
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
return !this.childDocs ? (null) : (
<div id="body" className="collectionTreeView-dropTarget"
- style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray") }}
+ style={{ overflow: "auto", background: StrCast(this.props.Document.backgroundColor, "lightgray"), 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}>
<EditableView
- contents={this.resolvedDataDoc.title}
+ contents={this.dataDoc.title}
display={"block"}
maxHeight={72}
height={"auto"}
- GetValue={() => StrCast(this.resolvedDataDoc.title)}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true)}
+ GetValue={() => StrCast(this.dataDoc.title)}
+ SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
- Doc.GetProto(this.props.Document).title = value;
+ Doc.SetInPlace(this.dataDoc, "title", value, false);
let doc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined;
if (!doc) doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List<string>([Templates.Title.Layout]) });
TreeView.loadId = doc[Id];
@@ -596,7 +591,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
{
TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => this.props.Document.chromeStatus !== "disabled",
+ this.outerXf, this.props.active, this.props.PanelWidth, this.props.renderDepth, () => !this.props.Document.hideHeaderFields,
BoolCast(this.props.Document.preventTreeViewOpen), [])
}
</ul>
diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionView.scss
index aff965469..e4187e4d6 100644
--- a/src/client/views/collections/CollectionBaseView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -1,6 +1,6 @@
@import "../globalCssVariables";
-#collectionBaseView {
+.collectionView {
border-width: 0;
border-color: $light-color-secondary;
border-style: solid;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 3d5b4e562..8f1278670 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -5,12 +5,9 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo
import { observer } from "mobx-react";
import * as React from 'react';
import { Id } from '../../../new_fields/FieldSymbols';
-import { StrCast } from '../../../new_fields/Types';
+import { StrCast, BoolCast, Cast } from '../../../new_fields/Types';
import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from '../ContextMenuItem';
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from './CollectionBaseView';
import { CollectionDockingView } from "./CollectionDockingView";
import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -20,20 +17,83 @@ import { CollectionTreeView } from "./CollectionTreeView";
import { CollectionViewBaseChrome } from './CollectionViewChromes';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { CollectionLinearView } from '../CollectionLinearView';
+import { CollectionStaffView } from './CollectionStaffView';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { ImageField } from '../../../new_fields/URLField';
+import { DocListCast } from '../../../new_fields/Doc';
+import Lightbox from 'react-image-lightbox-with-rotate';
+import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
export const COLLECTION_BORDER_WIDTH = 2;
-
+import { DateField } from '../../../new_fields/DateField';
+import { Doc, } from '../../../new_fields/Doc';
+import { listSpec } from '../../../new_fields/Schema';
+import { DocumentManager } from '../../util/DocumentManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import './CollectionView.scss';
+import { FieldViewProps, FieldView } from '../nodes/FieldView';
+import { Touchable } from '../Touchable';
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
+export enum CollectionViewType {
+ Invalid,
+ Freeform,
+ Schema,
+ Docking,
+ Tree,
+ Stacking,
+ Masonry,
+ Pivot,
+ Linear,
+ Staff
+}
+
+export namespace CollectionViewType {
+ const stringMapping = new Map<string, CollectionViewType>([
+ ["invalid", CollectionViewType.Invalid],
+ ["freeform", CollectionViewType.Freeform],
+ ["schema", CollectionViewType.Schema],
+ ["docking", CollectionViewType.Docking],
+ ["tree", CollectionViewType.Tree],
+ ["stacking", CollectionViewType.Stacking],
+ ["masonry", CollectionViewType.Masonry],
+ ["pivot", CollectionViewType.Pivot],
+ ["linear", CollectionViewType.Linear]
+ ]);
+
+ export const valueOf = (value: string) => stringMapping.get(value.toLowerCase());
+}
+
+export interface CollectionRenderProps {
+ addDocument: (document: Doc) => boolean;
+ removeDocument: (document: Doc) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
+ active: () => boolean;
+ whenActiveChanged: (isActive: boolean) => void;
+}
+
@observer
-export class CollectionView extends React.Component<FieldViewProps> {
- @observable private _collapsed = true;
+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; }
- public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
-
- constructor(props: any) {
- super(props);
+ get collectionViewType(): CollectionViewType | undefined {
+ let viewField = Cast(this.props.Document.viewType, "number");
+ if (CollectionView._safeMode) {
+ if (viewField === CollectionViewType.Freeform) {
+ return CollectionViewType.Tree;
+ }
+ if (viewField === CollectionViewType.Invalid) {
+ return CollectionViewType.Freeform;
+ }
+ }
+ return viewField === undefined ? CollectionViewType.Invalid : viewField;
}
componentDidMount = () => {
@@ -48,32 +108,74 @@ export class CollectionView extends React.Component<FieldViewProps> {
});
}
- componentWillUnmount = () => {
- this._reactionDisposer && this._reactionDisposer();
+ componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer();
+
+ // bcz: Argh? What's the height of the collection chromes??
+ chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
+
+ active = () => this.props.isSelected() || 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 {
+ let targetDataDoc = Doc.GetProto(this.props.Document);
+ Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+ let extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field
+ extension && (extension.lastModified = new DateField(new Date(Date.now())));
+ Doc.GetProto(doc).lastOpened = new DateField;
+ return true;
+ }
+
+ @action.bound
+ removeDocument(doc: Doc): boolean {
+ let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
+ docView && SelectionManager.DeselectDoc(docView);
+ let value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ 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);
+ return true;
+ }
+ return false;
}
- // bcz: Argh? What's the height of the collection chomes??
- chromeHeight = () => {
- return (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0);
+ // this is called with the document that was dragged and the collection to move it into.
+ // if the target collection is the same as this collection, then the move will be allowed.
+ // otherwise, the document being moved must be able to be removed from its container before
+ // moving it into the target.
+ @action.bound
+ moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean {
+ if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+ return true;
+ }
+ return this.removeDocument(doc) ? addDocument(doc) : false;
+ }
+
+ showIsTagged = () => {
+ const children = DocListCast(this.props.Document[this.props.fieldKey]);
+ const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
+ const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
+ return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- let props = { ...this.props, ...renderProps };
- switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) {
- case CollectionViewType.Schema: return (<CollectionSchemaView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
- // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- case CollectionViewType.Docking: return (<CollectionDockingView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
- case CollectionViewType.Tree: return (<CollectionTreeView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
- case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
- case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
- case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (<CollectionFreeFormView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
- case CollectionViewType.Linear: { return (<CollectionLinearView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />); }
+ let props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ switch (type) {
+ case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
+ case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
+ case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
+ case CollectionViewType.Staff: return (<CollectionStaffView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />)
+ case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
+ case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
+ case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (<CollectionFreeFormView key="collview" {...props} />); }
case CollectionViewType.Freeform:
- default:
- this.props.Document.freeformLayoutEngine = undefined;
- return (<CollectionFreeFormView chromeCollapsed={this._collapsed} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
+ default: { this.props.Document.freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
}
- return (null);
}
@action
@@ -84,23 +186,18 @@ export class CollectionView extends React.Component<FieldViewProps> {
private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
// currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip
- if (this.isAnnotationOverlay || this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking) {
- return [(null), this.SubViewHelper(type, renderProps)];
- }
- return [
- <CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />,
- this.SubViewHelper(type, renderProps)
- ];
+ let chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) :
+ <CollectionViewBaseChrome CollectionView={this} key="chrome" type={type} collapse={this.collapse} />;
+ return [chrome, this.SubViewHelper(type, renderProps)];
}
- get isAnnotationOverlay() { return this.props.fieldExt ? true : false; }
onContextMenu = (e: React.MouseEvent): void => {
- if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
let existingVm = ContextMenu.Instance.findByDescription("View Modes...");
- let subItems: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+ let subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" });
- if (CollectionBaseView.InSafeMode()) {
+ if (CollectionView._safeMode) {
ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
}
subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
@@ -112,6 +209,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
this.props.Document.autoHeight = true;
}, icon: "ellipsis-v"
});
+ subItems.push({ description: "Staff", event: () => this.props.Document.viewType = CollectionViewType.Staff, icon: "music" });
subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" });
subItems.push({ description: "Pivot", event: () => this.props.Document.viewType = CollectionViewType.Pivot, icon: "columns" });
switch (this.props.Document.viewType) {
@@ -120,21 +218,47 @@ export class CollectionView extends React.Component<FieldViewProps> {
break;
}
}
+ subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
!existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
let existing = ContextMenu.Instance.findByDescription("Layout...");
- let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
+ let 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" });
!existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" });
- ContextMenu.Instance.addItem({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
+
+ let more = ContextMenu.Instance.findByDescription("More...");
+ let 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" })
}
}
+ lightbox = (images: string[]) => {
+ return !this._isLightboxOpen ? (null) : (<Lightbox key="lightbox"
+ mainSrc={images[this._curLightboxImg]}
+ nextSrc={images[(this._curLightboxImg + 1) % images.length]}
+ prevSrc={images[(this._curLightboxImg + images.length - 1) % images.length]}
+ onCloseRequest={action(() => this._isLightboxOpen = false)}
+ onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
+ onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
+ }
render() {
- return (
- <CollectionBaseView {...this.props} onContextMenu={this.onContextMenu}>
- {this.SubView}
- </CollectionBaseView>
- );
+ const props: CollectionRenderProps = {
+ addDocument: this.addDocument,
+ removeDocument: this.removeDocument,
+ moveDocument: this.moveDocument,
+ active: this.active,
+ whenActiveChanged: this.whenActiveChanged,
+ };
+ 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")}`
+ }}
+ onContextMenu={this.onContextMenu}>
+ {this.showIsTagged()}
+ {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
+ {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField) ? Cast(d.data, ImageField)!.url.href : ""))}
+ </div>);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 3a66c05f4..cfc6c2a3f 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -14,7 +14,7 @@ import { undoBatch } from "../../util/UndoManager";
import { EditableView } from "../EditableView";
import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
import { DocLike } from "../MetadataEntryMenu";
-import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionViewType } from "./CollectionView";
import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
import * as Autosuggest from 'react-autosuggest';
@@ -40,9 +40,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
_templateCommand = {
- title: "set template", script: "this.target.childLayout = this.source ? this.source[0] : undefined", params: ["target", "source"],
+ title: "set template", script: "setChildLayout(this.target, this.source && this.source.length ? this.source[0]:undefined)", params: ["target", "source"],
initialize: emptyFunction,
- immediate: (draggedDocs: Doc[]) => this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined
+ immediate: (draggedDocs: Doc[]) => Doc.setChildLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined)
};
_contentCommand = {
// title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"], // bcz: doesn't look like we can do async stuff in scripting...
@@ -480,12 +480,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
getKeySuggestions = async (value: string): Promise<string[]> => {
value = value.toLowerCase();
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
+ let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
if (docs instanceof Doc) {
return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
} else {
@@ -591,19 +586,9 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
if (textwrappedRows.length) {
this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]);
} else {
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
- if (docs instanceof Doc) {
- let allRows = [docs[Id]];
- this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
- } else {
- let allRows = docs.map(doc => doc[Id]);
- this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
- }
+ let docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]);
+ let allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
+ this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows);
}
}
@@ -638,63 +623,14 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> {
- @observable private _currentKey: string = "";
- @observable private suggestions: string[] = [];
@computed private get descending() { return Cast(this.props.CollectionView.props.Document.sortAscending, "boolean", null); }
- @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); }
-
- getKeySuggestions = async (value: string): Promise<string[]> => {
- value = value.toLowerCase();
- let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike)
- = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]);
- if (typeof docs === "function") {
- docs = docs();
- }
- docs = await docs;
- if (docs instanceof Doc) {
- return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value));
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys).filter(key => key.toLowerCase().startsWith(value));
- }
- }
-
- @action
- onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
- this._currentKey = newValue;
- }
-
- getSuggestionValue = (suggestion: string) => suggestion;
-
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- }
-
- onSuggestionFetch = async ({ value }: { value: string }) => {
- const sugg = await this.getKeySuggestions(value);
- runInAction(() => {
- this.suggestions = sugg;
- });
- }
-
- @action
- onSuggestionClear = () => {
- this.suggestions = [];
- }
-
- setValue = (value: string) => {
- this.props.CollectionView.props.Document.sectionFilter = value;
- return true;
- }
@action toggleSort = () => {
if (this.props.CollectionView.props.Document.sortAscending) this.props.CollectionView.props.Document.sortAscending = undefined;
else if (this.props.CollectionView.props.Document.sortAscending === undefined) this.props.CollectionView.props.Document.sortAscending = false;
else this.props.CollectionView.props.Document.sortAscending = true;
}
- @action resetValue = () => { this._currentKey = this.sectionFilter; };
render() {
return (
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index 7f2913214..8b6fa330c 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -7,7 +7,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { SearchUtil } from "../../util/SearchUtil";
import { CollectionDockingView } from "./CollectionDockingView";
import { NumCast } from "../../../new_fields/Types";
-import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionViewType } from "./CollectionView";
import { DocumentButtonBar } from "../DocumentButtonBar";
import { DocumentManager } from "../../util/DocumentManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 886692172..48d330674 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -63,11 +63,12 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs:
fontSize: NumCast(pivotDoc.pivotFontSize, 10)
});
for (const doc of val) {
+ let layoutDoc = Doc.Layout(doc);
docMap.set(doc, {
x: x + xCount * pivotAxisWidth * 1.25,
y: -y,
width: pivotAxisWidth,
- height: doc.nativeWidth ? (NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth
+ height: layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth
});
xCount++;
if (xCount >= numCols) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index cfd18ad35..75af11537 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -1,20 +1,18 @@
.collectionfreeformlinkview-linkLine {
stroke: black;
- transform: translate(10000px,10000px);
opacity: 0.8;
pointer-events: all;
stroke-width: 3px;
+ transition: opacity 0.5s ease-in;
}
.collectionfreeformlinkview-linkCircle {
stroke: rgb(0,0,0);
opacity: 0.5;
- transform: translate(10000px,10000px);
pointer-events: all;
cursor: pointer;
}
.collectionfreeformlinkview-linkText {
stroke: rgb(0,0,0);
opacity: 0.5;
- transform: translate(10000px,10000px);
pointer-events: all;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index fe92eed10..837413842 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,63 +1,51 @@
import { observer } from "mobx-react";
-import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc";
-import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types";
-import { InkingControl } from "../../InkingControl";
+import { Doc } from "../../../../new_fields/Doc";
+import { Utils } from '../../../../Utils';
+import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinkView.scss";
import React = require("react");
import v5 = require("uuid/v5");
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { observable, action } from "mobx";
export interface CollectionFreeFormLinkViewProps {
- A: Doc;
- B: Doc;
+ A: DocumentView;
+ B: DocumentView;
LinkDocs: Doc[];
- addDocument: (document: Doc) => boolean;
- removeDocument: (document: Doc) => boolean;
}
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
-
- onPointerDown = (e: React.PointerEvent) => {
- if (e.button === 0 && !InkingControl.Instance.selectedTool) {
- let a = this.props.A;
- let b = this.props.B;
- let x1 = NumCast(a.x) + (BoolCast(a.isMinimized) ? 5 : a[WidthSym]() / 2);
- let y1 = NumCast(a.y) + (BoolCast(a.isMinimized) ? 5 : a[HeightSym]() / 2);
- let x2 = NumCast(b.x) + (BoolCast(b.isMinimized) ? 5 : b[WidthSym]() / 2);
- let y2 = NumCast(b.y) + (BoolCast(b.isMinimized) ? 5 : b[HeightSym]() / 2);
- // this.props.LinkDocs.map(l => {
- // let width = l[WidthSym]();
- // l.x = (x1 + x2) / 2 - width / 2;
- // l.y = (y1 + y2) / 2 + 10;
- // if (!this.props.removeDocument(l)) this.props.addDocument(l, false);
- // });
- e.stopPropagation();
- e.preventDefault();
- }
+ @observable _alive: number = 0;
+ @observable _opacity: number = 1;
+ @action
+ componentDidMount() {
+ this._alive = 1;
+ setTimeout(this.rerender, 50);
+ setTimeout(action(() => this._opacity = 0.05), 50);
+ }
+ @action
+ componentWillUnmount() {
+ this._alive = 0;
}
+ rerender = action(() => {
+ if (this._alive) {
+ setTimeout(this.rerender, 50);
+ this._alive++;
+ }
+ });
+
render() {
- // let l = this.props.LinkDocs;
- let a = this.props.A;
- let b = this.props.B;
- let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / 2);
- let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / 2);
- let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
- let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
- let text = "";
- // let first = this.props.LinkDocs[0];
- // if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : "");
- // else text = "-multiple-";
- return (
- <>
- <line key="linkLine" className="collectionfreeformlinkview-linkLine"
- x1={`${x1}`} y1={`${y1}`}
- x2={`${x2}`} y2={`${y2}`} />
- {/* <circle key="linkCircle" className="collectionfreeformlinkview-linkCircle"
- cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={8} onPointerDown={this.onPointerDown} /> */}
- <text key="linkText" textAnchor="middle" className="collectionfreeformlinkview-linkText" x={`${(x1 + x2) / 2}`} y={`${(y1 + y2) / 2}`}>
- {text}
- </text>
- </>
- );
+ let y = this._alive;
+ let acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv!.getElementsByClassName("docuLinkBox-cont") : [];
+ let a = (acont.length ? acont[0] : this.props.A.ContentDiv!).getBoundingClientRect();
+ let b = (bcont.length ? bcont[0] : this.props.B.ContentDiv!).getBoundingClientRect();
+ let pt1 = Utils.getNearestPointInPerimeter(a.left, a.top, a.width, a.height, b.left + b.width / 2, b.top + b.height / 2);
+ let pt2 = Utils.getNearestPointInPerimeter(b.left, b.top, b.width, b.height, a.left + a.width / 2, a.top + a.height / 2);
+ return (<line key="linkLine" className="collectionfreeformlinkview-linkLine"
+ style={{ opacity: this._opacity }}
+ x1={`${pt1[0]}`} y1={`${pt1[1]}`}
+ x2={`${pt2[0]}`} y2={`${pt2[1]}`} />);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
index 30e158603..cb5cef29c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss
@@ -1,10 +1,9 @@
.collectionfreeformlinksview-svgCanvas{
- transform: translate(-10000px,-10000px);
position: absolute;
top: 0;
left: 0;
- width: 20000px;
- height: 20000px;
+ width: 100%;
+ height: 100%;
pointer-events: none;
}
.collectionfreeformlinksview-container {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index a81f5315a..e9191c176 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,19 +1,18 @@
-import { computed, IReactionDisposer, reaction } from "mobx";
+import { computed, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../../new_fields/Doc";
+import { Doc } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { List } from "../../../../new_fields/List";
-import { listSpec } from "../../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
import { DocumentManager } from "../../../util/DocumentManager";
import { DocumentView } from "../../nodes/DocumentView";
-import { CollectionViewProps } from "../CollectionSubView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
+import { Utils } from "../../../../Utils";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { DocumentType } from "../../../documents/DocumentTypes";
@observer
-export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> {
+export class CollectionFreeFormLinksView extends React.Component {
_brushReactionDisposer?: IReactionDisposer;
componentDidMount() {
@@ -69,59 +68,33 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
// });
}
componentWillUnmount() {
- if (this._brushReactionDisposer) {
- this._brushReactionDisposer();
- }
+ this._brushReactionDisposer && this._brushReactionDisposer();
}
- documentAnchors(view: DocumentView) {
- let equalViews = [view];
- let containerDoc = FieldValue(Cast(view.props.Document.annotationOn, Doc));
- if (containerDoc) {
- equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
- }
- if (view.props.ContainingCollectionDoc) {
- let collid = view.props.ContainingCollectionDoc[Id];
- DocListCast(this.props.Document[this.props.fieldKey]).
- filter(child =>
- child[Id] === collid).map(view =>
- DocumentManager.Instance.getDocumentViews(view).map(view =>
- equalViews.push(view)));
- }
- return equalViews.filter(sv => sv.props.ContainingCollectionDoc === this.props.Document);
- }
-
@computed
get uniqueConnections() {
let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
- let srcViews = this.documentAnchors(connection.a);
- let targetViews = this.documentAnchors(connection.b);
-
- let possiblePairs: { a: Doc, b: Doc, }[] = [];
- srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document })));
- possiblePairs.map(possiblePair => {
- if (!drawnPairs.reduce((found, drawnPair) => {
- let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b));
- let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a));
- let match = match1 || match2;
- if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
- drawnPair.l.push(connection.l);
- }
- return match || found;
- }, false)) {
- drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] });
+ if (!drawnPairs.reduce((found, drawnPair) => {
+ let match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
+ let match2 = (connection.a === drawnPair.b && connection.b === drawnPair.a);
+ let match = match1 || match2;
+ if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) {
+ drawnPair.l.push(connection.l);
}
- });
+ return match || found;
+ }, false)) {
+ drawnPairs.push({ a: connection.a, b: connection.b, l: [connection.l] });
+ }
return drawnPairs;
- }, [] as { a: Doc, b: Doc, l: Doc[] }[]);
- return connections.map(c => <CollectionFreeFormLinkView key={c.l.reduce((p, l) => p + l[Id], "")} A={c.a} B={c.b} LinkDocs={c.l}
- removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
+ }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]);
+ return connections.filter(c => c.a.props.Document.type === DocumentType.LINK) // get rid of the filter to show links to documents in addition to document anchors
+ .map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
}
render() {
return (
<div className="collectionfreeformlinksview-container">
<svg className="collectionfreeformlinksview-svgCanvas">
- {this.uniqueConnections}
+ {SelectionManager.GetIsDragging() ? (null) : this.uniqueConnections}
</svg>
{this.props.children}
</div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index bb1a12f88..db36c4391 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -17,6 +17,7 @@
width: 100%;
height: 100%;
transform-origin: left top;
+ touch-action: none;
}
.collectionFreeform-customText {
@@ -25,6 +26,9 @@
}
.collectionfreeformview-container {
+ // touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions.
+ touch-action: none;
+
.collectionfreeformview>.jsx-parser {
position: inherit;
height: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index adbad5da5..3b313c34a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,16 +1,16 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable } from "mobx";
+import { action, computed, observable, trace } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { InkField, PointData, InkTool } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
-import { aggregateBounds, emptyFunction, intersectRect, returnEmptyString, returnOne, Utils } from "../../../../Utils";
+import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
@@ -25,19 +25,22 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"
import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingCanvas } from "../../InkingCanvas";
-import { CollectionFreeFormDocumentView, positionSchema } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "../../nodes/DocumentContentsView";
-import { documentSchema, DocumentViewProps } from "../../nodes/DocumentView";
+import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
+import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
-import PDFMenu from "../../pdf/PDFMenu";
import { CollectionSubView } from "../CollectionSubView";
import { computePivotLayout, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
-import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
+import { InteractionUtils } from "../../../util/InteractionUtils";
+import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import PDFMenu from "../../pdf/PDFMenu";
+import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
+import { InkingControl } from "../../InkingControl";
+import { InkingStroke, CreatePolyline } from "../../InkingStroke";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -51,6 +54,11 @@ export const panZoomSchema = createSchema({
isRuleProvider: "boolean",
fitToBox: "boolean",
panTransformType: "string",
+ scrollHeight: "number",
+ fitX: "number",
+ fitY: "number",
+ fitW: "number",
+ fitH: "number"
});
type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>;
@@ -67,9 +75,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; }
@computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
@computed get contentBounds() { return aggregateBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); }
- @computed get nativeWidth() { return this.fitToContent ? 0 : this.Document.nativeWidth || 0; }
+ @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; }
@computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; }
- private get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt')
+ private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
private easing = () => this.props.Document.panTransformType === "Ease";
private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document.panX || 0;
@@ -111,10 +119,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
}
- @computed get fieldExtensionDoc() {
- return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey);
- }
-
@action
onDrop = (e: React.DragEvent): Promise<void> => {
var pt = this.getTransform().transformPoint(e.pageX, e.pageY);
@@ -131,21 +135,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (super.drop(e, de)) {
if (de.data instanceof DragManager.DocumentDragData) {
if (de.data.droppedDocuments.length) {
- let z = NumCast(de.data.droppedDocuments[0].z);
+ let firstDoc = de.data.droppedDocuments[0];
+ let z = NumCast(firstDoc.z);
let x = (z ? xpo : xp) - de.data.offset[0];
let y = (z ? ypo : yp) - de.data.offset[1];
- let dropX = NumCast(de.data.droppedDocuments[0].x);
- let dropY = NumCast(de.data.droppedDocuments[0].y);
+ let dropX = NumCast(firstDoc.x);
+ let dropY = NumCast(firstDoc.y);
de.data.droppedDocuments.forEach(action((d: Doc) => {
+ let layoutDoc = Doc.Layout(d);
d.x = x + NumCast(d.x) - dropX;
d.y = y + NumCast(d.y) - dropY;
- if (!NumCast(d.width)) {
- d.width = 300;
+ if (!NumCast(layoutDoc.width)) {
+ layoutDoc.width = 300;
}
- if (!NumCast(d.height)) {
- let nw = NumCast(d.nativeWidth);
- let nh = NumCast(d.nativeHeight);
- d.height = nw && nh ? nh / nw * NumCast(d.width) : 300;
+ if (!NumCast(layoutDoc.height)) {
+ let nw = NumCast(layoutDoc.nativeWidth);
+ let nh = NumCast(layoutDoc.nativeHeight);
+ layoutDoc.height = nw && nh ? nh / nw * NumCast(layoutDoc.width) : 300;
}
this.bringToFront(d);
}));
@@ -158,12 +164,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let dragDoc = de.data.dropDocument;
let x = xp - de.data.offset[0];
let y = yp - de.data.offset[1];
- let dropX = NumCast(de.data.dropDocument.x);
- let dropY = NumCast(de.data.dropDocument.y);
+ let dropX = NumCast(dragDoc.x);
+ let dropY = NumCast(dragDoc.y);
dragDoc.x = x + NumCast(dragDoc.x) - dropX;
dragDoc.y = y + NumCast(dragDoc.y) - dropY;
- de.data.targetContext = this.props.Document;
- dragDoc.targetContext = this.props.Document;
+ de.data.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
this.bringToFront(dragDoc);
}
}
@@ -173,36 +178,33 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
pickCluster(probe: number[]) {
return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
+ let layoutDoc = Doc.Layout(cd);
let cx = NumCast(cd.x) - this._clusterDistance;
let cy = NumCast(cd.y) - this._clusterDistance;
- let cw = NumCast(cd.width) + 2 * this._clusterDistance;
- let ch = NumCast(cd.height) + 2 * this._clusterDistance;
- return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
+ let cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance;
+ let ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ?
NumCast(cd.cluster) : cluster;
}, -1);
}
- tryDragCluster(e: PointerEvent) {
- let cluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
- if (cluster !== -1) {
- let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
-
- // hacky way to get a list of DocumentViews in the current view given a list of Documents in the current view
- let prevSelected = SelectionManager.SelectedDocuments();
- this.selectDocuments(eles);
- let clusterDocs = SelectionManager.SelectedDocuments();
- SelectionManager.DeselectAll();
- prevSelected.map(dv => SelectionManager.SelectDoc(dv, true));
-
- let de = new DragManager.DocumentDragData(eles);
- de.moveDocument = this.props.moveDocument;
- const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
- de.offset = this.getTransform().transformDirection(e.x - left, e.y - top);
- de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
- DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, {
- handlers: { dragComplete: action(emptyFunction) },
- hideSource: !de.dropAction
- });
- return true;
+ tryDragCluster(e: PointerEvent | TouchEvent) {
+ let ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+ if (ptsParent) {
+ let cluster = this.pickCluster(this.getTransform().transformPoint(ptsParent.clientX, ptsParent.clientY));
+ if (cluster !== -1) {
+ let eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => NumCast(cd.cluster) === cluster);
+ let clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
+ let de = new DragManager.DocumentDragData(eles);
+ de.moveDocument = this.props.moveDocument;
+ const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0);
+ de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
+ de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
+ DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, {
+ handlers: { dragComplete: action(emptyFunction) },
+ hideSource: !de.dropAction
+ });
+ return true;
+ }
}
return false;
@@ -265,109 +267,257 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return clusterColor;
}
+ @observable private _points: { x: number, y: number }[] = [];
+
@action
onPointerDown = (e: React.PointerEvent): void => {
if (e.nativeEvent.cancelBubble) return;
this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
- if (e.button === 0 && !e.shiftKey && !e.altKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
+ if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && (!this.isAnnotationOverlay || this.zoomScaling() !== 1) && this.props.active()) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointerup", this.onPointerUp);
- this._lastX = e.pageX;
- this._lastY = e.pageY;
+ if (InkingControl.Instance.selectedTool === InkTool.None) {
+ this._lastX = e.pageX;
+ this._lastY = e.pageY;
+ }
+ else {
+ e.stopPropagation();
+ e.preventDefault();
+
+ if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ let point = this.getTransform().transformPoint(e.pageX, e.pageY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ }
}
}
+ @action
+ handle1PointerDown = (e: React.TouchEvent) => {
+ let pt = e.targetTouches.item(0);
+ if (pt) {
+ this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false;
+ }
+ }
+
+ @action
onPointerUp = (e: PointerEvent): void => {
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCH) && this._points.length <= 1) return;
+
+ if (this._points.length > 1) {
+ let B = this.svgBounds;
+ let points = this._points.map(p => ({ x: p.x - B.left, y: p.y - B.top }));
+ let inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top });
+ this.addDocument(inkDoc);
+ this._points = [];
+ }
+
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener("touchmove", this.onTouch);
+ document.removeEventListener("touchend", this.onTouchEnd);
+ }
+
+ @action
+ pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
+ // I think it makes sense for the marquee menu to go away when panned. -syip2
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+
+ let x = this.Document.panX || 0;
+ let y = this.Document.panY || 0;
+ let docs = this.childLayoutPairs.map(pair => pair.layout);
+ let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+ if (!this.isAnnotationOverlay) {
+ PDFMenu.Instance.fadeOut(true);
+ let minx = docs.length ? NumCast(docs[0].x) : 0;
+ let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
+ let miny = docs.length ? NumCast(docs[0].y) : 0;
+ let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
+ let ranges = docs.filter(doc => doc).reduce((range, doc) => {
+ let layoutDoc = Doc.Layout(doc);
+ let x = NumCast(doc.x);
+ let xe = x + NumCast(layoutDoc.width);
+ let y = NumCast(doc.y);
+ let ye = y + NumCast(layoutDoc.height);
+ 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]]);
+ let ink = this.extensionDoc && Cast(this.extensionDoc.ink, InkField);
+ if (ink && ink.inkData) {
+ // ink.inkData.forEach((value: PointData, key: string) => {
+ // let bounds = InkingCanvas.StrokeRect(value);
+ // ranges[0] = [Math.min(ranges[0][0], bounds.left), Math.max(ranges[0][1], bounds.right)];
+ // ranges[1] = [Math.min(ranges[1][0], bounds.top), Math.max(ranges[1][1], bounds.bottom)];
+ // });
+ }
+
+ let cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
+ let panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
+ this.props.PanelHeight() / this.zoomScaling() * cscale);
+ if (ranges[0][0] - dx > (this.panX() + panelDim[0] / 2)) x = ranges[0][1] + panelDim[0] / 2;
+ if (ranges[0][1] - dx < (this.panX() - panelDim[0] / 2)) x = ranges[0][0] - panelDim[0] / 2;
+ if (ranges[1][0] - dy > (this.panY() + panelDim[1] / 2)) y = ranges[1][1] + panelDim[1] / 2;
+ if (ranges[1][1] - dy < (this.panY() - panelDim[1] / 2)) y = ranges[1][0] - panelDim[1] / 2;
+ }
+ this.setPan(x - dx, y - dy);
+ this._lastX = e.clientX;
+ this._lastY = e.clientY;
}
@action
onPointerMove = (e: PointerEvent): void => {
- if (!e.cancelBubble) {
- if (this._hitCluster && this.tryDragCluster(e)) {
- e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
- e.preventDefault();
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- return;
+ if (InteractionUtils.IsType(e, InteractionUtils.TOUCH)) {
+ if (this.props.active()) {
+ e.stopPropagation();
}
- let x = this.Document.panX || 0;
- let y = this.Document.panY || 0;
- let docs = this.childLayoutPairs.map(pair => pair.layout);
- let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- if (!this.isAnnotationOverlay) {
- PDFMenu.Instance.fadeOut(true);
- let minx = docs.length ? NumCast(docs[0].x) : 0;
- let maxx = docs.length ? NumCast(docs[0].width) + minx : minx;
- let miny = docs.length ? NumCast(docs[0].y) : 0;
- let maxy = docs.length ? NumCast(docs[0].height) + miny : miny;
- let ranges = docs.filter(doc => doc).reduce((range, doc) => {
- let x = NumCast(doc.x);
- let xe = x + NumCast(doc.width);
- let y = NumCast(doc.y);
- let ye = y + NumCast(doc.height);
- 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]]);
- let ink = Cast(this.fieldExtensionDoc.ink, InkField);
- if (ink && ink.inkData) {
- ink.inkData.forEach((value: StrokeData, key: string) => {
- let bounds = InkingCanvas.StrokeRect(value);
- ranges[0] = [Math.min(ranges[0][0], bounds.left), Math.max(ranges[0][1], bounds.right)];
- ranges[1] = [Math.min(ranges[1][0], bounds.top), Math.max(ranges[1][1], bounds.bottom)];
- });
+ return;
+ }
+ if (!e.cancelBubble) {
+ if (InkingControl.Instance.selectedTool === InkTool.None) {
+ if (this._hitCluster && this.tryDragCluster(e)) {
+ e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
+ e.preventDefault();
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ return;
}
-
- let cscale = this.props.ContainingCollectionDoc ? NumCast(this.props.ContainingCollectionDoc.scale) : 1;
- let panelDim = this.props.ScreenToLocalTransform().transformDirection(this.props.PanelWidth() / this.zoomScaling() * cscale,
- this.props.PanelHeight() / this.zoomScaling() * cscale);
- if (ranges[0][0] - dx > (this.panX() + panelDim[0] / 2)) x = ranges[0][1] + panelDim[0] / 2;
- if (ranges[0][1] - dx < (this.panX() - panelDim[0] / 2)) x = ranges[0][0] - panelDim[0] / 2;
- if (ranges[1][0] - dy > (this.panY() + panelDim[1] / 2)) y = ranges[1][1] + panelDim[1] / 2;
- if (ranges[1][1] - dy < (this.panY() - panelDim[1] / 2)) y = ranges[1][0] - panelDim[1] / 2;
+ this.pan(e);
+ }
+ else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ let point = this.getTransform().transformPoint(e.clientX, e.clientY);
+ this._points.push({ x: point[0], y: point[1] });
}
- this.setPan(x - dx, y - dy);
- this._lastX = e.pageX;
- this._lastY = e.pageY;
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
}
}
+ handle1PointerMove = (e: TouchEvent) => {
+ // panning a workspace
+ if (!e.cancelBubble) {
+ let pt = e.targetTouches.item(0);
+ if (pt) {
+ if (InkingControl.Instance.selectedTool === InkTool.None) {
+ if (this._hitCluster && this.tryDragCluster(e)) {
+ e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
+ e.preventDefault();
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ return;
+ }
+ this.pan(pt);
+ }
+ else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) {
+ let point = this.getTransform().transformPoint(pt.clientX, pt.clientY);
+ this._points.push({ x: point[0], y: point[1] });
+ }
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }
+
+ handle2PointersMove = (e: TouchEvent) => {
+ // pinch zooming
+ if (!e.cancelBubble) {
+ let pt1: Touch | null = e.targetTouches.item(0);
+ let pt2: Touch | null = e.targetTouches.item(1);
+ if (!pt1 || !pt2) return;
+
+ if (this.prevPoints.size === 2) {
+ let oldPoint1 = this.prevPoints.get(pt1.identifier);
+ let oldPoint2 = this.prevPoints.get(pt2.identifier);
+ if (oldPoint1 && oldPoint2) {
+ let dir = InteractionUtils.Pinching(pt1, pt2, oldPoint1, oldPoint2);
+
+ // if zooming, zoom
+ if (dir !== 0) {
+ let d1 = Math.sqrt(Math.pow(pt1.clientX - oldPoint1.clientX, 2) + Math.pow(pt1.clientY - oldPoint1.clientY, 2));
+ let d2 = Math.sqrt(Math.pow(pt2.clientX - oldPoint2.clientX, 2) + Math.pow(pt2.clientY - oldPoint2.clientY, 2));
+ let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+
+ // calculate the raw delta value
+ let rawDelta = (dir * (d1 + d2));
+
+ // this floors and ceils the delta value to prevent jitteriness
+ let delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 16);
+ this.zoom(centerX, centerY, delta);
+ this.prevPoints.set(pt1.identifier, pt1);
+ this.prevPoints.set(pt2.identifier, pt2);
+ }
+ // this is not zooming. derive some form of panning from it.
+ else {
+ // use the centerx and centery as the "new mouse position"
+ let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+ this.pan({ clientX: centerX, clientY: centerY });
+ this._lastX = centerX;
+ this._lastY = centerY;
+ }
+ }
+ }
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ handle2PointersDown = (e: React.TouchEvent) => {
+ let pt1: React.Touch | null = e.targetTouches.item(0);
+ let pt2: React.Touch | null = e.targetTouches.item(1);
+ if (!pt1 || !pt2) return;
+
+ let centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
+ let centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
+ this._lastX = centerX;
+ this._lastY = centerY;
+ }
+
+ cleanUpInteractions = () => {
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener("touchmove", this.onTouch);
+ document.removeEventListener("touchend", this.onTouchEnd);
+ }
+
+ @action
+ zoom = (pointX: number, pointY: number, deltaY: number): void => {
+ let deltaScale = deltaY > 0 ? (1 / 1.1) : 1.1;
+ if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
+ deltaScale = 1 / this.zoomScaling();
+ }
+ if (deltaScale < 0) deltaScale = -deltaScale;
+ let [x, y] = this.getTransform().transformPoint(pointX, pointY);
+ let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
+
+ let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+ this.props.Document.scale = Math.abs(safeScale);
+ this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+ }
+
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.props.Document.lockedPosition || this.props.Document.inOverlay) return;
+ if (this.props.Document.lockedTransform || this.props.Document.inOverlay) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
else if (this.props.active()) {
e.stopPropagation();
- let deltaScale = e.deltaY > 0 ? (1 / 1.1) : 1.1;
- if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
- deltaScale = 1 / this.zoomScaling();
- }
- if (deltaScale < 0) deltaScale = -deltaScale;
- let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY);
- let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
-
- let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
- this.props.Document.scale = Math.abs(safeScale);
- this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
+ this.zoom(e.clientX, e.clientY, e.deltaY);
}
}
@action
- setPan(panX: number, panY: number) {
- if (!this.props.Document.lockedPosition || this.props.Document.inOverlay) {
- this.props.Document.panTransformType = "None";
+ setPan(panX: number, panY: number, panType: string = "None") {
+ if (!this.Document.lockedTransform || this.Document.inOverlay) {
+ this.Document.panTransformType = panType;
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
- const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.props.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
- this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
- this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+ const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
+ this.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+ this.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
}
}
@@ -412,19 +562,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
this.props.Document.scrollY = NumCast(doc.y) - offset;
}
} else {
- const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2;
- const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2;
+ let layoutdoc = Doc.Layout(doc);
+ const newPanX = NumCast(doc.x) + NumCast(layoutdoc.width) / 2;
+ const newPanY = NumCast(doc.y) + NumCast(layoutdoc.height) / 2;
const newState = HistoryUtil.getState();
newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType };
- this.setPan(newPanX, newPanY);
- this.Document.panTransformType = "Ease";
+ this.setPan(newPanX, newPanY, "Ease");
Doc.BrushDoc(this.props.Document);
this.props.focus(this.props.Document);
- willZoom && this.setScaleToZoom(doc, scale);
+ willZoom && this.setScaleToZoom(layoutdoc, scale);
afterFocus && setTimeout(() => {
if (afterFocus && afterFocus()) {
@@ -453,6 +603,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
...this.props,
DataDoc: childData,
Document: childLayout,
+ layoutKey: undefined,
ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves
onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them
ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
@@ -470,30 +621,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getScale: this.getScale
};
}
- getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
- return {
- ...this.props,
- ScreenToLocalTransform: this.getTransform,
- PanelWidth: layoutDoc[WidthSym],
- PanelHeight: layoutDoc[HeightSym],
- ContentScaling: returnOne,
- ContainingCollectionView: this.props.ContainingCollectionView,
- focus: this.focusDocument,
- backgroundColor: returnEmptyString,
- parentActive: this.props.active,
- bringToFront: this.bringToFront,
- zoomToScale: this.zoomToScale,
- getScale: this.getScale
- };
- }
getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, z?: number, width?: number, height?: number, transition?: string, state?: any } {
const script = this.Document.arrangeScript;
const result = script && script.script.run(params, console.log);
+ const layoutDoc = Doc.Layout(params.doc);
if (result && result.success) {
return { ...result, transition: "transform 1s" };
}
- return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(params.doc.width, "number"), height: Cast(params.doc.height, "number") };
+ return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc.width, "number"), height: Cast(layoutDoc.height, "number") };
}
viewDefsToJSX = (views: any[]) => {
@@ -610,7 +746,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// find rule colorations when rule providing is turned on by looking at each document to see if it has a coloring -- if so, use it's color as the rule for its associated heading.
this.Document.isRuleProvider && this.childLayoutPairs.map(pair =>
// iterate over the children of a displayed document (or if the displayed document is a template, iterate over the children of that template)
- DocListCast(pair.layout.layout instanceof Doc ? pair.layout.layout.data : pair.layout.data).map(heading => {
+ DocListCast(Doc.Layout(pair.layout).data).map(heading => {
let headingPair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, heading);
let headingLayout = headingPair.layout && (pair.layout.data_ext instanceof Doc) && (pair.layout.data_ext[`Layout[${headingPair.layout[Id]}]`] as Doc) || headingPair.layout;
if (headingLayout && NumCast(headingLayout.heading) > 0 && headingLayout.backgroundColor !== headingLayout.defaultBackgroundColor) {
@@ -621,16 +757,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
analyzeStrokes = async () => {
- let data = Cast(this.fieldExtensionDoc.ink, InkField);
- if (data) {
- CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.fieldExtensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
+ const extensionDoc = this.extensionDoc;
+ let data = extensionDoc && Cast(extensionDoc.ink, InkField);
+ if (data && extensionDoc) {
+ CognitiveServices.Inking.Appliers.ConcatenateHandwriting(extensionDoc, ["inkAnalysis", "handwriting"], data.inkData);
}
}
onContextMenu = (e: React.MouseEvent) => {
let layoutItems: ContextMenuProps[] = [];
- if (this.childDocs.some(d => BoolCast(d.isTemplate))) {
+ if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) {
layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
}
layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" });
@@ -684,58 +821,64 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private childViews = () => {
let children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
return [
- <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
...children,
...this.views,
];
}
+
+ @computed get svgBounds() {
+ let xs = this._points.map(p => p.x);
+ let ys = this._points.map(p => p.y);
+ let right = Math.max(...xs);
+ let left = Math.min(...xs);
+ let bottom = Math.max(...ys);
+ let top = Math.min(...ys);
+ return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top };
+ }
+
+ @computed get currentStroke() {
+ if (this._points.length <= 1) {
+ return (null);
+ }
+
+ let B = this.svgBounds;
+
+ return (
+ <svg width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)` }}>
+ {CreatePolyline(this._points, B.left, B.top)}
+ </svg>
+ );
+ }
+
+ children = () => {
+ let eles: JSX.Element[] = [];
+ this.currentStroke && (eles.push(this.currentStroke));
+ this.extensionDoc && (eles.push(...this.childViews()));
+ eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />);
+ return eles;
+ }
render() {
+ trace();
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
- this.props.Document.fitX = this.contentBounds && this.contentBounds.x;
- this.props.Document.fitY = this.contentBounds && this.contentBounds.y;
- this.props.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
- this.props.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
- // if fieldExt is set, then children will be stored in the extension document for the fieldKey.
+ this.Document.fitX = this.contentBounds && this.contentBounds.x;
+ this.Document.fitY = this.contentBounds && this.contentBounds.y;
+ this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
+ 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
- Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey);
- return (
+ return !this.extensionDoc ? (null) :
<div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (NumCast(this.props.Document.scrollHeight) ? NumCast(this.props.Document.scrollHeight) : "100%") : this.props.PanelHeight() }}
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}>
- <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
- addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} setPreviewCursor={this.props.setPreviewCursor}
- getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
+ style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
+ onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
+ <MarqueeView {...this.props} extensionDoc={this.extensionDoc} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
+ addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
<CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
easing={this.easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
- <CollectionFreeFormLinksView {...this.props} key="freeformLinks">
- <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} >
- {this.childViews}
- </InkingCanvas>
- </CollectionFreeFormLinksView>
- <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+ {this.children}
</CollectionFreeFormViewPannableContents>
</MarqueeView>
{this.overlayViews}
- <CollectionFreeFormOverlayView {...this.props} {...this.getDocumentViewProps(this.props.Document)} />
- </div>
- );
- }
-}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
- render() {
- return <DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
- renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />;
- }
-}
-
-@observer
-class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
- render() {
- return !this.props.Document.backgroundLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
- renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />);
+ </div>;
}
}
@@ -746,6 +889,7 @@ interface CollectionFreeFormViewPannableContentsProps {
panY: () => number;
zoomScaling: () => number;
easing: () => boolean;
+ children: () => JSX.Element[];
}
@observer
@@ -758,7 +902,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const pany = -this.props.panY();
const zoom = this.props.zoomScaling();
return <div className={freeformclass} style={{ borderRadius: "inherit", transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)` }}>
- {this.props.children}
+ {this.props.children()}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
new file mode 100644
index 000000000..91fcad4be
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -0,0 +1,46 @@
+import React = require("react")
+import AntimodeMenu from "../../AntimodeMenu";
+import { observer } from "mobx-react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { unimplementedFunction } from "../../../../Utils";
+
+@observer
+export default class MarqueeOptionsMenu extends AntimodeMenu {
+ static Instance: MarqueeOptionsMenu;
+
+ public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public showMarquee: () => void = unimplementedFunction;
+ public hideMarquee: () => void = unimplementedFunction;
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+
+ MarqueeOptionsMenu.Instance = this;
+ }
+
+ render() {
+ let buttons = [
+ <button
+ className="antimodeMenu-button"
+ title="Create a Collection"
+ onPointerDown={this.createCollection}>
+ <FontAwesomeIcon icon="object-group" size="lg" />
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Summarize Documents"
+ onPointerDown={this.summarize}>
+ <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Delete Documents"
+ onPointerDown={this.delete}>
+ <FontAwesomeIcon icon="trash-alt" size="lg" />
+ </button>,
+ ]
+ return this.getElement(buttons);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 04f6ec2ad..d14495626 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -8,6 +8,7 @@
}
.marqueeView {
overflow: hidden;
+ pointer-events: inherit;
}
.marqueeView:focus-within {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ecdd02b0f..1066f4f8d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../../new_fields/Doc";
-import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { InkField, PointData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { listSpec } from "../../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
@@ -13,29 +13,31 @@ import { Docs } from "../../../documents/Documents";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
import { undoBatch } from "../../../util/UndoManager";
-import { InkingCanvas } from "../../InkingCanvas";
import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionViewType } from "../CollectionBaseView";
+import { CollectionViewType } from "../CollectionView";
import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
+import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import InkSelectDecorations from "../../InkSelectDecorations";
+import { SubCollectionViewProps } from "../CollectionSubView";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
getTransform: () => Transform;
- container: CollectionFreeFormView;
addDocument: (doc: Doc) => boolean;
activeDocuments: () => Doc[];
- selectDocuments: (docs: Doc[]) => void;
+ selectDocuments: (docs: Doc[], ink: { Document: Doc, Ink: Map<any, any> }[]) => void;
removeDocument: (doc: Doc) => boolean;
addLiveTextDocument: (doc: Doc) => void;
isSelected: () => boolean;
- isAnnotationOverlay: boolean;
+ extensionDoc: Doc;
+ isAnnotationOverlay?: boolean;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
}
@observer
-export class MarqueeView extends React.Component<MarqueeViewProps>
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
{
private _mainCont = React.createRef<HTMLDivElement>();
@observable _lastX: number = 0;
@@ -50,13 +52,15 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
@action
- cleanupInteractions = (all: boolean = false) => {
+ cleanupInteractions = (all: boolean = false, hideMarquee: boolean = true) => {
if (all) {
document.removeEventListener("pointerup", this.onPointerUp, true);
document.removeEventListener("pointermove", this.onPointerMove, true);
}
document.removeEventListener("keydown", this.marqueeCommand, true);
- this._visible = false;
+ if (hideMarquee) {
+ this._visible = false;
+ }
}
@undoBatch
@@ -187,15 +191,33 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerUp = (e: PointerEvent): void => {
- if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
+ if (!this.props.active()) this.props.selectDocuments([this.props.Document], []);
if (this._visible) {
let mselect = this.marqueeSelect();
if (!e.shiftKey) {
- SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document);
+ SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
}
- this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
+ // let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
+ // let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
+ let docs = mselect.length ? mselect : [this.props.Document];
+ this.props.selectDocuments(docs, []);
}
- this.cleanupInteractions(true);
+ if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
+ MarqueeOptionsMenu.Instance.createCollection = this.collection;
+ MarqueeOptionsMenu.Instance.delete = this.delete;
+ MarqueeOptionsMenu.Instance.summarize = this.summary;
+ MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
+ MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
+ MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
+ }
+ this.cleanupInteractions(true, this._commandExecuted);
+
+ let hideMarquee = () => {
+ this.hideMarquee();
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ document.removeEventListener("pointerdown", hideMarquee);
+ };
+ document.addEventListener("pointerdown", hideMarquee);
if (e.altKey) {
e.preventDefault();
@@ -245,14 +267,130 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
}
+ get inkDoc() {
+ return this.props.extensionDoc;
+ }
+
get ink() { // ink will be stored on the extension doc for the field (fieldKey) where the container's data is stored.
- let cprops = this.props.container.props;
- return Cast(Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink, InkField);
+ return this.props.extensionDoc && Cast(this.props.extensionDoc.ink, InkField);
}
set ink(value: InkField | undefined) {
- let cprops = this.props.container.props;
- Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink = value;
+ this.props.extensionDoc && (this.props.extensionDoc.ink = value);
+ }
+
+ @action
+ showMarquee = () => {
+ this._visible = true;
+ }
+
+ @action
+ hideMarquee = () => {
+ this._visible = false;
+ }
+
+ @action
+ delete = () => {
+ this.marqueeSelect(false).map(d => this.props.removeDocument(d));
+ if (this.ink) {
+ // this.marqueeInkDelete(this.ink.inkData);
+ }
+ SelectionManager.DeselectAll();
+ this.cleanupInteractions(false);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ this.hideMarquee();
+ }
+
+ getCollection = (selected: Doc[]) => {
+ let bounds = this.Bounds;
+ let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+ "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
+ let colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
+ if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
+ let palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
+ let usedPaletted = new Map<string, number>();
+ [...this.props.activeDocuments(), this.props.Document].map(child => {
+ let bg = StrCast(Doc.Layout(child).backgroundColor);
+ if (palette.indexOf(bg) !== -1) {
+ palette.splice(palette.indexOf(bg), 1);
+ if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
+ else usedPaletted.set(bg, 1);
+ }
+ });
+ usedPaletted.delete("#f1efeb");
+ usedPaletted.delete("white");
+ usedPaletted.delete("rgba(255,255,255,1)");
+ let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
+ let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
+ let inkData = this.ink ? this.ink.inkData : undefined;
+ let newCollection = Docs.Create.FreeformDocument(selected, {
+ x: bounds.left,
+ y: bounds.top,
+ panX: 0,
+ panY: 0,
+ backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+ defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+ width: bounds.width,
+ height: bounds.height,
+ title: "a nested collection",
+ });
+ let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
+ // dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
+ // this.marqueeInkDelete(inkData);
+ this.hideMarquee();
+ return newCollection;
+ }
+
+ @action
+ collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect(false);
+ if (e instanceof KeyboardEvent ? e.key === "c" : true) {
+ selected.map(d => {
+ this.props.removeDocument(d);
+ d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ d.displayTimecode = undefined;
+ return d;
+ });
+ }
+ let newCollection = this.getCollection(selected);
+ this.props.addDocument(newCollection);
+ this.props.selectDocuments([newCollection], []);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ this.hideMarquee();
+ }
+
+ @action
+ summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect(false);
+ let newCollection = this.getCollection(selected);
+
+ selected.map(d => {
+ this.props.removeDocument(d);
+ d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ d.page = -1;
+ return d;
+ });
+ newCollection.chromeStatus = "disabled";
+ let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
+ newCollection.x = bounds.left + bounds.width;
+ Doc.GetProto(newCollection).summaryDoc = summary;
+ Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
+ if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
+ let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+ container.viewType = CollectionViewType.Stacking;
+ container.autoHeight = true;
+ Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
+ this.props.addLiveTextDocument(container);
+ } else if (e instanceof KeyboardEvent ? e.key === "S" : false) { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
+ Doc.GetProto(summary).maximizeLocation = "inTab"; // or "inPlace", or "onRight"
+ this.props.addLiveTextDocument(summary);
+ }
+ MarqueeOptionsMenu.Instance.fadeOut(true);
}
@undoBatch
@@ -265,12 +403,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._commandExecuted = true;
e.stopPropagation();
(e as any).propagationIsStopped = true;
- this.marqueeSelect(false).map(d => this.props.removeDocument(d));
- if (this.ink) {
- this.marqueeInkDelete(this.ink.inkData);
- }
- SelectionManager.DeselectAll();
- this.cleanupInteractions(false);
+ this.delete();
e.stopPropagation();
}
if (e.key === "c" || e.key === "s" || e.key === "S") {
@@ -278,136 +411,76 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
e.stopPropagation();
e.preventDefault();
(e as any).propagationIsStopped = true;
- let bounds = this.Bounds;
- let selected = this.marqueeSelect(false);
if (e.key === "c") {
- selected.map(d => {
- this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.displayTimecode = undefined;
- return d;
- });
+ this.collection(e);
}
- let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
- "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
- let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string"));
- if (!colorPalette) this.props.container.props.Document.colorPalette = new List<string>(defaultPalette);
- let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
- let usedPaletted = new Map<string, number>();
- [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
- let bg = StrCast(child.layout instanceof Doc ? child.layout.backgroundColor : child.backgroundColor);
- if (palette.indexOf(bg) !== -1) {
- palette.splice(palette.indexOf(bg), 1);
- if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
- else usedPaletted.set(bg, 1);
- }
- });
- usedPaletted.delete("#f1efeb");
- usedPaletted.delete("white");
- usedPaletted.delete("rgba(255,255,255,1)");
- let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
- let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
- let inkData = this.ink ? this.ink.inkData : undefined;
- let newCollection = Docs.Create.FreeformDocument(selected, {
- x: bounds.left,
- y: bounds.top,
- panX: 0,
- panY: 0,
- backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
- defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
- width: bounds.width,
- height: bounds.height,
- title: "a nested collection",
- });
- let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
- dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
- this.marqueeInkDelete(inkData);
if (e.key === "s" || e.key === "S") {
- selected.map(d => {
- this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
- return d;
- });
- newCollection.chromeStatus = "disabled";
- let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
- Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
- newCollection.x = bounds.left + bounds.width;
- Doc.GetProto(newCollection).summaryDoc = summary;
- Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
- if (e.key === "s") { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
- let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
- container.viewType = CollectionViewType.Stacking;
- container.autoHeight = true;
- Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
- this.props.addLiveTextDocument(container);
- } else if (e.key === "S") { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
- Doc.GetProto(summary).maximizeLocation = "inTab"; // or "inPlace", or "onRight"
- this.props.addLiveTextDocument(summary);
- }
- }
- else {
- this.props.addDocument(newCollection);
- this.props.selectDocuments([newCollection]);
+ this.summary(e);
}
this.cleanupInteractions(false);
}
}
- @action
- marqueeInkSelect(ink: Map<any, any>) {
- let idata = new Map();
- let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
- let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
- ink.forEach((value: StrokeData, key: string, map: any) => {
- if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
- idata.set(key,
- {
- pathData: value.pathData.map(val => ({ x: val.x + centerShiftX, y: val.y + centerShiftY })),
- color: value.color,
- width: value.width,
- tool: value.tool,
- page: -1
- });
- }
- });
- return idata;
- }
+ // @action
+ // marqueeInkSelect(ink: Map<any, any>) {
+ // let idata = new Map();
+ // let centerShiftX = 0 - (this.Bounds.left + this.Bounds.width / 2); // moves each point by the offset that shifts the selection's center to the origin.
+ // let centerShiftY = 0 - (this.Bounds.top + this.Bounds.height / 2);
+ // ink.forEach((value: PointData, key: string, map: any) => {
+ // if (InkingCanvas.IntersectStrokeRect(value, this.Bounds)) {
+ // // let transform = this.props.container.props.ScreenToLocalTransform().scale(this.props.container.props.ContentScaling());
+ // idata.set(key,
+ // {
+ // pathData: value.pathData.map(val => {
+ // let tVal = this.props.getTransform().inverse().transformPoint(val.x, val.y);
+ // return { x: tVal[0], y: tVal[1] };
+ // // return { x: val.x + centerShiftX, y: val.y + centerShiftY }
+ // }),
+ // color: value.color,
+ // width: value.width,
+ // tool: value.tool,
+ // page: -1
+ // });
+ // }
+ // });
+ // // InkSelectDecorations.Instance.SetSelected(idata);
+ // return idata;
+ // }
- @action
- marqueeInkDelete(ink?: Map<any, any>) {
- // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
- // ink.forEach((value: StrokeData, key: string, map: any) =>
- // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
+ // @action
+ // marqueeInkDelete(ink?: Map<any, any>) {
+ // // bcz: this appears to work but when you restart all the deleted strokes come back -- InkField isn't observing its changes so they aren't written to the DB.
+ // // ink.forEach((value: StrokeData, key: string, map: any) =>
+ // // InkingCanvas.IntersectStrokeRect(value, this.Bounds) && ink.delete(key));
- if (ink) {
- let idata = new Map();
- ink.forEach((value: StrokeData, key: string, map: any) =>
- !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
- this.ink = new InkField(idata);
- }
- }
+ // if (ink) {
+ // let idata = new Map();
+ // ink.forEach((value: PointData, key: string, map: any) =>
+ // !InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
+ // this.ink = new InkField(idata);
+ // }
+ // }
marqueeSelect(selectBackgrounds: boolean = true) {
let selRect = this.Bounds;
let selection: Doc[] = [];
this.props.activeDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
});
if (!selection.length && selectBackgrounds) {
this.props.activeDocuments().filter(doc => doc.z === undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) {
selection.push(doc);
}
@@ -420,10 +493,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
let size = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
let otherBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
this.props.activeDocuments().filter(doc => doc.z !== undefined).map(doc => {
+ let layoutDoc = Doc.Layout(doc);
var x = NumCast(doc.x);
var y = NumCast(doc.y);
- var w = NumCast(doc.width);
- var h = NumCast(doc.height);
+ var w = NumCast(layoutDoc.width);
+ var h = NumCast(layoutDoc.height);
if (this.intersectRect({ left: x, top: y, width: w, height: h }, otherBounds)) {
selection.push(doc);
}
@@ -434,21 +508,22 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@computed
get marqueeDiv() {
+ let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- return <div className="marquee" style={{ width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}`, zIndex: 2000 }} >
- <span className="marquee-legend" />
+ /**
+ * @RE - The commented out span below
+ * This contains the "C for collection, ..." text on marquees.
+ * Commented out by syip2 when the marquee menu was added.
+ */
+ return <div className="marquee" style={{ transform: `translate(${p[0]}px, ${p[1]}px)`, width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}`, zIndex: 2000 }} >
+ {/* <span className="marquee-legend" /> */}
</div>;
}
render() {
- let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
- return <div className="marqueeView" onScroll={(e) => e.currentTarget.scrollLeft = 0} style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
- <div style={{ position: "relative", transform: `translate(${p[0]}px, ${p[1]}px)` }} onScroll={(e) => e.currentTarget.scrollLeft = 0} >
- {this._visible ? this.marqueeDiv : null}
- <div ref={this._mainCont} style={{ transform: `translate(${-p[0]}px, ${-p[1]}px)` }} >
- {this.props.children}
- </div>
- </div>
+ return <div className="marqueeView" onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} style={{ borderRadius: "inherit" }} onClick={this.onClick} onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
</div>;
}
} \ No newline at end of file