aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorab <abdullah_ahmed@brown.edu>2019-07-30 11:44:19 -0400
committerab <abdullah_ahmed@brown.edu>2019-07-30 11:44:19 -0400
commitd58d66f29eae28ac8244f8c2bcaa423e0aa99743 (patch)
tree6a34daa4fd4111470763f6b5c215c95fcd254f52
parent38b5d646e62535504eb8667b840bf36cd7f2f6d8 (diff)
parentca91fe9e379fe3e2d48a0eb055415f008245ed31 (diff)
merged
-rw-r--r--src/.DS_Storebin6148 -> 6148 bytes
-rw-r--r--src/client/cognitive_services/CognitiveServices.ts40
-rw-r--r--src/client/documents/Documents.ts12
-rw-r--r--src/client/util/DragManager.ts1
-rw-r--r--src/client/views/ContextMenuItem.tsx13
-rw-r--r--src/client/views/MainOverlayTextBox.tsx8
-rw-r--r--src/client/views/MainView.tsx22
-rw-r--r--src/client/views/MetadataEntryMenu.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx21
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx3
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx39
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss50
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx79
-rw-r--r--src/client/views/collections/CollectionStackingView.scss7
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx8
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx23
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx11
-rw-r--r--src/client/views/collections/CollectionView.tsx37
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss7
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx4
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx7
-rw-r--r--src/client/views/nodes/ButtonBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx20
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx2
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx9
-rw-r--r--src/client/views/nodes/ImageBox.tsx57
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx19
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx8
-rw-r--r--src/client/views/nodes/PDFBox.tsx14
-rw-r--r--src/client/views/pdf/Annotation.tsx12
-rw-r--r--src/client/views/pdf/Page.tsx2
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx113
-rw-r--r--src/client/views/presentationview/PresentationModeMenu.scss30
-rw-r--r--src/client/views/presentationview/PresentationModeMenu.tsx92
-rw-r--r--src/client/views/presentationview/PresentationView.scss15
-rw-r--r--src/client/views/presentationview/PresentationView.tsx218
-rw-r--r--src/new_fields/Doc.ts3
-rw-r--r--src/new_fields/SchemaHeaderField.ts4
-rw-r--r--src/scraping/buxton/scraper.py4
-rw-r--r--src/server/index.ts18
43 files changed, 831 insertions, 262 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
index 071dafa1e..c544bc837 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index bbc438a9b..076907f09 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -42,7 +42,7 @@ export enum Confidence {
*/
export namespace CognitiveServices {
- const executeQuery = async <D, R>(service: Service, manager: APIManager<D>, data: D): Promise<Opt<R>> => {
+ const ExecuteQuery = async <D, R>(service: Service, manager: APIManager<D>, data: D): Promise<Opt<R>> => {
return fetch(Utils.prepend(`${RouteStore.cognitiveServices}/${service}`)).then(async response => {
let apiKey = await response.text();
if (!apiKey) {
@@ -103,15 +103,15 @@ export namespace CognitiveServices {
return request.post(options);
},
- analyzer: async (target: Doc, keys: string[], service: Service, converter: Converter) => {
+ analyzer: async (target: Doc, keys: string[], url: string, service: Service, converter: Converter) => {
let batch = UndoManager.StartBatch("Image Analysis");
- let imageData = Cast(target.data, ImageField);
+
let storageKey = keys[0];
- if (!imageData || await Cast(target[storageKey], Doc)) {
+ if (!url || await Cast(target[storageKey], Doc)) {
return;
}
let toStore: any;
- let results = await executeQuery<string, any>(service, Manager, imageData.url.href);
+ let results = await ExecuteQuery<string, any>(service, Manager, url);
if (!results) {
toStore = "Cognitive Services could not process the given image URL.";
} else {
@@ -122,6 +122,7 @@ export namespace CognitiveServices {
}
}
target[storageKey] = toStore;
+
batch.end();
}
@@ -129,31 +130,6 @@ export namespace CognitiveServices {
export type Face = { faceAttributes: any, faceId: string, faceRectangle: Rectangle };
- export const generateMetadata = async (target: Doc, threshold: Confidence = Confidence.Excellent) => {
- let converter = (results: any) => {
- let tagDoc = new Doc;
- results.tags.map((tag: Tag) => {
- let sanitized = tag.name.replace(" ", "_");
- let script = `return (${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`;
- let computed = CompileScript(script, { params: { this: "Doc" } });
- computed.compiled && (tagDoc[sanitized] = new ComputedField(computed));
- });
- tagDoc.title = "Generated Tags";
- tagDoc.confidence = threshold;
- return tagDoc;
- };
- Manager.analyzer(target, ["generatedTags"], Service.ComputerVision, converter);
- };
-
- export const extractFaces = async (target: Doc) => {
- let converter = (results: any) => {
- let faceDocs = new List<Doc>();
- results.map((face: Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!));
- return faceDocs;
- };
- Manager.analyzer(target, ["faces"], Service.Face, converter);
- };
-
}
export namespace Inking {
@@ -209,7 +185,8 @@ export namespace CognitiveServices {
analyzer: async (target: Doc, keys: string[], inkData: InkData) => {
let batch = UndoManager.StartBatch("Ink Analysis");
- let results = await executeQuery<InkData, any>(Service.Handwriting, Manager, inkData);
+
+ let results = await ExecuteQuery<InkData, any>(Service.Handwriting, Manager, inkData);
if (results) {
results.recognitionUnits && (results = results.recognitionUnits);
target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis");
@@ -217,6 +194,7 @@ export namespace CognitiveServices {
let individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1);
target[keys[1]] = individualWords.join(" ");
}
+
batch.end();
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ee1b9fd0d..d1b3071ed 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -82,6 +82,7 @@ export interface DocumentOptions {
backgroundColor?: string;
dropAction?: dropActionType;
backgroundLayout?: string;
+ chromeStatus?: string;
curPage?: number;
documentText?: string;
borderRounding?: string;
@@ -164,7 +165,6 @@ export namespace Docs {
[DocumentType.LINKDOC, {
data: new List<Doc>(),
layout: { view: EmptyBox },
- options: {}
}],
[DocumentType.BUTTON, {
layout: { view: ButtonBox },
@@ -404,23 +404,23 @@ export namespace Docs {
}
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform });
}
export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema });
}
export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree });
}
export function StackingDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking });
}
export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry });
}
export function ButtonDocument(options?: DocumentOptions) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9221ef274..abcc3a4e1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -216,6 +216,7 @@ export namespace DragManager {
this.annotationDocument = annotationDoc;
this.xOffset = this.yOffset = 0;
}
+ targetContext: Doc | undefined;
dragDocument: Doc;
annotationDocument: Doc;
dropDocument: Doc;
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index badb9cf19..a1787e78f 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -4,12 +4,14 @@ import { observer } from "mobx-react";
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { UndoManager } from "../util/UndoManager";
library.add(faAngleRight);
export interface OriginalMenuProps {
description: string;
event: () => void;
+ undoable?: boolean;
icon: IconProp; //maybe should be optional (icon?)
closeMenu?: () => void;
}
@@ -35,9 +37,14 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
}
}
- handleEvent = (e: React.MouseEvent<HTMLDivElement>) => {
+ handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => {
if ("event" in this.props) {
- this.props.event();
+ let batch: UndoManager.Batch | undefined;
+ if (this.props.undoable !== false) {
+ batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`);
+ }
+ await this.props.event();
+ batch && batch.end();
this.props.closeMenu && this.props.closeMenu();
}
}
@@ -94,7 +101,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
) : null}
<div className="contextMenu-description">
{this.props.description}
- <FontAwesomeIcon icon={faAngleRight} size="lg" style={{ position: "absolute", right: "10px"}} />
+ <FontAwesomeIcon icon={faAngleRight} size="lg" style={{ position: "absolute", right: "10px" }} />
</div>
{submenu}
</div>
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index b2fb11afe..72eb956e3 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -4,7 +4,7 @@ import "normalize.css";
import * as React from 'react';
import { Doc } from '../../new_fields/Doc';
import { BoolCast } from '../../new_fields/Types';
-import { emptyFunction, returnTrue, returnZero, Utils } from '../../Utils';
+import { emptyFunction, returnTrue, returnZero, Utils, returnOne } from '../../Utils';
import { DragManager } from '../util/DragManager';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -30,6 +30,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
private _outerdiv: HTMLElement | null = null;
private _textBox: FormattedTextBox | undefined;
private _tooltip?: HTMLElement;
+ ChromeHeight?: () => number;
@observable public TextDoc?: Doc;
@observable public TextDataDoc?: Doc;
@@ -50,6 +51,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
(box?: FormattedTextBox) => {
this._textBox = box;
if (box) {
+ this.ChromeHeight = box.props.ChromeHeight;
this.TextDoc = box.props.Document;
this.TextDataDoc = box.props.DataDoc;
let xf = () => {
@@ -141,8 +143,8 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
Document={FormattedTextBox.InputBoxOverlay.props.Document}
DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc}
isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true}
- ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
- ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} firstinstance={this.props.firstinstance} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
+ ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne}
+ ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
</div>
</div>
</div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 80b674092..1726b328f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -92,6 +92,8 @@ export class MainView extends React.Component {
componentWillUnMount() {
window.removeEventListener("keydown", KeyManager.Instance.handle);
+ window.removeEventListener("pointerdown", this.globalPointerDown);
+ window.removeEventListener("pointerup", this.globalPointerUp);
}
constructor(props: Readonly<{}>) {
@@ -138,18 +140,23 @@ export class MainView extends React.Component {
this.initAuthenticationRouters();
}
+ globalPointerDown = action((e: PointerEvent) => {
+ this.isPointerDown = true;
+ const targets = document.elementsFromPoint(e.x, e.y);
+ if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) {
+ ContextMenu.Instance.closeMenu();
+ }
+ });
+
+ globalPointerUp = () => this.isPointerDown = false;
+
initEventListeners = () => {
// window.addEventListener("pointermove", (e) => this.reportLocation(e))
window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler
window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler
// click interactions for the context menu
- document.addEventListener("pointerdown", action((e: PointerEvent) => {
- this.isPointerDown = true;
- const targets = document.elementsFromPoint(e.x, e.y);
- if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) {
- ContextMenu.Instance.closeMenu();
- }
- }), true);
+ document.addEventListener("pointerdown", this.globalPointerDown);
+ document.addEventListener("pointerup", this.globalPointerUp);
}
initAuthenticationRouters = async () => {
@@ -292,7 +299,6 @@ export class MainView extends React.Component {
}
@action
onPointerUp = (e: PointerEvent) => {
- this.isPointerDown = false;
if (Math.abs(e.clientX - this._downsize) < 4) {
if (this.flyoutWidth < 5) this.flyoutWidth = 250;
else this.flyoutWidth = 0;
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 652e0e91a..36c240dd8 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -5,6 +5,7 @@ import { observable, action, runInAction, trace } from 'mobx';
import { KeyValueBox } from './nodes/KeyValueBox';
import { Doc, Field } from '../../new_fields/Doc';
import * as Autosuggest from 'react-autosuggest';
+import { undoBatch } from '../util/UndoManager';
export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
export interface MetadataEntryProps {
@@ -74,6 +75,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
this.userModified = e.target.value.trim() !== "";
}
+ @undoBatch
@action
onValueKeyDown = async (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1859ebee7..588102f01 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -210,8 +210,23 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
docs.push(document);
}
let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument);
- var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout);
- stack.addChild(newContentItem.contentItems[0], undefined);
+ if (stack === undefined) {
+ let stack: any = this._goldenLayout.root;
+ while (!stack.isStack) {
+ if (stack.contentItems.length) {
+ stack = stack.contentItems[0];
+ } else {
+ stack.addChild({ type: 'stack', content: [docContentConfig] });
+ stack = undefined;
+ break;
+ }
+ }
+ if (stack) {
+ stack.addChild(docContentConfig);
+ }
+ } else {
+ stack.addChild(docContentConfig, undefined);
+ }
this.layoutChanged();
}
@@ -561,7 +576,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
return Transform.Identity();
}
- get previewPanelCenteringOffset() { return this.nativeWidth && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get previewPanelCenteringOffset() { return this.nativeWidth && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth()) / 2 : 0; }
addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
if (doc.dockingConfig) {
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 194765880..fdf0896ac 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -115,7 +115,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
}
- private dropRef = (ele: HTMLElement) => {
+ private dropRef = (ele: HTMLElement | null) => {
this._dropDisposer && this._dropDisposer();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
@@ -154,6 +154,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
PanelHeight: returnZero,
PanelWidth: returnZero,
addDocTab: this.props.addDocTab,
+ ContentScaling: returnOne
};
let field = props.Document[props.fieldKey];
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 9fc28eafa..387107c55 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -129,6 +129,10 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
}
+ changeColumnColor = (color: string): void => {
+
+ }
+
renderTypes = () => {
if (this.props.typeConst) return <></>;
return (
@@ -168,6 +172,40 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
);
}
+ renderColors = () => {
+ return (
+ <div className="collectionSchema-headerMenu-group">
+ <label>Color:</label>
+ <div className="columnMenu-colors">
+ <input type="radio" name="column-color" id="pink" value="#FFB4E8" onClick={() => this.changeColumnColor("#FFB4E8")} />
+ <label htmlFor="pink">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ <input type="radio" name="column-color" id="purple" value="#b28dff" onClick={() => this.changeColumnColor("#b28dff")} />
+ <label htmlFor="purple">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ <input type="radio" name="column-color" id="blue" value="#afcbff" onClick={() => this.changeColumnColor("#afcbff")} />
+ <label htmlFor="blue">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ <input type="radio" name="column-color" id="yellow" value="#f3ffe3" onClick={() => this.changeColumnColor("#f3ffe3")} />
+ <label htmlFor="yellow">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ <input type="radio" name="column-color" id="red" value="#ffc9de" onClick={() => this.changeColumnColor("#ffc9de")} />
+ <label htmlFor="red">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ <input type="radio" name="column=color" id="none" value="#f1efeb" onClick={() => this.changeColumnColor("#f1efeb")} />
+ <label htmlFor="none">
+ <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div>
+ </label>
+ </div>
+ </div>
+ );
+ }
+
renderContent = () => {
return (
<div className="collectionSchema-header-menuOptions">
@@ -187,6 +225,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
<>
{this.renderTypes()}
{this.renderSorting()}
+ {this.renderColors()}
<div className="collectionSchema-headerMenu-group">
<button onClick={() => this.props.deleteColumn(this.props.keyValue)}>Delete Column</button>
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index e0de76247..b1e98b162 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -6,14 +6,15 @@
border-style: solid;
border-radius: $border-radius;
box-sizing: border-box;
- // position: absolute;
+ position: absolute;
+ top: 0;
width: 100%;
- height: calc(100% - 50px);
+ transition: height .5s;
+ height: 100%;
// overflow: hidden;
// overflow-x: scroll;
// border: none;
overflow: hidden;
- transition: top 0.5s;
// .collectionSchemaView-cellContents {
// height: $MAX_ROW_HEIGHT;
@@ -78,7 +79,7 @@
overflow-x: auto;
height: 100%;
display: -webkit-inline-box;
- direction: ltr;
+ direction: ltr;
}
.rt-thead {
@@ -122,7 +123,7 @@
font-size: 13px;
text-align: center;
background-color: $light-color-secondary;
-
+
&:last-child {
overflow: visible;
}
@@ -147,7 +148,7 @@
// &:nth-child(even) {
// background-color: $light-color;
// }
-
+
// &:nth-child(odd) {
// background-color: $light-color-secondary;
// }
@@ -175,7 +176,7 @@
padding: 0;
font-size: 13px;
text-align: center;
-
+
// white-space: normal;
.imageBox-cont {
@@ -195,6 +196,11 @@
height: 100%;
}
}
+
+ .rt-resizer {
+ width: 20px;
+ right: -10px;
+ }
}
.documentView-node-topmost {
@@ -207,18 +213,19 @@
background: $light-color;
}
-.collectionSchema-col{
+.collectionSchema-col {
height: 100%;
.collectionSchema-col-wrapper {
&.col-before {
border-left: 2px solid red;
}
+
&.col-after {
border-right: 2px solid red;
}
}
-}
+}
.collectionSchemaView-header {
@@ -285,7 +292,7 @@ button.add-column {
background-color: $light-color;
border: 1px solid $light-color-secondary;
padding: 2px 3px;
-
+
&:not(:last-child) {
border-top: 0;
}
@@ -305,6 +312,19 @@ button.add-column {
border-radius: 20px;
}
}
+
+ .columnMenu-colors {
+
+
+ input[type="radio"] {
+ display: none;
+ }
+
+ .columnMenu-colorPicker {
+ width: 20px;
+ height: 20px;
+ }
+ }
}
.collectionSchema-row {
@@ -312,8 +332,8 @@ button.add-column {
height: 100%;
background-color: white;
- &.row-focused {
- background-color: rgb(255, 246, 246);//$light-color-secondary;
+ &.row-focused .rt-tr {
+ background-color: rgb(255, 246, 246); //$light-color-secondary;
}
&.row-wrapped {
@@ -358,9 +378,11 @@ button.add-column {
&.row-above {
border-top: 1px solid red;
}
+
&.row-below {
border-bottom: 1px solid red;
}
+
&.row-inside {
border: 1px solid red;
}
@@ -461,7 +483,9 @@ button.add-column {
.rt-table {
overflow-x: hidden; // todo; this shouldnt be like this :((
overflow-y: visible;
- } // TODO fix
+ }
+
+ // TODO fix
.row-dragger {
background-color: rgb(252, 252, 252);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 8436b22a4..08ab22725 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -72,7 +72,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable private _node: HTMLDivElement | null = null;
@observable private _focusedTable: Doc = this.props.Document;
- @computed get chromeCollapsed() { return this.props.chromeCollapsed; }
@computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
@computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
@computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
@@ -199,13 +198,13 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
get schemaTable() {
return (
<SchemaTable
- Document={this.props.Document} // child doc
+ Document={this.props.Document}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
childDocs={this.childDocs}
CollectionView={this.props.CollectionView}
ContainingCollectionView={this.props.ContainingCollectionView}
- fieldKey={this.props.fieldKey} // might just be this.
+ fieldKey={this.props.fieldKey}
renderDepth={this.props.renderDepth}
moveDocument={this.props.moveDocument}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
@@ -234,11 +233,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
render() {
+ Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
// if (SelectionManager.SelectedDocuments().length > 0) console.log(StrCast(SelectionManager.SelectedDocuments()[0].Document.title));
// if (DocumentManager.Instance.getDocumentView(this.props.Document)) console.log(StrCast(this.props.Document.title), SelectionManager.IsSelected(DocumentManager.Instance.getDocumentView(this.props.Document)!))
return (
- <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel}
- onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
+ <div className="collectionSchemaView-container"
+ onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}>
{this.schemaTable}
{this.dividerDragger}
{!this.previewWidth() ? (null) : this.previewPanel}
@@ -252,7 +252,7 @@ export interface SchemaTableProps {
dataDoc?: Doc;
PanelHeight: () => number;
PanelWidth: () => number;
- childDocs: Doc[];
+ childDocs?: Doc[];
CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
fieldKey: string;
@@ -287,9 +287,19 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
@computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); }
@computed get columns() {
+ console.log("columns");
return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []);
}
- @computed get childDocs() { return this.props.childDocs; }
+ @computed get childDocs() {
+ if (this.props.childDocs) return this.props.childDocs;
+
+ let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ return DocListCast(doc[this.props.fieldKey]);
+ }
+ set childDocs(docs: Doc[]) {
+ let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ doc[this.props.fieldKey] = new List<Doc>(docs);
+ }
set columns(columns: SchemaHeaderField[]) { this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns); }
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
@@ -406,8 +416,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
super(props);
// convert old schema columns (list of strings) into new schema columns (list of schema header fields)
let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []);
- if (oldSchemaColumns && oldSchemaColumns.length) {
- let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i) : i);
+ if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") {
+ console.log("REMAKING COLUMNs");
+ let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns);
}
}
@@ -425,11 +436,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
tableRemoveDoc = (document: Doc): boolean => {
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
- let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
- // let children = this.childDocs;
+ // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
+ let children = this.childDocs;
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
+ this.childDocs = children;
return true;
}
return false;
@@ -521,7 +533,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
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);
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
// let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
let children = this.childDocs;
const pdoc = FieldValue(children[this._focusedCell.row]);
@@ -531,7 +543,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeFocusedCellByDirection = (direction: string): void => {
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
// let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
let children = this.childDocs;
switch (direction) {
@@ -563,7 +575,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeFocusedCellByIndex = (row: number, col: number): void => {
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
// let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
this._focusedCell = { row: row, col: col };
@@ -574,7 +586,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
createRow = () => {
- let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
// let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []);
let children = this.childDocs;
@@ -582,6 +594,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let proto = Doc.GetProto(newDoc);
proto.title = "";
children.push(newDoc);
+ this.childDocs = children;
}
@action
@@ -589,20 +602,24 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let index = 0;
let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1;
if (!found) {
- this.columns.push(new SchemaHeaderField("New field"));
+ console.log("create column found");
+ this.columns.push(new SchemaHeaderField("New field", "#f1efeb"));
return;
}
while (found) {
index++;
found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1;
}
- this.columns.push(new SchemaHeaderField("New field (" + index + ")"));
+ console.log("create column new");
+ this.columns.push(new SchemaHeaderField("New field (" + index + ")", "#f1efeb"));
}
@action
deleteColumn = (key: string) => {
+ console.log("deleting columnnn");
let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField));
if (list === undefined) {
+ console.log("delete column");
this.props.Document.schemaColumns = list = new List<SchemaHeaderField>([]);
} else {
const index = list.map(c => c.heading).indexOf(key);
@@ -614,16 +631,19 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeColumns = (oldKey: string, newKey: string, addNew: boolean) => {
+ console.log("changingin columnsdfhs");
let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField));
if (list === undefined) {
- this.props.Document.schemaColumns = list = new List<SchemaHeaderField>([new SchemaHeaderField(newKey)]);
+ console.log("change columns new");
+ this.props.Document.schemaColumns = list = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
} else {
+ console.log("change column");
if (addNew) {
- this.columns.push(new SchemaHeaderField(newKey));
+ this.columns.push(new SchemaHeaderField(newKey, "f1efeb"));
} else {
const index = list.map(c => c.heading).indexOf(oldKey);
if (index > -1) {
- list[index] = new SchemaHeaderField(newKey);
+ list[index] = new SchemaHeaderField(newKey, "f1efeb");
}
}
}
@@ -688,8 +708,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
get documentKeys() {
- // const docs = DocListCast(this.props.Document[this.props.fieldKey]);
- let docs = this.childDocs;
+ const docs = DocListCast(this.props.Document[this.props.fieldKey]);
+
+ // let docs = this.childDocs;
let keys: { [key: string]: boolean } = {};
// bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
// then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
@@ -717,7 +738,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed
get reactTable() {
- let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
// let children = DocListCast(cdoc[this.props.fieldKey]);
let children = this.childDocs;
@@ -731,7 +752,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return <ReactTable
style={{ position: "relative", float: "left", width: `calc(100% - ${previewWidth}px` }}
- data={this.childDocs}
+ data={children}
page={0}
pageSize={children.length}
showPagination={false}
@@ -746,7 +767,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
row => {
if (row.original.type === "collection") {
// let childDocs = DocListCast(row.original[this.props.fieldKey]);
- return <div className="sub"><SchemaTable {...this.props} Document={row.original} /></div>;
+ return <div className="sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>;
}
}
: undefined}
@@ -765,6 +786,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
let csv: string = this.columns.reduce((val, col) => val + col + ",", "");
csv = csv.substr(0, csv.length - 1) + "\n";
let self = this;
+ let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
+ // let children = DocListCast(cdoc[this.props.fieldKey]);
this.childDocs.map(doc => {
csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", "");
csv = csv.substr(0, csv.length - 1) + "\n";
@@ -785,8 +808,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
// const docs = DocListCast(this.props.Document[this.props.fieldKey]);
let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document;
- // const docs = DocListCast(cdoc[this.props.fieldKey]);
- let docs = this.childDocs;
+ const docs = DocListCast(cdoc[this.props.fieldKey]);
+ // let docs = this.childDocs;
row = row % docs.length;
while (row < 0) row += docs.length;
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 9dbe4ccb8..0cb01dc9d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -5,6 +5,7 @@
width: 100%;
position: absolute;
display: flex;
+ top: 0;
overflow-y: auto;
flex-wrap: wrap;
transition: top .5s;
@@ -73,6 +74,7 @@
transform-origin: top left;
grid-column-end: span 1;
height: 100%;
+ margin: auto;
}
.collectionStackingView-sectionHeader {
@@ -133,9 +135,9 @@
.collectionStackingView-addDocumentButton,
.collectionStackingView-addGroupButton {
- display: inline-block;
- margin: 0 5px;
+ display: flex;
overflow: hidden;
+ margin: auto;
width: 90%;
color: lightgrey;
overflow: ellipses;
@@ -144,6 +146,7 @@
.editableView-container-editing {
color: grey;
padding: 10px;
+ width: 100%;
}
.editableView-input:hover,
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index f647da8f0..bcf3a85d7 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -276,9 +276,11 @@ 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 uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
return (
- <div className="collectionStackingView" style={{ top: this.chromeCollapsed ? 0 : 100 }}
+ <div className="collectionStackingView"
ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
{/* {sectionFilter as boolean ? [
["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())],
@@ -287,9 +289,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
{this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
map(section => this.section(section[0], section[1])) :
this.section(undefined, this.filteredChildren)}
- {this.props.Document.sectionFilter ?
+ {(this.props.Document.sectionFilter && this.props.CollectionView.props.Document.chromeStatus !== 'disabled') ?
<div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
- style={{ width: (this.columnWidth / (headings.length + 1)) - 10, marginTop: 10 }}>
+ style={{ width: (this.columnWidth / (headings.length + (this.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? 1 : 0))) - 10, marginTop: 10 }}>
<EditableView {...editableViewProps} />
</div> : null}
</div>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 387e189e7..d8bed7e88 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -34,7 +34,7 @@ interface CSVFieldColumnProps {
@observer
export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
- @observable private _background = "white";
+ @observable private _background = "inherit";
private _dropRef: HTMLDivElement | null = null;
private dropDisposer?: DragManager.DragDropDisposer;
@@ -111,7 +111,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
let outerXf = Utils.GetScreenTransform(this.props.parent._masonryGridRef!);
let offset = this.props.parent.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.parent.props.ScreenToLocalTransform().
- translate(offset[0], offset[1] - (this.props.parent.chromeCollapsed ? 0 : 100)).
+ translate(offset[0], offset[1]).
scale(NumCast(doc.width, 1) / this.props.parent.columnWidth);
}
@@ -158,7 +158,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
pointerLeave = () => {
- this._background = "white";
+ this._background = "inherit";
}
@action
@@ -239,7 +239,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
};
let headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
- style={{ width: (style.columnWidth) / (uniqueHeadings.length + 1) }}>
+ style={{
+ width: (style.columnWidth) /
+ ((uniqueHeadings.length +
+ (this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? 1 : 0)) || 1)
+ }}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
<div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
@@ -261,7 +265,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
</div> : (null);
for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth}px `;
return (
- <div key={heading} style={{ width: `${100 / (uniqueHeadings.length + 1)}%`, background: this._background }}
+ <div key={heading} style={{ width: `${100 / ((uniqueHeadings.length + (this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? 1 : 0)) || 1)}%`, background: this._background }}
ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
{headingView}
<div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
@@ -279,10 +283,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
{this.children(this.props.docList)}
{singleColumn ? (null) : this.props.parent.columnDragger}
</div>
- <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: style.columnWidth / (uniqueHeadings.length + 1) }}>
- <EditableView {...newEditableViewProps} />
- </div>
+ {(this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ?
+ <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / (uniqueHeadings.length + (this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? 1 : 0)) }}>
+ <EditableView {...newEditableViewProps} />
+ </div> : null}
</div>
);
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4d31c3ae7..b1e6eada0 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -237,10 +237,10 @@ class TreeView extends React.Component<TreeViewProps> {
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: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" });
+ ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
} else {
- ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)), icon: "caret-square-right" });
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" });
+ ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" });
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
}
ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
@@ -522,8 +522,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document
- ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()), icon: "plus" });
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)), icon: "minus" });
+ ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
+ ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
@@ -572,6 +572,7 @@ 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);
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 81c84852a..b7ac8768f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -18,7 +18,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
import { StrCast, PromiseValue } from '../../../new_fields/Types';
import { DocumentType } from '../../documents/Documents';
import { CollectionStackingViewChrome, CollectionViewBaseChrome } from './CollectionViewChromes';
-import { observable, action, runInAction } from 'mobx';
+import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
import { faEye } from '@fortawesome/free-regular-svg-icons';
export const COLLECTION_BORDER_WIDTH = 2;
@@ -35,16 +35,25 @@ library.add(faImage, faEye);
@observer
export class CollectionView extends React.Component<FieldViewProps> {
- @observable private _collapsed = false;
+ @observable private _collapsed = true;
+
+ private _reactionDisposer: IReactionDisposer | undefined;
public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); }
componentDidMount = () => {
- // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
- let chromeStatus = this.props.Document.chromeStatus;
- if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
- runInAction(() => this._collapsed = true);
- }
+ this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus),
+ () => {
+ // chrome status is one of disabled, collapsed, or visible. this determines initial state from document
+ let chromeStatus = this.props.Document.chromeStatus;
+ if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) {
+ runInAction(() => this._collapsed = true);
+ }
+ });
+ }
+
+ componentWillUnmount = () => {
+ this._reactionDisposer && this._reactionDisposer();
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
@@ -87,14 +96,14 @@ export class CollectionView extends React.Component<FieldViewProps> {
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
let subItems: ContextMenuProps[] = [];
- subItems.push({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "signature" });
+ subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" });
if (CollectionBaseView.InSafeMode()) {
- ContextMenu.Instance.addItem({ description: "Test Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Invalid), icon: "project-diagram" });
+ ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" });
}
- subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" });
- subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" });
- subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" });
- subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" });
+ subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" });
+ subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" });
+ subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
+ subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" });
switch (this.props.Document.viewType) {
case CollectionViewType.Freeform: {
subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) });
@@ -102,7 +111,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
}
}
ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
- ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight")), icon: "project-diagram" });
+ ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" });
}
}
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 6525f3b07..989315194 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -3,16 +3,19 @@
.collectionViewChrome-cont {
position: relative;
+ opacity: 0.9;
z-index: 9001;
transition: top .5s;
- background: lightslategray;
+ background: lightgrey;
+ transition: margin-top .5s;
+ background: lightgray;
padding: 10px;
.collectionViewChrome {
display: grid;
grid-template-columns: 1fr auto;
padding-bottom: 10px;
- border-bottom: .5px solid lightgrey;
+ border-bottom: .5px solid rgb(180, 180, 180);
.collectionViewBaseChrome {
display: flex;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 9c751c4df..2bffe3cc0 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -185,11 +185,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
render() {
return (
- <div className="collectionViewChrome-cont" style={{ top: this._collapsed ? -100 : 0 }}>
+ <div className="collectionViewChrome-cont" style={{ marginTop: this._collapsed ? -70 : 0, height: 70 }}>
<div className="collectionViewChrome">
<div className="collectionViewBaseChrome">
<button className="collectionViewBaseChrome-collapse"
- style={{ top: this._collapsed ? 90 : 10, transform: `rotate(${this._collapsed ? 180 : 0}deg)` }}
+ style={{ marginTop: this._collapsed ? 60 : 0, transform: `rotate(${this._collapsed ? 180 : 0}deg)` }}
title="Collapse collection chrome" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
</button>
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index 9c3c9c07c..9baa250a6 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -29,6 +29,7 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
else {
this.props.script("");
}
+
return (
<div className="collectionViewBaseChrome-viewSpecsMenu-row">
<input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"
@@ -36,7 +37,7 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
onChange={(e) => runInAction(() => this._key = e.target.value)}
placeholder="KEY" />
<button className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
- style={{ background: PastelSchemaPalette.get(this._contains ? "green" : "red") }}
+ style={{ background: this._contains ? "#77dd77" : "#ff6961" }}
onClick={() => runInAction(() => this._contains = !this._contains)}>
{this._contains ? "CONTAINS" : "DOES NOT CONTAIN"}
</button>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d70022280..8dac785e1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -65,8 +65,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1;
}
+ ComputeContentBounds(boundsList: { x: number, y: number, width: number, height: number }[]) {
+ let bounds = boundsList.reduce((bounds, b) => {
+ var [sptX, sptY] = [b.x, b.y];
+ let [bptX, bptY] = [sptX + b.width, sptY + b.height];
+ return {
+ x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+ r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
+ };
+ }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+ return bounds;
+ }
+
@computed get contentBounds() {
- let bounds = this.fitToBox && !this.isAnnotationOverlay ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined;
+ let bounds = this.fitToBox && !this.isAnnotationOverlay ? this.ComputeContentBounds(this.elements.filter(e => e.bounds).map(e => e.bounds!)) : undefined;
return {
panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0,
panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0,
@@ -151,6 +163,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let dropY = NumCast(de.data.dropDocument.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;
this.bringToFront(dragDoc);
}
}
@@ -426,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return result.result === undefined ? {} : result.result;
}
- private viewDefToJSX(viewDef: any): JSX.Element | undefined {
+ private viewDefToJSX(viewDef: any): { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } } | undefined {
if (viewDef.type === "text") {
const text = Cast(viewDef.text, "string");
const x = Cast(viewDef.x, "number");
@@ -434,25 +448,27 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const width = Cast(viewDef.width, "number");
const height = Cast(viewDef.height, "number");
const fontSize = Cast(viewDef.fontSize, "number");
- if ([text, x, y].some(val => val === undefined)) {
+ if ([text, x, y, width, height].some(val => val === undefined)) {
return undefined;
}
- return <div className="collectionFreeform-customText" style={{
- transform: `translate(${x}px, ${y}px)`,
- width, height, fontSize
- }}>{text}</div>;
+ return {
+ ele: <div className="collectionFreeform-customText" style={{
+ transform: `translate(${x}px, ${y}px)`,
+ width, height, fontSize
+ }}>{text}</div>, bounds: { x: x!, y: y!, width: width!, height: height! }
+ };
}
}
@computed.struct
- get views() {
+ get elements() {
let curPage = FieldValue(this.Document.curPage, -1);
const initScript = this.Document.arrangeInit;
const script = this.Document.arrangeScript;
let state: any = undefined;
const docs = this.childDocs;
- let elements: JSX.Element[] = [];
+ let elements: { ele: JSX.Element, bounds?: { x: number, y: number, width: number, height: number } }[] = [];
if (initScript) {
const initResult = initScript.script.run({ docs, collection: this.Document });
if (initResult.success) {
@@ -460,7 +476,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const { state: scriptState, views } = result;
state = scriptState;
if (Array.isArray(views)) {
- elements = views.reduce<JSX.Element[]>((prev, ele) => {
+ elements = views.reduce<typeof elements>((prev, ele) => {
const jsx = this.viewDefToJSX(ele);
jsx && prev.push(jsx);
return prev;
@@ -468,15 +484,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
}
}
- let docviews = docs.reduce((prev, doc) => {
- if (!(doc instanceof Doc)) return prev;
+ let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => {
var page = NumCast(doc.page, -1);
+ let bounds: { x?: number, y?: number, width?: number, height?: number };
if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) {
let minim = BoolCast(doc.isMinimized);
if (minim === undefined || !minim) {
const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {};
state = pos.state === undefined ? state : pos.state;
- prev.push(<CollectionFreeFormDocumentView key={doc[Id]} x={pos.x} y={pos.y} width={pos.width} height={pos.height} {...this.getChildDocumentViewProps(doc)} />);
+ prev.push({
+ ele: <CollectionFreeFormDocumentView key={doc[Id]} x={pos.x} y={pos.y} width={pos.width} height={pos.height} {...this.getChildDocumentViewProps(doc)} />,
+ bounds: (pos.x !== undefined && pos.y !== undefined && pos.width !== undefined && pos.height !== undefined) ? { x: pos.x, y: pos.y, width: pos.width, height: pos.height } : undefined
+ });
}
}
return prev;
@@ -487,6 +506,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return docviews;
}
+ @computed.struct
+ get views() {
+ return this.elements.map(ele => ele.ele);
+ }
+
@action
onCursorMove = (e: React.PointerEvent) => {
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
@@ -496,7 +520,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
let layoutItems: ContextMenuProps[] = [];
layoutItems.push({
description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`,
- event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox),
+ event: async () => this.props.Document.fitToBox = !this.fitToBox,
icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt"
});
layoutItems.push({
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1c767e012..67bed284f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -293,15 +293,16 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
d.page = -1;
return d;
});
+ newCollection.chromeStatus = "disabled";
let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
newCollection.proto!.summaryDoc = summary;
selected = [newCollection];
newCollection.x = bounds.left + bounds.width;
summary.proto!.subBulletDocs = new List<Doc>(selected);
- //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
summary.templates = new List<string>([Templates.Bullet.Layout]);
- let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, title: "-summary-" });
+ let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
container.viewType = CollectionViewType.Stacking;
+ container.autoHeight = true;
this.props.addLiveTextDocument(container);
// });
} else if (e.key === "S") {
@@ -312,6 +313,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
d.page = -1;
return d;
});
+ newCollection.chromeStatus = "disabled";
let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
newCollection.proto!.summaryDoc = summary;
selected = [newCollection];
@@ -319,6 +321,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
//this.props.addDocument(newCollection, false);
summary.proto!.summarizedDocs = new List<Doc>(selected);
summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight"
+ summary.autoHeight = true;
this.props.addLiveTextDocument(summary);
}
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index d2c23fdab..e2c559c9a 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -70,7 +70,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
render() {
return (
<div className="buttonBox-outerDiv" onContextMenu={this.onContextMenu}>
- <button className="buttonBox-mainButton" onClick={this.onClick}>{this.Document.text || "Button"}</button>
+ <button className="buttonBox-mainButton" onClick={this.onClick}>{this.Document.text || this.Document.title || "Button"}</button>
</div>
);
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f101222ae..4b5cf3a43 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -99,6 +99,7 @@ export interface DocumentViewProps {
zoomToScale: (scale: number) => void;
getScale: () => number;
animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void;
+ ChromeHeight?: () => number;
}
const schema = createSchema({
@@ -439,7 +440,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
let annotationDoc = de.data.annotationDocument;
annotationDoc.linkedToDoc = true;
+ de.data.targetContext = this.props.ContainingCollectionView!.props.Document;
let targetDoc = this.props.Document;
+ targetDoc.targetContext = de.data.targetContext;
let annotations = await DocListCastAsync(annotationDoc.annotations);
if (annotations) {
annotations.forEach(anno => {
@@ -448,7 +451,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc);
if (pdfDoc) {
- DocUtils.MakeLink(annotationDoc, targetDoc, undefined, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
+ DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
}
}
if (de.data instanceof DragManager.LinkDragData) {
@@ -644,6 +647,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
@computed get contents() {
return (<DocumentContentsView {...this.props}
+ ChromeHeight={this.chromeHeight}
isSelected={this.isSelected} select={this.select}
selectOnLoad={this.props.selectOnLoad}
layoutKey={"layout"}
@@ -651,6 +655,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DataDoc={this.dataDoc} />);
}
+ chromeHeight = () => {
+ let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
+ let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
+ return showTitle ? 25 : 0;
+ }
+
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.
@@ -666,8 +676,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%";
var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined;
- let showTitle = showOverlays && showOverlays.title !== "undefined" ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
- let showCaption = showOverlays && showOverlays.caption !== "undefined" ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
+ let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle);
+ let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption);
let templates = Cast(this.layoutDoc.templates, listSpec("string"));
if (!showOverlays && templates instanceof List) {
templates.map(str => {
@@ -716,11 +726,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
}}>
<EditableView
- contents={this.layoutDoc[showTitle]}
+ contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]}
display={"block"}
height={72}
fontSize={12}
- GetValue={() => StrCast(this.layoutDoc[showTitle!])}
+ GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])}
SetValue={(value: string) => (Doc.GetProto(this.layoutDoc)[showTitle!] = value) ? true : true}
/>
</div>
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 3570531b2..acf1aced3 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -20,7 +20,7 @@ export interface RectangleTemplate {
export default class FaceRectangles extends React.Component<FaceRectanglesProps> {
render() {
- let faces = DocListCast(Doc.GetProto(this.props.document).faces);
+ let faces = DocListCast(this.props.document.faces);
let templates: RectangleTemplate[] = faces.map(faceDoc => {
let rectangle = Cast(faceDoc.faceRectangle, Doc) as Doc;
let style = {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index ffaee8042..da54ecc3a 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -48,6 +48,8 @@ export interface FieldViewProps {
PanelHeight: () => number;
setVideoBox?: (player: VideoBox) => void;
setPdfBox?: (player: PDFBox) => void;
+ ContentScaling: () => number;
+ ChromeHeight?: () => number;
}
@observer
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2db0776ba..87b1d43c1 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -37,6 +37,7 @@ import React = require("react");
import { For } from 'babel-types';
import { DateField } from '../../../new_fields/DateField';
import { Utils } from '../../../Utils';
+import { MainOverlayTextBox } from '../MainOverlayTextBox';
library.add(faEdit);
library.add(faSmile, faTextHeight);
@@ -413,7 +414,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
fieldExtDoc.annotations = new List<Doc>(targetAnnotations);
}
- let link = DocUtils.MakeLink(this.props.Document, region);
+ let link = DocUtils.MakeLink(this.props.Document, region, doc);
if (link) {
cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
linkId = link[Id];
@@ -527,6 +528,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
if (targetContext) {
DocumentManager.Instance.jumpToDocument(targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
+ } else if (jumpToDoc) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
+
}
}
});
@@ -642,7 +646,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let nh = NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.props.Document.height, 0);
let sh = scrBounds.height;
- this.props.Document.height = nh ? dh / nh * sh : sh;
+ const ChromeHeight = MainOverlayTextBox.Instance.ChromeHeight;
+ this.props.Document.height = (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0);
this.dataDoc.nativeHeight = nh ? sh : undefined;
}
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 29a76b0c8..dbe545048 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -25,9 +25,12 @@ import { Docs, DocumentType } from '../../documents/Documents';
import { DocServer } from '../../DocServer';
import { Font } from '@react-pdf/renderer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { CognitiveServices } from '../../cognitive_services/CognitiveServices';
+import { CognitiveServices, Service, Tag, Confidence } from '../../cognitive_services/CognitiveServices';
import FaceRectangles from './FaceRectangles';
import { faEye } from '@fortawesome/free-regular-svg-icons';
+import { ComputedField } from '../../../new_fields/ScriptField';
+import { CompileScript } from '../../util/Scripting';
+import { thisExpression } from 'babel-types';
var requestImageSize = require('../../util/request-image-size');
var path = require('path');
const { Howl } = require('howler');
@@ -96,7 +99,11 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
this.props.Document.width = drop.width;
this.props.Document.height = drop.height;
Doc.GetProto(this.props.Document).type = DocumentType.TEMPLATE;
- this.props.Document.layout = temp;
+ if (this.props.DataDoc && this.props.DataDoc.layout === this.props.Document) {
+ this.props.DataDoc.layout = temp;
+ } else {
+ this.props.Document.layout = temp;
+ }
e.stopPropagation();
} else if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
@@ -226,20 +233,56 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
let modes: ContextMenuProps[] = [];
- let dataDoc = Doc.GetProto(this.props.Document);
- modes.push({ description: "Generate Tags", event: () => CognitiveServices.Image.generateMetadata(dataDoc), icon: "tag" });
- modes.push({ description: "Find Faces", event: () => CognitiveServices.Image.extractFaces(dataDoc), icon: "camera" });
+ modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" });
+ modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" });
ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" });
ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes, icon: "eye" });
}
}
+ extractFaces = () => {
+ let converter = (results: any) => {
+ let faceDocs = new List<Doc>();
+ results.map((face: CognitiveServices.Image.Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!));
+ return faceDocs;
+ };
+ CognitiveServices.Image.Manager.analyzer(this.extensionDoc, ["faces"], this.url, Service.Face, converter);
+ }
+
+ generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
+ let converter = (results: any) => {
+ let tagDoc = new Doc;
+ let tagsList = new List();
+ results.tags.map((tag: Tag) => {
+ tagsList.push(tag.name);
+ let sanitized = tag.name.replace(" ", "_");
+ let script = `return (${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`;
+ let computed = CompileScript(script, { params: { this: "Doc" } });
+ computed.compiled && (tagDoc[sanitized] = new ComputedField(computed));
+ });
+ this.extensionDoc.generatedTags = tagsList;
+ tagDoc.title = "Generated Tags Doc";
+ tagDoc.confidence = threshold;
+ return tagDoc;
+ };
+ CognitiveServices.Image.Manager.analyzer(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter);
+ }
+
@action
onDotDown(index: number) {
this.Document.curPage = index;
}
+ @computed get fieldExtensionDoc() {
+ return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true");
+ }
+
+ @computed private get url() {
+ let data = Cast(Doc.GetProto(this.props.Document).data, ImageField);
+ return data ? data.url.href : undefined;
+ }
+
dots(paths: string[]) {
let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
let dist = Math.min(nativeWidth / paths.length, 40);
@@ -287,7 +330,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let aspect = size.height / size.width;
let rotation = NumCast(this.dataDoc.rotation) % 180;
if (rotation === 90 || rotation === 270) aspect = 1 / aspect;
- if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) {
+ if (Math.abs(NumCast(layoutdoc.height) - size.height) > 1 || Math.abs(NumCast(layoutdoc.width) - size.width) > 1) {
setTimeout(action(() => {
layoutdoc.height = layoutdoc[WidthSym]() * aspect;
layoutdoc.nativeHeight = size.height;
@@ -394,7 +437,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
style={{ color: [DocListCast(this.extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={faFileAudio} size="sm" />
</div>
{/* {this.lightbox(paths)} */}
- <FaceRectangles document={this.props.Document} color={"#0000FF"} backgroundColor={"#0000FF"} />
+ <FaceRectangles document={this.extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
</div>);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 77824b4ff..f10079169 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -20,6 +20,8 @@ import { RichTextField } from "../../../new_fields/RichTextField";
import { ImageField } from "../../../new_fields/URLField";
import { SelectionManager } from "../../util/SelectionManager";
import { listSpec } from "../../../new_fields/Schema";
+import { CollectionViewType } from "../collections/CollectionBaseView";
+import { undoBatch } from "../../util/UndoManager";
export type KVPScript = {
script: CompiledScript;
@@ -89,6 +91,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return false;
}
+ @undoBatch
public static SetField(doc: Doc, key: string, value: string) {
const script = this.CompileKVPScript(value);
if (!script) return false;
@@ -195,6 +198,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
+ if (!fieldTemplate) {
+ return;
+ }
let previousViewType = fieldTemplate.viewType;
Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc));
previousViewType && (fieldTemplate.viewType = previousViewType);
@@ -211,14 +217,17 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return Docs.Create.StackingDocument([], options);
}
let first = await Cast(data[0], Doc);
- if (!first) {
+ if (!first || !first.data) {
return Docs.Create.StackingDocument([], options);
}
- switch (first.type) {
- case "image":
- return Docs.Create.StackingDocument([], options);
- case "text":
+ switch (first.data.constructor) {
+ case RichTextField:
return Docs.Create.TreeDocument([], options);
+ case ImageField:
+ return Docs.Create.MasonryDocument([], options);
+ default:
+ console.log(`Template for ${first.data.constructor} not supported!`);
+ return undefined;
}
} else if (data instanceof ImageField) {
return Docs.Create.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options);
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 064f3edcc..3775f0f47 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
-import { emptyFunction, returnFalse, returnZero, returnTrue } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, returnTrue, returnOne } from '../../../Utils';
import { CompileScript, CompiledScript, ScriptOptions } from "../../util/Scripting";
import { Transform } from '../../util/Transform';
import { EditableView } from "../EditableView";
@@ -16,6 +16,7 @@ import { DragManager, SetupDrag } from '../../util/DragManager';
import { ContextMenu } from '../ContextMenu';
import { Docs } from '../../documents/Documents';
import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { undoBatch } from '../../util/UndoManager';
// Represents one row in a key value plane
@@ -70,6 +71,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
PanelWidth: returnZero,
PanelHeight: returnZero,
addDocTab: returnZero,
+ ContentScaling: returnOne
};
let contents = <FieldView {...props} />;
// let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
@@ -91,12 +93,12 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
<tr className={this.props.rowStyle} onPointerEnter={action(() => this.isPointerOver = true)} onPointerLeave={action(() => this.isPointerOver = false)}>
<td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
<div className="keyValuePair-td-key-container">
- <button style={hover} className="keyValuePair-td-key-delete" onClick={() => {
+ <button style={hover} className="keyValuePair-td-key-delete" onClick={undoBatch(() => {
if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
props.Document[props.fieldKey] = undefined;
}
else props.Document.proto![props.fieldKey] = undefined;
- }}>
+ })}>
X
</button>
<input
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 4973340df..fa072aecf 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -69,24 +69,10 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
componentDidMount() {
if (this.props.setPdfBox) this.props.setPdfBox(this);
-
- document.removeEventListener("copy", this.copy);
- document.addEventListener("copy", this.copy);
}
componentWillUnmount() {
this._reactionDisposer && this._reactionDisposer();
- document.removeEventListener("copy", this.copy);
- }
-
- private copy = (e: ClipboardEvent) => {
- if (this.props.active()) {
- if (e.clipboardData) {
- e.clipboardData.setData("text/plain", text);
- e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]);
- e.preventDefault();
- }
- }
}
public GetPage() {
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index ed7081b1d..513f9fed6 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -9,6 +9,8 @@ import { List } from "../../../new_fields/List";
import PDFMenu from "./PDFMenu";
import { DocumentManager } from "../../util/DocumentManager";
import { PresentationView } from "../presentationview/PresentationView";
+import { LinkManager } from "../../util/LinkManager";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
interface IAnnotationProps {
anno: Doc;
@@ -110,11 +112,15 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
@action
- onPointerDown = (e: React.PointerEvent) => {
+ onPointerDown = async (e: React.PointerEvent) => {
if (e.button === 0) {
- let targetDoc = Cast(this.props.document.target, Doc, null);
+ let targetDoc = await Cast(this.props.document.target, Doc);
if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc, false);
+ let context = await Cast(targetDoc.targetContext, Doc);
+ if (context) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined,
+ ((doc) => this.props.parent.props.parent.props.addDocTab(context!, context!.proto, e.ctrlKey ? "onRight" : "inTab")));
+ }
}
}
if (e.button === 2) {
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index c205617b4..c5b2a1dda 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -175,7 +175,7 @@ export default class Page extends React.Component<IPageProps> {
}
let pdfDoc = await Cast(annotationDoc.pdfDoc, Doc);
if (pdfDoc) {
- DocUtils.MakeLink(annotationDoc, targetDoc, undefined, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
+ DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(pdfDoc.title)}`, "", StrCast(pdfDoc.title));
}
}
}
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 36f1178f1..ccc3a72a9 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
-import { faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons';
+import { faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch, faArrowRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
@@ -9,17 +9,26 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils } from "../../../Utils";
+import { Utils, returnFalse, emptyFunction, returnOne } from "../../../Utils";
import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
import { SelectionManager } from "../../util/SelectionManager";
-import "./PresentationView.scss";
+import { indexOf } from "typescript-collections/dist/lib/arrays";
+import { map } from "bluebird";
+import { ContextMenu } from "../ContextMenu";
+import { DocumentContentsView } from "../nodes/DocumentContentsView";
+import { Transform } from "../../util/Transform";
+import { FieldView } from "../nodes/FieldView";
+import { DocumentView } from "../nodes/DocumentView";
+import { DocumentType } from "../../documents/Documents";
import React = require("react");
+
library.add(faArrowUp);
library.add(fileSolid);
library.add(faLocationArrow);
library.add(fileRegular as any);
library.add(faSearch);
+library.add(faArrowRight);
interface PresentationElementProps {
mainDocument: Doc;
@@ -46,6 +55,7 @@ export enum buttonIndex {
FadeAfter = 3,
HideAfter = 4,
Group = 5,
+ OpenRight = 6
}
@@ -68,7 +78,7 @@ export default class PresentationElement extends React.Component<PresentationEle
constructor(props: PresentationElementProps) {
super(props);
- this.selectedButtons = new Array(6);
+ this.selectedButtons = new Array(7);
this.presElRef = React.createRef();
}
@@ -132,7 +142,7 @@ export default class PresentationElement extends React.Component<PresentationEle
if (!foundDoc) {
let newDoc = new Doc();
- let defaultBooleanArray: boolean[] = new Array(6);
+ let defaultBooleanArray: boolean[] = new Array(7);
newDoc.selectedButtons = new List(defaultBooleanArray);
newDoc.docId = this.props.document[Id];
castedList.push(newDoc);
@@ -394,6 +404,18 @@ export default class PresentationElement extends React.Component<PresentationEle
}
+ @action
+ onRightTabClick = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ if (this.selectedButtons[buttonIndex.OpenRight]) {
+ this.selectedButtons[buttonIndex.OpenRight] = false;
+ // action maybe
+ } else {
+ this.selectedButtons[buttonIndex.OpenRight] = true;
+ }
+ this.autoSaveButtonChange(buttonIndex.OpenRight);
+ }
+
/**
* Creating a drop target for drag and drop when called.
*/
@@ -629,7 +651,7 @@ export default class PresentationElement extends React.Component<PresentationEle
*/
getSelectedButtonsOfDoc = async (paramDoc: Doc) => {
let castedList = Cast(this.props.presButtonBackUp.selectedButtonDocs, listSpec(Doc));
- let foundSelectedButtons: boolean[] = new Array(6);
+ let foundSelectedButtons: boolean[] = new Array(7);
//if this is the first time this doc mounts, push a doc for it to store
for (let doc of castedList!) {
@@ -649,7 +671,7 @@ export default class PresentationElement extends React.Component<PresentationEle
//This is used to add dragging as an event.
onPointerEnter = (e: React.PointerEvent): void => {
- this.props.document.libraryBrush = true;
+ // this.props.document.libraryBrush = true;
if (e.buttons === 1 && SelectionManager.GetIsDragging()) {
let selected = NumCast(this.props.mainDocument.selectedDoc, 0);
@@ -666,7 +688,7 @@ export default class PresentationElement extends React.Component<PresentationEle
//This is used to remove the dragging when dropped.
onPointerLeave = (e: React.PointerEvent): void => {
- this.props.document.libraryBrush = false;
+ // this.props.document.libraryBrush = false;
//to get currently selected presentation doc
let selected = NumCast(this.props.mainDocument.selectedDoc, 0);
@@ -766,8 +788,78 @@ export default class PresentationElement extends React.Component<PresentationEle
}
}
+ private get embedInline() {
+ return BoolCast(this.props.document.embedOpen);
+ }
+ private set embedInline(value: boolean) {
+ this.props.document.embedOpen = value;
+ }
+ onContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
+ e.preventDefault();
+ e.stopPropagation();
+ ContextMenu.Instance.addItem({ description: this.embedInline ? "Collapse Inline" : "Expand Inline", event: () => this.embedInline = !this.embedInline, icon: "expand" });
+ ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
+ }
+
+ renderEmbeddedInline = () => {
+ if (!this.embedInline) {
+ return (null);
+ }
+
+ // return <ul key={this.props.document[Id] + "more"}>
+ // {TreeView.GetChildElements([this.props.document], "", new Doc(), undefined, "", (doc: Doc, relativeTo?: Doc, before?: boolean) => false, this.props.removeDocByRef, this.move,
+ // StrCast(this.props.document.dropAction) as dropActionType, (doc: Doc, dataDoc: Doc | undefined, where: string) => { }, Transform.Identity, () => ({ translateX: 0, translateY: 0 }), () => false, () => 400, 7)}
+ // </ul >;
+ let propDocWidth = NumCast(this.props.document.nativeWidth);
+ let propDocHeight = NumCast(this.props.document.nativeHeight);
+ let scale = () => {
+ let newScale = 175 / NumCast(this.props.document.nativeWidth, 175);
+ console.log("New Scale: ", newScale);
+ return newScale;
+ };
+ return (
+ <div style={{
+ position: "relative",
+ height: propDocHeight === 0 ? 100 : propDocHeight * scale(),
+ width: propDocWidth === 0 ? "auto" : propDocWidth * scale(),
+ marginTop: 15
+
+ }}>
+ <DocumentView
+ fitToBox={StrCast(this.props.document.type).indexOf(DocumentType.COL) !== -1}
+ Document={this.props.document}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ addDocTab={returnFalse}
+ renderDepth={1}
+ PanelWidth={() => 350}
+ PanelHeight={() => 100}
+ focus={emptyFunction}
+ selectOnLoad={false}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ ContainingCollectionView={undefined}
+ ContentScaling={scale}
+ />
+ <div style={{
+ width: " 100%",
+ height: " 100%",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ background: "transparent",
+ zIndex: 2,
+
+ }}></div>
+ </div>
+ );
+ }
render() {
let p = this.props;
@@ -784,7 +876,7 @@ export default class PresentationElement extends React.Component<PresentationEle
let dropAction = StrCast(this.props.document.dropAction) as dropActionType;
let onItemDown = SetupDrag(this.presElRef, () => p.document, this.move, dropAction, this.props.mainDocument[Id], true);
return (
- <div className={className} key={p.document[Id] + p.index}
+ <div className={className} onContextMenu={this.onContextMenu} key={p.document[Id] + p.index}
ref={this.presElRef}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
onPointerDown={onItemDown}
@@ -809,7 +901,10 @@ export default class PresentationElement extends React.Component<PresentationEle
this.changeGroupStatus();
this.onGroupClick(p.document, p.index, this.selectedButtons[buttonIndex.Group]);
}}> <FontAwesomeIcon icon={"arrow-up"} /> </button>
+ <button title="Open Right" className={this.selectedButtons[buttonIndex.OpenRight] ? "presentation-interaction-selected" : "presentation-interaction"} onPointerDown={(e) => e.stopPropagation()} onClick={this.onRightTabClick}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ <br />
+ {this.renderEmbeddedInline()}
</div>
);
}
diff --git a/src/client/views/presentationview/PresentationModeMenu.scss b/src/client/views/presentationview/PresentationModeMenu.scss
new file mode 100644
index 000000000..336f43d20
--- /dev/null
+++ b/src/client/views/presentationview/PresentationModeMenu.scss
@@ -0,0 +1,30 @@
+.presMenu-cont {
+ position: fixed;
+ z-index: 10000;
+ height: 35px;
+ background: #323232;
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+ border-radius: 0px 6px 6px 6px;
+ overflow: hidden;
+ display: flex;
+
+ .presMenu-button {
+ background-color: transparent;
+ width: 35px;
+ height: 35px;
+ }
+
+ .presMenu-button:hover {
+ background-color: #121212;
+ }
+
+ .presMenu-dragger {
+ height: 100%;
+ transition: width .2s;
+ background-image: url("https://logodix.com/logo/1020374.png");
+ background-size: 90% 100%;
+ background-repeat: no-repeat;
+ background-position: left center;
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/presentationview/PresentationModeMenu.tsx b/src/client/views/presentationview/PresentationModeMenu.tsx
new file mode 100644
index 000000000..b3edeb1e2
--- /dev/null
+++ b/src/client/views/presentationview/PresentationModeMenu.tsx
@@ -0,0 +1,92 @@
+import React = require("react");
+import { observable, action, runInAction } from "mobx";
+import "./PresentationModeMenu.scss";
+import { observer } from "mobx-react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+
+export interface PresModeMenuProps {
+ next: () => void;
+ back: () => void;
+ presStatus: boolean;
+ startOrResetPres: () => void;
+ closePresMode: () => void;
+}
+
+@observer
+export default class PresModeMenu extends React.Component<PresModeMenuProps> {
+
+ @observable private _top: number = 20;
+ @observable private _right: number = 0;
+ @observable private _opacity: number = 1;
+ @observable private _transition: string = "opacity 0.5s";
+ @observable private _transitionDelay: string = "";
+ //@observable private Pinned: boolean = false;
+
+
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+
+ @action
+ pointerEntered = (e: React.PointerEvent) => {
+ this._transition = "opacity 0.1s";
+ this._transitionDelay = "";
+ this._opacity = 1;
+ }
+
+ @action
+ dragging = (e: PointerEvent) => {
+ this._right -= e.movementX;
+ this._top += e.movementY;
+
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ dragEnd = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.dragging);
+ document.removeEventListener("pointerup", this.dragEnd);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ dragStart = (e: React.PointerEvent) => {
+ document.removeEventListener("pointermove", this.dragging);
+ document.addEventListener("pointermove", this.dragging);
+ document.removeEventListener("pointerup", this.dragEnd);
+ document.addEventListener("pointerup", this.dragEnd);
+ let clientRect = this._mainCont.current!.getBoundingClientRect();
+
+ // runInAction(() => this._left = (clientRect.width - e.nativeEvent.offsetX) + clientRect.left);
+ // runInAction(() => this._top = e.nativeEvent.offsetY);
+
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ renderPlayPauseButton = () => {
+ if (this.props.presStatus) {
+ return <button title="Reset Presentation" className="presMenu-button" onClick={this.props.startOrResetPres}><FontAwesomeIcon icon="stop" /></button>;
+ } else {
+ return <button title="Start Presentation From Start" className="presMenu-button" onClick={this.props.startOrResetPres}><FontAwesomeIcon icon="play" /></button>;
+ }
+ }
+
+ render() {
+ return (
+ <div className="presMenu-cont" onPointerEnter={this.pointerEntered} ref={this._mainCont}
+ style={{ right: this._right, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
+ <button title="Back" className="presMenu-button" onClick={this.props.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
+ {this.renderPlayPauseButton()}
+ <button title="Next" className="presMenu-button" onClick={this.props.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ <button className="presMenu-button" title="Close Presentation Menu" onClick={this.props.closePresMode}>
+ <FontAwesomeIcon icon="times" size="lg" />
+ </button>
+ <div className="presMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />
+ </div >
+ );
+ }
+
+
+
+
+} \ No newline at end of file
diff --git a/src/client/views/presentationview/PresentationView.scss b/src/client/views/presentationview/PresentationView.scss
index 2bb0ec8c8..97cbd4a24 100644
--- a/src/client/views/presentationview/PresentationView.scss
+++ b/src/client/views/presentationview/PresentationView.scss
@@ -1,7 +1,7 @@
.presentationView-cont {
position: absolute;
background: white;
- z-index: 1;
+ z-index: 2;
box-shadow: #AAAAAA .2vw .2vw .4vw;
right: 0;
top: 0;
@@ -19,6 +19,17 @@
-ms-user-select: none;
user-select: none;
transition: all .1s;
+ //max-height: 250px;
+
+
+
+ .documentView-node {
+ // height: auto !important;
+ // width: aut !important;
+ position: absolute;
+ z-index: 1;
+ }
+
}
.presentationView-item-above {
@@ -55,7 +66,7 @@
padding-bottom: 3px;
font-size: 25px;
display: inline-block;
- width: calc(100% - 160px);
+ width: calc(100% - 200px);
}
.presentation-icon {
diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx
index e25725275..ea85a8c6a 100644
--- a/src/client/views/presentationview/PresentationView.tsx
+++ b/src/client/views/presentationview/PresentationView.tsx
@@ -4,7 +4,7 @@ import { observable, action, runInAction, reaction, autorun } from "mobx";
import "./PresentationView.scss";
import { DocumentManager } from "../../util/DocumentManager";
import { Utils } from "../../../Utils";
-import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, WidthSym } from "../../../new_fields/Doc";
import { listSpec } from "../../../new_fields/Schema";
import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../../new_fields/Types";
import { Id } from "../../../new_fields/FieldSymbols";
@@ -12,10 +12,13 @@ import { List } from "../../../new_fields/List";
import PresentationElement, { buttonIndex } from "./PresentationElement";
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit } from '@fortawesome/free-solid-svg-icons';
+import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit, faEye } from '@fortawesome/free-solid-svg-icons';
import { Docs } from "../../documents/Documents";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import PresentationViewList from "./PresentationList";
+import { ContextMenu } from "../ContextMenu";
+import PresModeMenu from "./PresentationModeMenu";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
library.add(faArrowLeft);
library.add(faArrowRight);
@@ -25,6 +28,7 @@ library.add(faPlus);
library.add(faTimes);
library.add(faMinus);
library.add(faEdit);
+library.add(faEye);
export interface PresViewProps {
@@ -62,6 +66,8 @@ export class PresentationView extends React.Component<PresViewProps> {
//Variable that holds reference to title input, so that new presentations get titles assigned.
@observable titleInputElement: HTMLInputElement | undefined;
@observable PresTitleChangeOpen: boolean = false;
+ @observable presMode: boolean = false;
+
@observable opacity = 1;
@observable persistOpacity = true;
@@ -84,6 +90,7 @@ export class PresentationView extends React.Component<PresViewProps> {
//The first lifecycle function that gets called to set up the current presentation.
async componentWillMount() {
+
this.props.Documents.forEach(async (doc, index: number) => {
//For each presentation received from mainContainer, a mapping is created.
@@ -361,11 +368,16 @@ export class PresentationView extends React.Component<PresViewProps> {
//checking if curDoc has navigation open
let curDocButtons = this.presElementsMappings.get(curDoc)!.selected;
if (curDocButtons[buttonIndex.Navigate]) {
- DocumentManager.Instance.jumpToDocument(curDoc, false);
+ this.jumpToTabOrRight(curDocButtons, curDoc);
} else if (curDocButtons[buttonIndex.Show]) {
let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(curDoc, true);
+ if (curDocButtons[buttonIndex.OpenRight]) {
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(curDoc, true);
+ } else {
+ await DocumentManager.Instance.jumpToDocument(curDoc, false, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
+ }
+
let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
curDoc.viewScale = newScale;
@@ -378,9 +390,15 @@ export class PresentationView extends React.Component<PresViewProps> {
return;
}
let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]);
+ let curDocButtons = this.presElementsMappings.get(docToJump)!.selected;
+
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
+ if (curDocButtons[buttonIndex.OpenRight]) {
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(docToJump, willZoom);
+ } else {
+ await DocumentManager.Instance.jumpToDocument(docToJump, willZoom, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
+ }
let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc);
curDoc.viewScale = newScale;
//saving the scale that user was on
@@ -390,6 +408,14 @@ export class PresentationView extends React.Component<PresViewProps> {
}
+ jumpToTabOrRight = (curDocButtons: boolean[], curDoc: Doc) => {
+ if (curDocButtons[buttonIndex.OpenRight]) {
+ DocumentManager.Instance.jumpToDocument(curDoc, false);
+ } else {
+ DocumentManager.Instance.jumpToDocument(curDoc, false, undefined, doc => CollectionDockingView.Instance.AddTab(undefined, doc, undefined));
+ }
+ }
+
/**
* Async function that supposedly return the doc that is located at given index.
*/
@@ -579,18 +605,35 @@ export class PresentationView extends React.Component<PresViewProps> {
//The function that starts or resets presentaton functionally, depending on status flag.
@action
- startOrResetPres = () => {
+ startOrResetPres = async () => {
if (this.presStatus) {
this.resetPresentation();
} else {
this.presStatus = true;
- this.startPresentation(0);
+ let startIndex = await this.findStartDocument();
+ this.startPresentation(startIndex);
const current = NumCast(this.curPresentation.selectedDoc);
- this.gotoDocument(0, current);
+ this.gotoDocument(startIndex, current);
}
this.curPresentation.presStatus = this.presStatus;
}
+ findStartDocument = async () => {
+ let docAtZero = await this.getDocAtIndex(0);
+ if (docAtZero === undefined) {
+ return 0;
+ }
+ let docAtZeroPresId = StrCast(docAtZero.presentId);
+
+ if (this.groupMappings.has(docAtZeroPresId)) {
+ let group = this.groupMappings.get(docAtZeroPresId)!;
+ let lastDoc = group[group.length - 1];
+ return this.childrenDocs.indexOf(lastDoc);
+ } else {
+ return 0;
+ }
+ }
+
//The function that resets the presentation by removing every action done by it. It also
//stops the presentaton.
@action
@@ -809,56 +852,127 @@ export class PresentationView extends React.Component<PresViewProps> {
this.presElementsMappings.set(keyDoc, elem);
}
+ _downsize = 0;
+ onPointerDown = (e: React.PointerEvent) => {
+ this._downsize = e.clientX;
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointermove", this.onPointerMove);
+ document.addEventListener("pointerup", this.onPointerUp);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ @action
+ onPointerMove = (e: PointerEvent) => {
+
+ this.curPresentation.width = Math.max(window.innerWidth - e.clientX, 300);
+ }
+ @action
+ onPointerUp = (e: PointerEvent) => {
+ if (Math.abs(e.clientX - this._downsize) < 4) {
+ let presWidth = NumCast(this.curPresentation.width);
+ if (presWidth - 300 !== 0) {
+ this.curPresentation.width = 0;
+ }
+ }
+ document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ togglePresView = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ let width = NumCast(this.curPresentation.width);
+ if (width === 0) {
+ this.curPresentation.width = 300;
+ }
+ }
+ @action
+ openPresMode = () => {
+ if (!this.presMode) {
+ this.curPresentation.width = 0;
+ this.presMode = true;
+ }
+ }
+
+ @action
+ closePresMode = () => {
+ if (this.presMode) {
+ this.presMode = false;
+ this.curPresentation.width = 300;
+ }
+
+ }
+
+ renderPresMode = () => {
+ if (this.presMode) {
+ return <PresModeMenu next={this.next} back={this.back} startOrResetPres={this.startOrResetPres} presStatus={this.presStatus} closePresMode={this.closePresMode} />;
+ } else {
+ return (null);
+ }
+
+ }
render() {
let width = NumCast(this.curPresentation.width);
return (
- <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}>
- <div className="presentationView-heading">
- {this.renderSelectOrPresSelection()}
- <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button>
- <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
- runInAction(() => { if (this.PresTitleChangeOpen) { this.PresTitleChangeOpen = false; } });
- runInAction(() => this.PresTitleInputOpen ? this.PresTitleInputOpen = false : this.PresTitleInputOpen = true);
- }}><FontAwesomeIcon icon={"plus"} /></button>
- <button title="Remove Presentation" className='presentation-icon' style={{ marginRight: 10 }} onClick={this.removePresentation}><FontAwesomeIcon icon={"minus"} /></button>
- <button title="Change Presentation Title" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
- runInAction(() => { if (this.PresTitleInputOpen) { this.PresTitleInputOpen = false; } });
- runInAction(() => this.PresTitleChangeOpen ? this.PresTitleChangeOpen = false : this.PresTitleChangeOpen = true);
- }}><FontAwesomeIcon icon={"edit"} /></button>
+ <div>
+ <div className="presentationView-cont" onPointerEnter={action(() => !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflowY: "scroll", overflowX: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}>
+ <div className="presentationView-heading">
+ {this.renderSelectOrPresSelection()}
+ <button title="Close Presentation" className='presentation-icon' onClick={this.closePresentation}><FontAwesomeIcon icon={"times"} /></button>
+ <button title="Open Presentation Mode" className="presentation-icon" style={{ marginRight: 10 }} onClick={this.openPresMode}><FontAwesomeIcon icon={"eye"} /></button>
+ <button title="Add Presentation" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
+ runInAction(() => { if (this.PresTitleChangeOpen) { this.PresTitleChangeOpen = false; } });
+ runInAction(() => this.PresTitleInputOpen ? this.PresTitleInputOpen = false : this.PresTitleInputOpen = true);
+ }}><FontAwesomeIcon icon={"plus"} /></button>
+ <button title="Remove Presentation" className='presentation-icon' style={{ marginRight: 10 }} onClick={this.removePresentation}><FontAwesomeIcon icon={"minus"} /></button>
+ <button title="Change Presentation Title" className="presentation-icon" style={{ marginRight: 10 }} onClick={() => {
+ runInAction(() => { if (this.PresTitleInputOpen) { this.PresTitleInputOpen = false; } });
+ runInAction(() => this.PresTitleChangeOpen ? this.PresTitleChangeOpen = false : this.PresTitleChangeOpen = true);
+ }}><FontAwesomeIcon icon={"edit"} /></button>
+ </div>
+ <div className="presentation-buttons">
+ <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
+ {this.renderPlayPauseButton()}
+ <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ </div>
+
+ <PresentationViewList
+ mainDocument={this.curPresentation}
+ deleteDocument={this.RemoveDoc}
+ gotoDocument={this.gotoDocument}
+ groupMappings={this.groupMappings}
+ PresElementsMappings={this.presElementsMappings}
+ setChildrenDocs={this.setChildrenDocs}
+ presStatus={this.presStatus}
+ presButtonBackUp={this.presButtonBackUp}
+ presGroupBackUp={this.presGroupBackUp}
+ removeDocByRef={this.removeDocByRef}
+ clearElemMap={() => this.presElementsMappings.clear()}
+ />
+ <input
+ type="checkbox"
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this.persistOpacity = e.target.checked;
+ this.opacity = this.persistOpacity ? 1 : 0.4;
+ })}
+ checked={this.persistOpacity}
+ style={{ position: "absolute", bottom: 5, left: 5 }}
+ onPointerEnter={action(() => this.labelOpacity = 1)}
+ onPointerLeave={action(() => this.labelOpacity = 0)}
+ />
+ <p style={{ position: "absolute", bottom: 1, left: 22, opacity: this.labelOpacity, transition: "0.7s opacity ease" }}>opacity {this.persistOpacity ? "persistent" : "on focus"}</p>
</div>
- <div className="presentation-buttons">
- <button title="Back" className="presentation-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
- {this.renderPlayPauseButton()}
- <button title="Next" className="presentation-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ <div className="mainView-libraryHandle"
+ style={{ cursor: "ew-resize", right: `${width - 10}px`, backgroundColor: "white", opacity: this.opacity, transition: "0.7s opacity ease" }}
+ onPointerDown={this.onPointerDown} onClick={this.togglePresView}>
+ <span title="library View Dragger" style={{ width: "100%", height: "100%", position: "absolute" }} />
</div>
- <input
- type="checkbox"
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- this.persistOpacity = e.target.checked;
- this.opacity = this.persistOpacity ? 1 : 0.4;
- })}
- checked={this.persistOpacity}
- style={{ position: "absolute", bottom: 5, left: 5 }}
- onPointerEnter={action(() => this.labelOpacity = 1)}
- onPointerLeave={action(() => this.labelOpacity = 0)}
- />
- <p style={{ position: "absolute", bottom: 1, left: 22, opacity: this.labelOpacity, transition: "0.7s opacity ease" }}>opacity {this.persistOpacity ? "persistent" : "on focus"}</p>
- <PresentationViewList
- mainDocument={this.curPresentation}
- deleteDocument={this.RemoveDoc}
- gotoDocument={this.gotoDocument}
- groupMappings={this.groupMappings}
- PresElementsMappings={this.presElementsMappings}
- setChildrenDocs={this.setChildrenDocs}
- presStatus={this.presStatus}
- presButtonBackUp={this.presButtonBackUp}
- presGroupBackUp={this.presGroupBackUp}
- removeDocByRef={this.removeDocByRef}
- clearElemMap={() => this.presElementsMappings.clear()}
- />
+ {this.renderPresMode()}
+
</div>
);
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index da4f459e2..59314783b 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -12,6 +12,7 @@ import { scriptingGlobal } from "../client/util/Scripting";
import { List } from "./List";
import { DocumentType } from "../client/documents/Documents";
import { ComputedField } from "./ScriptField";
+import { PrefetchProxy } from "./Proxy";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -348,7 +349,7 @@ export namespace Doc {
while (proto && !Doc.IsPrototype(proto)) {
proto = proto.proto;
}
- (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;
+ (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
}, 0);
} else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already
docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0);
diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts
index a6df31e81..d5da56b10 100644
--- a/src/new_fields/SchemaHeaderField.ts
+++ b/src/new_fields/SchemaHeaderField.ts
@@ -48,11 +48,11 @@ export class SchemaHeaderField extends ObjectField {
color: string;
type: number;
- constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType) {
+ constructor(heading: string = "", color?: string, type?: ColumnType) {
super();
this.heading = heading;
- this.color = color;
+ this.color = color === "" || color === undefined ? RandomPastel() : color;
if (type) {
this.type = type;
}
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index 182b22a1a..1ff0e3b31 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -139,7 +139,7 @@ def write_text_doc(content):
data_doc = {
"_id": data_doc_guid,
"fields": {
- "proto": protofy("commonImportProto"),
+ "proto": protofy("textProto"),
"data": {
"Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}',
"__type": "RichTextField"
@@ -235,7 +235,7 @@ def parse_document(file_name: str):
count += 1
view_guids.append(write_image(pure_name, image))
copyfile(dir_path + "/" + image, dir_path +
- "/" + image.replace(".", "_o.", 1))
+ "/" + image.replace(".", "_o.", 1))
os.rename(dir_path + "/" + image, dir_path +
"/" + image.replace(".", "_m.", 1))
print(f"extracted {count} images...")
diff --git a/src/server/index.ts b/src/server/index.ts
index 0afbcc4ee..378485b33 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -440,8 +440,22 @@ app.post(RouteStore.forgot, postForgot);
app.get(RouteStore.reset, getReset);
app.post(RouteStore.reset, postReset);
-app.use(RouteStore.corsProxy, (req, res) =>
- req.pipe(request(decodeURIComponent(req.url.substring(1)))).pipe(res));
+const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
+app.use(RouteStore.corsProxy, (req, res) => {
+ req.pipe(request(decodeURIComponent(req.url.substring(1)))).on("response", res => {
+ const headers = Object.keys(res.headers);
+ headers.forEach(headerName => {
+ const header = res.headers[headerName];
+ if (Array.isArray(header)) {
+ res.headers[headerName] = header.filter(h => !headerCharRegex.test(h));
+ } else if (header) {
+ if (headerCharRegex.test(header as any)) {
+ delete res.headers[headerName];
+ }
+ }
+ });
+ }).pipe(res);
+});
app.get(RouteStore.delete, (req, res) => {
if (release) {