aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/DocumentManager.ts11
-rw-r--r--src/client/util/DragManager.ts28
-rw-r--r--src/client/util/SelectionManager.ts5
-rw-r--r--src/client/util/TooltipTextMenu.tsx72
-rw-r--r--src/client/util/request-image-size.js2
-rw-r--r--src/client/views/DocumentDecorations.tsx50
-rw-r--r--src/client/views/GlobalKeyHandler.ts145
-rw-r--r--src/client/views/MainOverlayTextBox.tsx2
-rw-r--r--src/client/views/MainView.tsx79
-rw-r--r--src/client/views/SearchItem.tsx69
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx37
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx20
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx25
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx58
-rw-r--r--src/client/views/globalCssVariables.scss2
-rw-r--r--src/client/views/globalCssVariables.scss.d.ts1
-rw-r--r--src/client/views/nodes/DocumentView.tsx19
-rw-r--r--src/client/views/nodes/FieldView.tsx10
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx10
-rw-r--r--src/client/views/nodes/ImageBox.tsx44
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx22
-rw-r--r--src/client/views/nodes/LinkMenuItem.tsx13
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/pdf/Annotation.tsx144
-rw-r--r--src/client/views/pdf/PDFViewer.scss3
-rw-r--r--src/client/views/pdf/PDFViewer.tsx150
-rw-r--r--src/client/views/pdf/Page.tsx4
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx3
-rw-r--r--src/client/views/search/FilterBox.scss56
-rw-r--r--src/client/views/search/FilterBox.tsx89
-rw-r--r--src/client/views/search/Pager.tsx12
-rw-r--r--src/client/views/search/SearchBox.scss7
-rw-r--r--src/client/views/search/SearchBox.tsx8
-rw-r--r--src/client/views/search/SearchItem.scss58
-rw-r--r--src/client/views/search/SearchItem.tsx191
-rw-r--r--src/client/views/search/SelectorContextMenu.scss1
-rw-r--r--src/client/views/search/ToggleBar.tsx1
42 files changed, 1040 insertions, 451 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6dc98dbbc..7d7a1f02a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -97,9 +97,10 @@ export namespace DocUtils {
let linkDocProto = Doc.GetProto(linkDoc);
linkDocProto.context = targetContext;
- linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title;
+ linkDocProto.title = title === "" ? source.title + " to " + target.title : title;
linkDocProto.linkDescription = description;
linkDocProto.linkTags = tags;
+ linkDocProto.type = DocTypes.LINK;
linkDocProto.anchor1 = source;
linkDocProto.anchor1Page = source.curPage;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index d0014dbf3..acd8dcef7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -44,15 +44,14 @@ export class DocumentManager {
DocumentManager.Instance.DocumentViews.map(view => {
let doc = view.props.Document.proto;
if (doc && doc[Id]) {
- if(doc[Id] === id)
- {toReturn.push(view);}
+ if (doc[Id] === id) { toReturn.push(view); }
}
});
}
return toReturn;
}
- public getAllDocumentViews(doc: Doc){
+ public getAllDocumentViews(doc: Doc) {
return this.getDocumentViewsById(doc[Id]);
}
@@ -150,7 +149,7 @@ export class DocumentManager {
docContext.panTransformType = "Ease";
targetContextView.props.focus(docDelegate, willZoom);
} else {
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, docContext);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, undefined);
setTimeout(() => {
this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
}, 10);
@@ -159,7 +158,7 @@ export class DocumentManager {
const actualDoc = Doc.MakeAlias(docDelegate);
actualDoc.libraryBrush = true;
if (linkPage !== undefined) actualDoc.curPage = linkPage;
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, actualDoc);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, undefined);
}
} else {
let contextView: DocumentView | null;
@@ -168,7 +167,7 @@ export class DocumentManager {
contextDoc.panTransformType = "Ease";
contextView.props.focus(docDelegate, willZoom);
} else {
- (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, contextDoc);
+ (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, undefined);
setTimeout(() => {
this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage);
}, 10);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index a6bba3656..7dc48fb78 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,18 +1,24 @@
-import { action, runInAction, observable } from "mobx";
-import { Doc, DocListCastAsync } from "../../new_fields/Doc";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { action, runInAction } from "mobx";
+import { Doc } from "../../new_fields/Doc";
+import { Cast } from "../../new_fields/Types";
+import { URLField } from "../../new_fields/URLField";
import { emptyFunction } from "../../Utils";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import * as globalCssVariables from "../views/globalCssVariables.scss";
+import { DocumentManager } from "./DocumentManager";
import { LinkManager } from "./LinkManager";
-import { URLField } from "../../new_fields/URLField";
import { SelectionManager } from "./SelectionManager";
-import { Docs, DocUtils } from "../documents/Documents";
-import { DocumentManager } from "./DocumentManager";
-import { Id } from "../../new_fields/FieldSymbols";
export type dropActionType = "alias" | "copy" | undefined;
-export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc>, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) {
+export function SetupDrag(
+ _reference: React.RefObject<HTMLElement>,
+ docFunc: () => Doc | Promise<Doc>,
+ moveFunc?: DragManager.MoveFunction,
+ dropAction?: dropActionType,
+ options?: any,
+ dontHideOnDrop?: boolean,
+ dragStarted?: () => void
+) {
let onRowMove = async (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
@@ -20,12 +26,13 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
document.removeEventListener("pointermove", onRowMove);
document.removeEventListener('pointerup', onRowUp);
let doc = await docFunc();
- var dragData = new DragManager.DocumentDragData([doc], [doc]);
+ var dragData = new DragManager.DocumentDragData([doc], [undefined]);
dragData.dropAction = dropAction;
dragData.moveDocument = moveFunc;
dragData.options = options;
dragData.dontHideOnDrop = dontHideOnDrop;
DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
+ dragStarted && dragStarted();
};
let onRowUp = (): void => {
document.removeEventListener("pointermove", onRowMove);
@@ -34,6 +41,7 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: ()
let onItemDown = async (e: React.PointerEvent) => {
if (e.button === 0) {
e.stopPropagation();
+ e.preventDefault();
if (e.shiftKey && CollectionDockingView.Instance) {
CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]);
} else {
@@ -111,6 +119,8 @@ export namespace DragManager {
hideSource: boolean | (() => boolean);
+ dragHasStarted?: () => void;
+
withoutShiftDrag?: boolean;
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 7dbb81e76..3bc71ad42 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,11 +1,13 @@
-import { observable, action, runInAction } from "mobx";
+import { observable, action, runInAction, IReactionDisposer, reaction, autorun } from "mobx";
import { Doc } from "../../new_fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { NumCast } from "../../new_fields/Types";
export namespace SelectionManager {
+
class Manager {
+
@observable IsDragging: boolean = false;
@observable SelectedDocuments: Array<DocumentView> = [];
@@ -18,6 +20,7 @@ export namespace SelectionManager {
}
manager.SelectedDocuments.push(docView);
+ // console.log(manager.SelectedDocuments);
docView.props.whenActiveChanged(true);
}
}
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 5c1324d7d..e72e3cf5e 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,57 +1,37 @@
-import { action, IReactionDisposer, reaction } from "mobx";
-import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css
-import { baseKeymap, lift, deleteSelection } from "prosemirror-commands";
-import { history, redo, undo } from "prosemirror-history";
-import { keymap } from "prosemirror-keymap";
-import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state";
+import { action } from "mobx";
+import { Dropdown, MenuItem, icons, } from "prosemirror-menu"; //no import css
+import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { schema } from "./RichTextSchema";
import { Schema, NodeType, MarkType, Mark, ResolvedPos } from "prosemirror-model";
-import { Node as ProsNode } from "prosemirror-model"
-import React = require("react");
+import { Node as ProsNode } from "prosemirror-model";
import "./TooltipTextMenu.scss";
-const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
+const { toggleMark, setBlockType } = require("prosemirror-commands");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list';
-import { liftTarget, RemoveMarkStep, AddMarkStep } from 'prosemirror-transform';
-import {
- faListUl, faGrinTongueSquint,
-} from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { wrapInList, liftListItem, } from 'prosemirror-schema-list';
+import { faListUl } from '@fortawesome/free-solid-svg-icons';
import { FieldViewProps } from "../views/nodes/FieldView";
-import { throwStatement } from "babel-types";
const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
-import { View } from "@react-pdf/renderer";
import { DragManager } from "./DragManager";
import { Doc, Opt, Field } from "../../new_fields/Doc";
import { DocServer } from "../DocServer";
-import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { DocumentManager } from "./DocumentManager";
import { Id } from "../../new_fields/FieldSymbols";
-import { Utils } from "../../Utils";
import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox";
-import { text } from "body-parser";
-import { type } from "os";
-// import { wrap } from "module";
-
-const SVG = "http://www.w3.org/2000/svg";
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
public tooltip: HTMLElement;
- private num_icons = 0;
private view: EditorView;
private fontStyles: MarkType[];
private fontSizes: MarkType[];
private listTypes: NodeType[];
private editorProps: FieldViewProps & FormattedTextBoxProps;
- private state: EditorState;
private fontSizeToNum: Map<MarkType, number>;
private fontStylesToName: Map<MarkType, string>;
private listTypeToIcon: Map<NodeType, string>;
- private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
private link: HTMLAnchorElement;
//private wrapper: HTMLDivElement;
@@ -69,7 +49,6 @@ export class TooltipTextMenu {
constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) {
this.view = view;
- this.state = view.state;
this.editorProps = editorProps;
//this.wrapper = document.createElement("div");
this.tooltip = document.createElement("div");
@@ -285,7 +264,7 @@ export class TooltipTextMenu {
if (DocumentManager.Instance.getDocumentView(f)) {
DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false);
}
- else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, f);
+ else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, undefined);
}
}));
}
@@ -378,7 +357,7 @@ export class TooltipTextMenu {
//for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
changeToMarkInGroup = (markType: MarkType, view: EditorView, fontMarks: MarkType[]) => {
- let { empty, $cursor, ranges } = view.state.selection as TextSelection;
+ let { $cursor, ranges } = view.state.selection as TextSelection;
let state = view.state;
let dispatch = view.dispatch;
@@ -390,13 +369,12 @@ export class TooltipTextMenu {
dispatch(state.tr.removeStoredMark(type));
}
} else {
- let has = false, tr = state.tr;
+ let has = false;
for (let i = 0; !has && i < ranges.length; i++) {
let { $from, $to } = ranges[i];
has = state.doc.rangeHasMark($from.pos, $to.pos, type);
}
for (let i of ranges) {
- let { $from, $to } = i;
if (has) {
toggleMark(type)(view.state, view.dispatch, view);
}
@@ -418,7 +396,7 @@ export class TooltipTextMenu {
}
//remove all node typeand apply the passed-in one to the selected text
- changeToNodeType(nodeType: NodeType | undefined, view: EditorView, allNodes: NodeType[]) {
+ changeToNodeType(nodeType: NodeType | undefined, view: EditorView) {
//remove old
liftListItem(schema.nodes.list_item)(view.state, view.dispatch);
if (nodeType) { //add new
@@ -435,7 +413,7 @@ export class TooltipTextMenu {
execEvent: "",
class: "menuicon",
css: css,
- enable(state) { return true; },
+ enable() { return true; },
run() {
changeToMarkInGroup(markType, view, groupMarks);
}
@@ -450,7 +428,7 @@ export class TooltipTextMenu {
css: "color:white;",
class: "summarize",
execEvent: "",
- run: (state, dispatch, view) => {
+ run: (state, dispatch) => {
TooltipTextMenu.insertStar(state, dispatch);
}
@@ -465,7 +443,7 @@ export class TooltipTextMenu {
execEvent: "",
css: "color:white;",
class: "summarize",
- run: (state, dispatch, view) => {
+ run: () => {
this.collapseToolTip();
}
});
@@ -544,7 +522,7 @@ export class TooltipTextMenu {
execEvent: "",
class: "menuicon",
css: css,
- enable(state) { return true; },
+ enable() { return true; },
run() {
changeToNodeInGroup(nodeType, view, groupNodes);
}
@@ -663,17 +641,6 @@ export class TooltipTextMenu {
//return;
}
- //let linksInSelection = this.activeMarksOnSelection([schema.marks.link]);
- // if (linksInSelection.length > 0) {
- // let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs;
- // this.link.href = attributes.href;
- // this.link.textContent = attributes.title;
- // this.link.style.visibility = "visible";
- // } else this.link.style.visibility = "hidden";
-
- // Otherwise, reposition it and update its content
- //this.tooltip.style.display = "";
- let { from, to } = state.selection;
//UPDATE LIST ITEM DROPDOWN
@@ -720,17 +687,16 @@ export class TooltipTextMenu {
//finds all active marks on selection in given group
activeMarksOnSelection(markGroup: MarkType[]) {
//current selection
- let { empty, $cursor, ranges } = this.view.state.selection as TextSelection;
+ let { empty, ranges } = this.view.state.selection as TextSelection;
let state = this.view.state;
let dispatch = this.view.dispatch;
let activeMarks: MarkType[];
if (!empty) {
activeMarks = markGroup.filter(mark => {
if (dispatch) {
- let has = false, tr = state.tr;
+ let has = false;
for (let i = 0; !has && i < ranges.length; i++) {
let { $from, $to } = ranges[i];
- let hasmark: boolean = state.doc.rangeHasMark($from.pos, $to.pos, mark);
return state.doc.rangeHasMark($from.pos, $to.pos, mark);
}
}
@@ -741,9 +707,7 @@ export class TooltipTextMenu {
const pos = this.view.state.selection.$from;
const ref_node: ProsNode = this.reference_node(pos);
if (ref_node !== null && ref_node !== this.view.state.doc) {
- let text_node_type: NodeType;
if (ref_node.isText) {
- text_node_type = ref_node.type;
}
else {
return [];
@@ -778,7 +742,7 @@ export class TooltipTextMenu {
else if (pos.pos > 0) {
let skip = false;
for (let i: number = pos.pos - 1; i > 0; i--) {
- this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode, pos: number, parent: ProsNode, index: number) => {
+ this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => {
if (node.isLeaf && !skip) {
ref_node = node;
skip = true;
diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js
index 0f9328872..f6fe1068a 100644
--- a/src/client/util/request-image-size.js
+++ b/src/client/util/request-image-size.js
@@ -10,7 +10,7 @@
*/
const request = require('request');
-const imageSize = require('image-size');
+// const imageSize = require('image-size');
const HttpError = require('standard-http-error');
module.exports = function requestImageSize(options) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index db10007f4..61e9d209a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -26,6 +26,7 @@ import { Template, Templates } from "./Templates";
import React = require("react");
import { RichTextField } from '../../new_fields/RichTextField';
import { LinkManager } from '../util/LinkManager';
+import { ObjectField } from '../../new_fields/ObjectField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -77,32 +78,31 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._fieldKey = text.slice(1, text.length);
this._title = this.selectionTitle;
} else if (text.startsWith(">")) {
- let field = SelectionManager.SelectedDocuments()[0];
- let collection = field.props.ContainingCollectionView!.props.Document;
-
- let collectionKey = field.props.ContainingCollectionView!.props.fieldKey;
- let collectionKeyProp = `fieldKey={"${collectionKey}"}`;
+ let fieldTemplateView = SelectionManager.SelectedDocuments()[0];
+ SelectionManager.DeselectAll();
+ let fieldTemplate = fieldTemplateView.props.Document;
+ let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
let metaKey = text.slice(1, text.length);
- let metaKeyProp = `fieldKey={"${metaKey}"}`;
-
- let template = Doc.MakeAlias(field.props.Document);
- template.proto = collection;
- template.title = metaKey;
- template.nativeWidth = Cast(field.nativeWidth, "number");
- template.nativeHeight = Cast(field.nativeHeight, "number");
- template.embed = true;
- template.isTemplate = true;
- template.templates = new List<string>([Templates.TitleBar(metaKey)]);
- if (field.props.Document.backgroundLayout) {
- let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
- let collectionAnoKeyProp = `fieldKey={"annotations"}`;
- template.layout = StrCast(field.props.Document.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
- template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
- } else {
- template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp);
+
+ // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+ let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
+ let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ if (backgroundLayout) {
+ layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
+ backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
}
- Doc.AddDocToList(collection, collectionKey, template);
- SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document));
+ let nw = Cast(fieldTemplate.nativeWidth, "number");
+ let nh = Cast(fieldTemplate.nativeHeight, "number");
+
+ fieldTemplate.title = metaKey;
+ fieldTemplate.layout = layout;
+ fieldTemplate.backgroundLayout = backgroundLayout;
+ fieldTemplate.nativeWidth = nw;
+ fieldTemplate.nativeHeight = nh;
+ fieldTemplate.embed = true;
+ fieldTemplate.isTemplate = true;
+ fieldTemplate.templates = new List<string>([Templates.TitleBar(metaKey)]);
+ fieldTemplate.proto = Doc.GetProto(docTemplate);
}
else {
if (SelectionManager.SelectedDocuments().length > 0) {
@@ -500,7 +500,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let nheight = doc.nativeHeight || 0;
let width = (doc.width || 0);
let height = (doc.height || (nheight / nwidth * width));
- let scale = element.props.ScreenToLocalTransform().Scale;
+ let scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling();
let actualdW = Math.max(width + (dW * scale), 20);
let actualdH = Math.max(height + (dH * scale), 20);
doc.x = (doc.x || 0) + dX * (actualdW - width);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
new file mode 100644
index 000000000..e467d7c61
--- /dev/null
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -0,0 +1,145 @@
+import { UndoManager } from "../util/UndoManager";
+import { SelectionManager } from "../util/SelectionManager";
+import { CollectionDockingView } from "./collections/CollectionDockingView";
+import { MainView } from "./MainView";
+import { DragManager } from "../util/DragManager";
+import { action } from "mobx";
+
+const modifiers = ["control", "meta", "shift", "alt"];
+type KeyHandler = (keycode: string) => KeyControlInfo;
+type KeyControlInfo = {
+ preventDefault: boolean,
+ stopPropagation: boolean
+};
+
+export default class KeyManager {
+ public static Handler: KeyManager = new KeyManager();
+ private router = new Map<string, KeyHandler>();
+
+ constructor() {
+ let isMac = navigator.platform.toLowerCase().indexOf("mac") >= 0;
+
+ // SHIFT CONTROL ALT META
+ this.router.set("0000", this.unmodified);
+ this.router.set(isMac ? "0001" : "0100", this.ctrl);
+ this.router.set(isMac ? "0100" : "0010", this.alt);
+ this.router.set(isMac ? "1001" : "1100", this.ctrl_shift);
+ }
+
+ public handle = (e: KeyboardEvent) => {
+ let keyname = e.key.toLowerCase();
+ this.handleGreedy(keyname);
+
+ if (modifiers.includes(keyname)) {
+ return;
+ }
+
+ let bit = (value: boolean) => value ? "1" : "0";
+ let modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
+
+ let handleConstrained = this.router.get(modifierIndex);
+ if (!handleConstrained) {
+ return;
+ }
+
+ let control = handleConstrained(keyname);
+
+ control.stopPropagation && e.stopPropagation();
+ control.preventDefault && e.preventDefault();
+ }
+
+ private handleGreedy = action((keyname: string) => {
+ switch (keyname) {
+ }
+ });
+
+ private unmodified = action((keyname: string) => {
+ switch (keyname) {
+ case "escape":
+ if (MainView.Instance.isPointerDown) {
+ DragManager.AbortDrag();
+ } else {
+ if (CollectionDockingView.Instance.HasFullScreen()) {
+ CollectionDockingView.Instance.CloseFullScreen();
+ } else {
+ SelectionManager.DeselectAll();
+ }
+ }
+ break;
+ }
+
+ return {
+ stopPropagation: false,
+ preventDefault: false
+ };
+ });
+
+ private alt = action((keyname: string) => {
+ let stopPropagation = true;
+ let preventDefault = true;
+
+ switch (keyname) {
+ case "n":
+ let toggle = MainView.Instance.addMenuToggle.current!;
+ toggle.checked = !toggle.checked;
+ break;
+ }
+
+ return {
+ stopPropagation: stopPropagation,
+ preventDefault: preventDefault
+ };
+ });
+
+ private ctrl = action((keyname: string) => {
+ let stopPropagation = true;
+ let preventDefault = true;
+
+ switch (keyname) {
+ case "arrowright":
+ MainView.Instance.mainFreeform && CollectionDockingView.Instance.AddRightSplit(MainView.Instance.mainFreeform, undefined);
+ break;
+ case "arrowleft":
+ MainView.Instance.mainFreeform && CollectionDockingView.Instance.CloseRightSplit(MainView.Instance.mainFreeform);
+ break;
+ case "f":
+ MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible;
+ break;
+ case "o":
+ let target = SelectionManager.SelectedDocuments()[0];
+ target && target.fullScreenClicked();
+ break;
+ case "r":
+ preventDefault = false;
+ break;
+ case "y":
+ UndoManager.Redo();
+ break;
+ case "z":
+ UndoManager.Undo();
+ break;
+ }
+
+ return {
+ stopPropagation: stopPropagation,
+ preventDefault: preventDefault
+ };
+ });
+
+ private ctrl_shift = action((keyname: string) => {
+ let stopPropagation = true;
+ let preventDefault = true;
+
+ switch (keyname) {
+ case "z":
+ UndoManager.Redo();
+ break;
+ }
+
+ return {
+ stopPropagation: stopPropagation,
+ preventDefault: preventDefault
+ };
+ });
+
+} \ No newline at end of file
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 1933ab94b..d31319429 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -108,7 +108,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
document.removeEventListener('pointerup', this.textBoxUp);
}
- addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
+ addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
if (true) { // location === "onRight") { need to figure out stack to add "inTab"
CollectionDockingView.Instance.AddRightSplit(doc, dataDoc);
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6290d8985..087bc904a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -36,24 +36,24 @@ import { CollectionBaseView } from './collections/CollectionBaseView';
import { List } from '../../new_fields/List';
import PDFMenu from './pdf/PDFMenu';
import { InkTool } from '../../new_fields/InkField';
-import * as _ from "lodash";
+import _ from "lodash";
+import KeyManager from './GlobalKeyHandler';
@observer
export class MainView extends React.Component {
public static Instance: MainView;
+ @observable addMenuToggle = React.createRef<HTMLInputElement>();
@observable private _workspacesShown: boolean = false;
@observable public pwidth: number = 0;
@observable public pheight: number = 0;
@computed private get mainContainer(): Opt<Doc> {
return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc));
}
- @computed private get mainFreeform(): Opt<Doc> {
+ @computed get mainFreeform(): Opt<Doc> {
let docs = DocListCast(this.mainContainer!.data);
return (docs && docs.length > 1) ? docs[1] : undefined;
}
- private globalDisplayFlags = observable({
- jumpToVisible: false
- });
+ public isPointerDown = false;
private set mainContainer(doc: Opt<Doc>) {
if (doc) {
if (!("presentationView" in doc)) {
@@ -64,12 +64,23 @@ export class MainView extends React.Component {
}
componentWillMount() {
- document.removeEventListener("keydown", this.globalKeyHandler);
- document.addEventListener("keydown", this.globalKeyHandler);
+ window.removeEventListener("keydown", KeyManager.Handler.handle);
+ window.addEventListener("keydown", KeyManager.Handler.handle);
+
+ window.removeEventListener("pointerdown", this.pointerDown);
+ window.addEventListener("pointerdown", this.pointerDown);
+
+ window.removeEventListener("pointerup", this.pointerUp);
+ window.addEventListener("pointerup", this.pointerUp);
}
+ pointerDown = (e: PointerEvent) => this.isPointerDown = true;
+ pointerUp = (e: PointerEvent) => this.isPointerDown = false;
+
componentWillUnMount() {
- document.removeEventListener("keydown", this.globalKeyHandler);
+ window.removeEventListener("keydown", KeyManager.Handler.handle);
+ window.removeEventListener("pointerdown", this.pointerDown);
+ window.removeEventListener("pointerup", this.pointerUp);
}
constructor(props: Readonly<{}>) {
@@ -122,18 +133,6 @@ export class MainView extends React.Component {
// 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
- window.addEventListener("keydown", (e) => {
- if (e.key === "Escape") {
- DragManager.AbortDrag();
- SelectionManager.DeselectAll();
- } else if (e.key === "z" && e.ctrlKey) {
- e.preventDefault();
- UndoManager.Undo();
- } else if ((e.key === "y" && e.ctrlKey) || (e.key === "z" && e.ctrlKey && e.shiftKey)) {
- e.preventDefault();
- UndoManager.Redo();
- }
- }, false); // drag event handler
// click interactions for the context menu
document.addEventListener("pointerdown", action(function (e: PointerEvent) {
@@ -203,7 +202,7 @@ export class MainView extends React.Component {
openNotifsCol = () => {
if (this._notifsCol && CollectionDockingView.Instance) {
- CollectionDockingView.Instance.AddRightSplit(this._notifsCol, this._notifsCol);
+ CollectionDockingView.Instance.AddRightSplit(this._notifsCol, undefined);
}
}
@@ -292,7 +291,7 @@ export class MainView extends React.Component {
];
return < div id="add-nodes-menu" >
- <input type="checkbox" id="add-menu-toggle" />
+ <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
<label htmlFor="add-menu-toggle" title="Add Node"><p>+</p></label>
<div id="add-options-content">
@@ -300,8 +299,7 @@ export class MainView extends React.Component {
<li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li>
<li key="undo"><button className="add-button round-button" title="Undo" onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
<li key="redo"><button className="add-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
- <li key="color"><button className="add-button round-button" title="Redo" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
-
+ <li key="color"><button className="add-button round-button" title="Select Color" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
<div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
<SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
</div>
@@ -359,41 +357,10 @@ export class MainView extends React.Component {
@observable isSearchVisible = false;
@action
toggleSearch = () => {
+ // console.log("search toggling")
this.isSearchVisible = !this.isSearchVisible;
}
- @action
- globalKeyHandler = (e: KeyboardEvent) => {
- if (e.key === "Control" || !e.ctrlKey) return;
-
- if (e.key === "v") return;
- if (e.key === "c") return;
-
- e.preventDefault();
- e.stopPropagation();
-
- switch (e.key) {
- case "ArrowRight":
- if (this.mainFreeform) {
- CollectionDockingView.Instance.AddRightSplit(this.mainFreeform, undefined);
- }
- break;
- case "ArrowLeft":
- if (this.mainFreeform) {
- CollectionDockingView.Instance.CloseRightSplit(this.mainFreeform);
- }
- break;
- case "o":
- this.globalDisplayFlags.jumpToVisible = true;
- break;
- case "escape":
- _.mapValues(this.globalDisplayFlags, () => false);
- break;
- case "f":
- this.isSearchVisible = !this.isSearchVisible;
- }
- }
-
render() {
return (
diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx
new file mode 100644
index 000000000..13e4b88f7
--- /dev/null
+++ b/src/client/views/SearchItem.tsx
@@ -0,0 +1,69 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Doc } from "../../new_fields/Doc";
+import { DocumentManager } from "../util/DocumentManager";
+import { SetupDrag } from "../util/DragManager";
+
+
+export interface SearchProps {
+ doc: Doc;
+}
+
+library.add(faCaretUp);
+library.add(faObjectGroup);
+library.add(faStickyNote);
+library.add(faFilePdf);
+library.add(faFilm);
+
+export class SearchItem extends React.Component<SearchProps> {
+
+ onClick = () => {
+ DocumentManager.Instance.jumpToDocument(this.props.doc, false);
+ }
+
+ //needs help
+ // @computed get layout(): string { const field = Cast(this.props.doc[fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
+
+
+ public static DocumentIcon(layout: string) {
+ let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
+ layout.indexOf("ImageBox") !== -1 ? faImage :
+ layout.indexOf("Formatted") !== -1 ? faStickyNote :
+ layout.indexOf("Video") !== -1 ? faFilm :
+ layout.indexOf("Collection") !== -1 ? faObjectGroup :
+ faCaretUp;
+ return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
+ }
+ onPointerEnter = (e: React.PointerEvent) => {
+ this.props.doc.libraryBrush = true;
+ Doc.SetOnPrototype(this.props.doc, "protoBrush", true);
+ }
+ onPointerLeave = (e: React.PointerEvent) => {
+ this.props.doc.libraryBrush = false;
+ Doc.SetOnPrototype(this.props.doc, "protoBrush", false);
+ }
+
+ collectionRef = React.createRef<HTMLDivElement>();
+ startDocDrag = () => {
+ let doc = this.props.doc;
+ const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
+ if (isProto) {
+ return Doc.MakeDelegate(doc);
+ } else {
+ return Doc.MakeAlias(doc);
+ }
+ }
+ render() {
+ return (
+ <div className="search-item" ref={this.collectionRef} id="result"
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
+ onClick={this.onClick} onPointerDown={SetupDrag(this.collectionRef, this.startDocDrag)} >
+ <div className="search-title" id="result" >title: {this.props.doc.title}</div>
+ {/* <div className="search-type" id="result" >Type: {this.props.doc.layout}</div> */}
+ {/* <div className="search-type" >{SearchItem.DocumentIcon(this.layout)}</div> */}
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e675e82fc..13b0579d2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -24,7 +24,6 @@ import { SubCollectionViewProps } from "./CollectionSubView";
import { ParentDocSelector } from './ParentDocumentSelector';
import React = require("react");
import { MainView } from '../MainView';
-import { LinkManager } from '../../util/LinkManager';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faFile } from '@fortawesome/free-solid-svg-icons';
@@ -52,6 +51,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
private _flush: boolean = false;
private _ignoreStateChange = "";
private _isPointerDown = false;
+ private _maximizedSrc: Opt<DocumentView>;
constructor(props: SubCollectionViewProps) {
super(props);
@@ -66,12 +66,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.hack = true;
this.undohack = UndoManager.StartBatch("goldenDrag");
dragDocs.map((dragDoc, i) =>
- this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : dragDoc, true).contentItems[0].tab._dragListener.
+ this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : undefined, true).contentItems[0].tab._dragListener.
onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }));
}
@action
- public OpenFullScreen(document: Doc, dataDoc: Doc) {
+ public OpenFullScreen(docView: DocumentView) {
+ let document = Doc.MakeAlias(docView.props.Document);
+ let dataDoc = docView.dataDoc;
let newItemStackConfig = {
type: 'stack',
content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)]
@@ -80,10 +82,25 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.root.contentItems[0].addChild(docconfig);
docconfig.callDownwards('_$init');
this._goldenLayout._$maximiseItem(docconfig);
+ this._maximizedSrc = docView;
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
this.stateChanged();
}
+ public CloseFullScreen = () => {
+ let target = this._goldenLayout._maximisedItem;
+ if (target !== null && this._maximizedSrc) {
+ this._goldenLayout._maximisedItem.remove();
+ SelectionManager.SelectDoc(this._maximizedSrc, false);
+ this._maximizedSrc = undefined;
+ this.stateChanged();
+ }
+ }
+
+ public HasFullScreen = () => {
+ return this._goldenLayout._maximisedItem !== null;
+ }
+
@undoBatch
@action
public CloseRightSplit = (document: Doc): boolean => {
@@ -167,7 +184,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return newContentItem;
}
@action
- public AddTab = (stack: any, document: Doc, dataDocument: Doc) => {
+ public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => {
let docs = Cast(this.props.Document.data, listSpec(Doc));
if (docs) {
docs.push(document);
@@ -257,7 +274,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
@action
onPointerUp = (e: React.PointerEvent): void => {
- this._isPointerDown = false;
if (this._flush) {
this._flush = false;
setTimeout(() => this.stateChanged(), 10);
@@ -350,7 +366,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
}
let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc;
- if (doc instanceof Doc && dataDoc instanceof Doc) {
+ if (doc instanceof Doc) {
let dragSpan = document.createElement("span");
dragSpan.style.position = "relative";
dragSpan.style.bottom = "6px";
@@ -469,7 +485,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
constructor(props: any) {
super(props);
DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => {
- this._dataDoc = this._document = f as Doc;
+ this._document = f as Doc;
if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) {
DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt<Field>) => this._dataDoc = f as Doc));
}
@@ -510,7 +526,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; }
- addDocTab = (doc: Doc, dataDoc: Doc, location: string) => {
+ addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => {
if (doc.dockingConfig) {
MainView.Instance.openWorkspace(doc);
} else if (location === "onRight") {
@@ -520,15 +536,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
}
}
get content() {
- if (!this._document || !this._dataDoc) {
+ if (!this._document) {
return (null);
}
+ let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc;
return (
<div className="collectionDockingView-content" ref={this._mainCont}
style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier})` }}>
<DocumentView key={this._document[Id]}
Document={this._document}
- DataDoc={this._dataDoc}
+ DataDoc={resolvedDataDoc}
bringToFront={emptyFunction}
addDocument={undefined}
removeDocument={undefined}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 98bf513bb..b0d46953c 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -119,8 +119,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
let fieldContentView = <FieldView {...props} />;
let reference = React.createRef<HTMLDivElement>();
let onItemDown = (e: React.PointerEvent) => {
- (this.props.CollectionView.props.isSelected() ?
- SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e) : undefined);
+ (!this.props.CollectionView.props.isSelected() ? undefined :
+ SetupDrag(reference, () => props.Document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
};
let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc });
@@ -349,12 +349,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
<div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />;
}
+
@computed
get previewPanel() {
+ // let layoutDoc = this.previewDocument;
+ // let resolvedDataDoc = (layoutDoc !== this.props.DataDoc) ? this.props.DataDoc : undefined;
+ // if (layoutDoc && !(Cast(layoutDoc.layout, Doc) instanceof Doc) &&
+ // resolvedDataDoc && resolvedDataDoc !== layoutDoc) {
+ // // ... so change the layout to be an expanded view of the template layout. This allows the view override the template's properties and be referenceable as its own document.
+ // layoutDoc = Doc.expandTemplateLayout(layoutDoc, resolvedDataDoc);
+ // }
+
+ let layoutDoc = this.previewDocument ? Doc.expandTemplateLayout(this.previewDocument, this.props.DataDoc) : undefined;
return <div ref={this.createTarget}>
<CollectionSchemaPreview
- Document={this.previewDocument}
- DataDocument={BoolCast(this.props.Document.isTemplate) ? this.previewDocument : this.props.DataDoc}
+ Document={layoutDoc}
+ DataDocument={this.previewDocument !== this.props.DataDoc ? this.props.DataDoc : undefined}
childDocs={this.childDocs}
renderDepth={this.props.renderDepth}
width={this.previewWidth}
@@ -404,7 +414,7 @@ interface CollectionSchemaPreviewProps {
removeDocument: (document: Doc) => boolean;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
- addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
+ addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
setPreviewScript: (script: string) => void;
previewScript?: string;
}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6c4ea18a1..b10907937 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,12 +4,13 @@ import { action, computed, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
-import { BoolCast, NumCast } from "../../../new_fields/Types";
+import { BoolCast, NumCast, Cast } from "../../../new_fields/Types";
import { emptyFunction, Utils } from "../../../Utils";
import { ContextMenu } from "../ContextMenu";
import { CollectionSchemaPreview } from "./CollectionSchemaView";
import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
+import { resolve } from "bluebird";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -66,17 +67,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
get singleColumnChildren() {
let children = this.childDocs.filter(d => !d.isMinimized);
return children.map((d, i) => {
+ let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]());
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!).scale(this.columnWidth / d[WidthSym]());
let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
- let height = () => this.singleColDocHeight(d);
+ let height = () => this.singleColDocHeight(layoutDoc);
return <div className="collectionStackingView-columnDoc"
key={d[Id]}
ref={dref}
style={{ width: width(), height: height() }} >
<CollectionSchemaPreview
- Document={d}
- DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc}
+ Document={layoutDoc}
+ DataDocument={d !== this.props.DataDoc ? this.props.DataDoc : undefined}
renderDepth={this.props.renderDepth}
width={width}
height={height}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5c80fbd38..927aa363f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -36,7 +36,7 @@ export interface TreeViewProps {
deleteDoc: (doc: Doc) => boolean;
moveDocument: DragManager.MoveFunction;
dropAction: "alias" | "copy" | undefined;
- addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
panelWidth: () => number;
panelHeight: () => number;
addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean;
@@ -97,7 +97,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
@undoBatch delete = () => this.props.deleteDoc(this.resolvedDataDoc);
- @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.document, "onRight");
+ @undoBatch openRight = async () => this.props.addDocTab(this.props.document, undefined, "onRight");
onPointerDown = (e: React.PointerEvent) => e.stopPropagation();
onPointerEnter = (e: React.PointerEvent): void => {
@@ -183,16 +183,19 @@ class TreeView extends React.Component<TreeViewProps> {
let keys = Array.from(Object.keys(this.resolvedDataDoc));
if (this.resolvedDataDoc.proto instanceof Doc) {
keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto)));
- while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
}
- let keyList: string[] = keys.reduce((l, key) => Cast(this.resolvedDataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]);
+ let keyList: string[] = keys.reduce((l, key) => {
+ let listspec = DocListCast(this.resolvedDataDoc[key]);
+ if (listspec && listspec.length) return [...l, key];
+ return l;
+ }, [] as string[]);
keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key));
if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links");
if (keyList.indexOf(this.fieldKey) !== -1) {
keyList.splice(keyList.indexOf(this.fieldKey), 1);
}
keyList.splice(0, 0, this.fieldKey);
- return keyList;
+ return keyList.filter((item, index) => keyList.indexOf(item) >= index);
}
/**
* Renders the EditableView title element for placement into the tree.
@@ -233,7 +236,7 @@ class TreeView extends React.Component<TreeViewProps> {
onWorkspaceContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) });
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
@@ -322,14 +325,14 @@ class TreeView extends React.Component<TreeViewProps> {
this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
</ul >;
} else {
- console.log("PW = " + this.props.panelWidth());
- contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.props.panelHeight() }} key={this.props.document[Id]}>
+ let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
+ contentElement = <div ref={this._dref} style={{ display: "inline-block", height: layoutDoc[HeightSym]() }} key={this.props.document[Id]}>
<CollectionSchemaPreview
- Document={this.props.document}
+ Document={layoutDoc}
DataDocument={this.resolvedDataDoc}
renderDepth={this.props.renderDepth}
width={docWidth}
- height={this.props.panelHeight}
+ height={layoutDoc[HeightSym]}
getTransform={this.docTransform}
CollectionView={undefined}
addDocument={emptyFunction as any}
@@ -365,7 +368,7 @@ class TreeView extends React.Component<TreeViewProps> {
remove: ((doc: Doc) => boolean),
move: DragManager.MoveFunction,
dropAction: dropActionType,
- addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void,
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void,
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: () => boolean,
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 0517c17bf..e500e5c70 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -63,7 +63,7 @@ export class CollectionView extends React.Component<FieldViewProps> {
otherdoc.height = 50;
Doc.GetProto(otherdoc).title = "applied(" + this.props.Document.title + ")";
Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document);
- this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight");
+ this.props.addDocTab && this.props.addDocTab(otherdoc, undefined, "onRight");
}), icon: "project-diagram"
});
}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index fa7058d5a..b29a30069 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -9,7 +9,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
import { NumCast } from "../../../new_fields/Types";
import { CollectionViewType } from "./CollectionBaseView";
-type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc, location: string): void };
+type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void };
@observer
export class SelectorContextMenu extends React.Component<SelectorProps> {
@observable private _docs: { col: Doc, target: Doc }[] = [];
@@ -43,7 +43,7 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
col.panX = newPanX;
col.panY = newPanY;
}
- this.props.addDocTab(col, col, "inTab"); // bcz: dataDoc?
+ this.props.addDocTab(col, undefined, "inTab"); // bcz: dataDoc?
};
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 5ac2e1f9c..ccf261c95 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,7 +1,7 @@
@import "../../globalCssVariables";
.collectionfreeformview-ease {
- position: absolute;
+ position: inherit;
top: 0;
left: 0;
width: 100%;
@@ -25,8 +25,9 @@
height: 100%;
width: 100%;
}
+
>.jsx-parser {
- z-index:0;
+ z-index: 0;
}
//nested freeform views
@@ -40,25 +41,27 @@
border-radius: $border-radius;
box-sizing: border-box;
position: absolute;
+
.marqueeView {
overflow: hidden;
}
+
top: 0;
left: 0;
width: 100%;
height: 100%;
}
-
+
.collectionfreeformview-overlay {
.collectionfreeformview>.jsx-parser {
position: inherit;
height: 100%;
}
-
+
>.jsx-parser {
- position:absolute;
- z-index:0;
+ position: absolute;
+ z-index: 0;
}
.formattedTextBox-cont {
@@ -72,9 +75,11 @@
box-sizing: border-box;
position:absolute;
z-index: -1;
+
.marqueeView {
overflow: hidden;
}
+
top: 0;
left: 0;
width: 100%;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 996032b1d..b1aba10bf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -28,6 +28,7 @@ import { MarqueeView } from "./MarqueeView";
import React = require("react");
import v5 = require("uuid/v5");
+
export const panZoomSchema = createSchema({
panX: "number",
panY: "number",
@@ -49,9 +50,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get nativeHeight() { return this.Document.nativeHeight || 0; }
public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private panX = () => this.Document.panX || 0;
- private panY = () => this.Document.panY || 0;
- private zoomScaling = () => this.Document.scale || 1;
+ private panX = () => this.props.fitToBox ? this.props.fitToBox[0] : this.Document.panX || 0;
+ private panY = () => this.props.fitToBox ? this.props.fitToBox[1] : this.Document.panY || 0;
+ private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox[2] : this.Document.scale || 1;
private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections
private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections
private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform());
@@ -239,8 +240,17 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
var scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY));
- this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
- this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+ // this.props.Document.panX = this.isAnnotationOverlay ? newPanX : panX;
+ // this.props.Document.panY = this.isAnnotationOverlay ? newPanY : panY;
+ this.props.Document.panX = panX;
+ if (this.props.Document.scrollY) {
+ this.props.Document.scrollY = panY;
+ this.props.Document.panY = panY;
+ }
+ else {
+
+ this.props.Document.panY = panY;
+ }
}
@action
@@ -326,13 +336,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
- let datadoc = BoolCast(this.props.Document.isTemplate) || this.props.DataDoc === this.props.Document ? undefined : this.props.DataDoc;
- if (Cast(layoutDoc.layout, Doc) instanceof Doc) { // if this document is using a template to render, then set the dataDoc for the template to be this document
- datadoc = layoutDoc;
- }
+ getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
+ let resolvedDataDoc = this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
+ let layoutDoc = Doc.expandTemplateLayout(childDocLayout, resolvedDataDoc);
return {
- DataDoc: datadoc,
+ DataDoc: resolvedDataDoc !== layoutDoc && resolvedDataDoc ? resolvedDataDoc : undefined,
Document: layoutDoc,
addDocument: this.props.addDocument,
removeDocument: this.props.removeDocument,
@@ -353,6 +361,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
getScale: this.getScale
};
}
+ getDocumentViewProps(layoutDoc: Doc): DocumentViewProps {
+ return {
+ DataDoc: this.props.DataDoc,
+ Document: this.props.Document,
+ addDocument: this.props.addDocument,
+ removeDocument: this.props.removeDocument,
+ moveDocument: this.props.moveDocument,
+ ScreenToLocalTransform: this.getTransform,
+ renderDepth: this.props.renderDepth + 1,
+ selectOnLoad: layoutDoc[Id] === this._selectOnLoaded,
+ PanelWidth: layoutDoc[WidthSym],
+ PanelHeight: layoutDoc[HeightSym],
+ ContentScaling: returnOne,
+ ContainingCollectionView: this.props.CollectionView,
+ focus: this.focusDocument,
+ parentActive: this.props.active,
+ whenActiveChanged: this.props.whenActiveChanged,
+ bringToFront: this.bringToFront,
+ addDocTab: this.props.addDocTab,
+ zoomToScale: this.zoomToScale,
+ getScale: this.getScale
+ };
+ }
@computed.struct
get views() {
@@ -363,7 +394,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
if (Math.round(page) === Math.round(curPage) || page === -1) {
let minim = BoolCast(doc.isMinimized, false);
if (minim === undefined || !minim) {
- prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getDocumentViewProps(doc)} />);
+ prev.push(<CollectionFreeFormDocumentView key={doc[Id]} {...this.getChildDocumentViewProps(doc)} />);
}
}
return prev;
@@ -407,12 +438,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
private childViews = () => [
- <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} DataDoc={this.props.DataDoc} />,
+ <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />,
...this.views
]
render() {
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
const easing = () => this.props.Document.panTransformType === "Ease";
+
if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
return (
<div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index cfbf4aab8..fec105516 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -19,6 +19,7 @@ $serif: "Crimson Text", serif;
// misc values
$border-radius: 0.3em;
//
+$search-thumnail-size: 175;
// dragged items
$contextMenu-zindex: 1000; // context menu shows up over everything
@@ -33,4 +34,5 @@ $MAX_ROW_HEIGHT: 44px;
COLLECTION_BORDER_WIDTH: $COLLECTION_BORDER_WIDTH;
MINIMIZED_ICON_SIZE: $MINIMIZED_ICON_SIZE;
MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
+ SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
} \ No newline at end of file
diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts
index 9788d31f7..d95cec9d8 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -4,6 +4,7 @@ interface IGlobalScss {
COLLECTION_BORDER_WIDTH: string;
MINIMIZED_ICON_SIZE: string;
MAX_ROW_HEIGHT: string;
+ SEARCH_THUMBNAIL_SIZE: string;
}
declare const globalCssVariables: IGlobalScss;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4ac43ef4d..9edde896f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -71,6 +71,7 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
+ fitToBox?: number[];
addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
@@ -84,7 +85,7 @@ export interface DocumentViewProps {
parentActive: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc) => void;
- addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void;
+ addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void;
collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void;
zoomToScale: (scale: number) => void;
getScale: () => number;
@@ -254,7 +255,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating");
}
let isMinimized: boolean | undefined;
- expandedDocs.map(d => Doc.GetProto(d)).map(maximizedDoc => {
+ expandedDocs.map(maximizedDoc => {
let iconAnimating = Cast(maximizedDoc.isIconAnimating, List);
if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) {
if (isMinimized === undefined) {
@@ -321,7 +322,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (dataDocs) {
expandedDocs.forEach(maxDoc =>
(!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) &&
- this.props.addDocTab(getDispDoc(maxDoc), getDispDoc(maxDoc), maxLocation)));
+ this.props.addDocTab(getDispDoc(maxDoc), undefined, maxLocation)));
}
} else {
let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2);
@@ -333,7 +334,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let linkedPages = [linkedDocs.length ? NumCast(linkedDocs[0].anchor1Page, undefined) : NumCast(linkedDocs[0].anchor2Page, undefined),
linkedDocs.length ? NumCast(linkedDocs[0].anchor2Page, undefined) : NumCast(linkedDocs[0].anchor1Page, undefined)];
let maxLocation = StrCast(linkedDoc.maximizeLocation, "inTab");
- DocumentManager.Instance.jumpToDocument(linkedDoc, ctrlKey, false, document => this.props.addDocTab(document, document, maxLocation), linkedPages[altKey ? 1 : 0]);
+ DocumentManager.Instance.jumpToDocument(linkedDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedPages[altKey ? 1 : 0]);
// else if (linkedToDocs.length || linkedFromDocs.length) {
// let linkedFwdDocs = [
@@ -351,7 +352,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// if (!linkedFwdDocs.some(l => l instanceof Promise)) {
// let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
// let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
- // DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
+ // DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
// }
}
}
@@ -393,7 +394,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); };
- fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); };
+ fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.dataDoc, "onRight"); };
makeBtnClicked = (): void => {
let doc = Doc.GetProto(this.props.Document);
doc.isButton = !BoolCast(doc.isButton, false);
@@ -406,8 +407,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
doc.nativeWidth = doc.nativeHeight = undefined;
}
}
- fullScreenClicked = (): void => {
- CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.dataDoc);
+ public fullScreenClicked = (): void => {
+ CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this);
SelectionManager.DeselectAll();
}
@@ -503,7 +504,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({
description: "Find aliases", event: async () => {
const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
- this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), Docs.SchemaDocument(["title"], aliases, {}), "onRight"); // bcz: dataDoc?
+ this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), undefined, "onRight"); // bcz: dataDoc?
}, icon: "search"
});
cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 55f61ddff..3f5a2e744 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -18,6 +18,8 @@ import { ImageBox } from "./ImageBox";
import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
import { Id } from "../../../new_fields/FieldSymbols";
+import { BoolCast, Cast } from "../../../new_fields/Types";
+import { DarpaDatasetDoc } from "../../northstar/model/idea/idea";
//
@@ -28,6 +30,8 @@ import { Id } from "../../../new_fields/FieldSymbols";
export interface FieldViewProps {
fieldKey: string;
fieldExt: string;
+ leaveNativeSize?: boolean;
+ fitToBox?: number[];
ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
Document: Doc;
DataDoc?: Doc;
@@ -36,7 +40,7 @@ export interface FieldViewProps {
renderDepth: number;
selectOnLoad: boolean;
addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean;
- addDocTab: (document: Doc, dataDoc: Doc, where: string) => void;
+ addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
removeDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
@@ -72,7 +76,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <FormattedTextBox {...this.props} />;
}
else if (field instanceof ImageField) {
- return <ImageBox {...this.props} />;
+ return <ImageBox {...this.props} leaveNativeSize={true} />;
}
else if (field instanceof IconField) {
return <IconBox {...this.props} />;
@@ -86,7 +90,7 @@ export class FieldView extends React.Component<FieldViewProps> {
return <p>{field.date.toLocaleString()}</p>;
}
else if (field instanceof Doc) {
- return <p><b>{field.title + " + " + field[Id]}</b></p>;
+ return <p><b>{field.title + " : id= " + field[Id]}</b></p>;
// let returnHundred = () => 100;
// return (
// <DocumentContentsView Document={field}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 2a45aeb43..07cd43ce3 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -211,7 +211,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
- field => this._editorView && !this._applyingChange &&
+ field => this._editorView && !this._applyingChange && this.props.Document[this.props.fieldKey] instanceof RichTextField &&
this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
);
this.setupEditor(config, this.dataDoc, this.props.fieldKey);
@@ -247,6 +247,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.selectOnLoad) {
if (!this.props.isOverlay) this.props.select(false);
else this._editorView!.focus();
+ this.tryUpdateHeight();
}
}
@@ -280,7 +281,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
if (this._linkClicked) {
DocServer.GetRefField(this._linkClicked).then(f => {
- (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, false, document => this.props.addDocTab(document, document, "inTab"));
+ (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, false, document => this.props.addDocTab(document, undefined, "inTab"));
});
e.stopPropagation();
e.preventDefault();
@@ -382,6 +383,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
}
+ this.tryUpdateHeight();
+ }
+
+ @action
+ tryUpdateHeight() {
if (this.props.isOverlay && this.props.Document.autoHeight) {
let xf = this._ref.current!.getBoundingClientRect();
let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5c00d9d44..06bf65f73 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -20,6 +20,8 @@ import { positionSchema } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
+import { RouteStore } from '../../../server/RouteStore';
+var requestImageSize = require('../../util/request-image-size');
var path = require('path');
@@ -27,7 +29,7 @@ library.add(faImage);
export const pageSchema = createSchema({
- curPage: "number"
+ curPage: "number",
});
type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
@@ -41,7 +43,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
private _downX: number = 0;
private _downY: number = 0;
private _lastTap: number = 0;
- @observable private _photoIndex: number = 0;
@observable private _isOpen: boolean = false;
private dropDisposer?: DragManager.DragDropDisposer;
@@ -115,20 +116,21 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
e.stopPropagation();
}
+ @action
lightbox = (images: string[]) => {
if (this._isOpen) {
return (<Lightbox
- mainSrc={images[this._photoIndex]}
- nextSrc={images[(this._photoIndex + 1) % images.length]}
- prevSrc={images[(this._photoIndex + images.length - 1) % images.length]}
+ mainSrc={images[this.Document.curPage || 0]}
+ nextSrc={images[((this.Document.curPage || 0) + 1) % images.length]}
+ prevSrc={images[((this.Document.curPage || 0) + images.length - 1) % images.length]}
onCloseRequest={action(() =>
this._isOpen = false
)}
onMovePrevRequest={action(() =>
- this._photoIndex = (this._photoIndex + images.length - 1) % images.length
+ this.Document.curPage = ((this.Document.curPage || 0) + images.length - 1) % images.length
)}
onMoveNextRequest={action(() =>
- this._photoIndex = (this._photoIndex + 1) % images.length
+ this.Document.curPage = ((this.Document.curPage || 0) + 1) % images.length
)}
/>);
}
@@ -160,7 +162,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
@action
onDotDown(index: number) {
- this._photoIndex = index;
this.Document.curPage = index;
}
@@ -170,7 +171,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let left = (nativeWidth - paths.length * dist) / 2;
return paths.map((p, i) =>
<div className="imageBox-placer" key={i} >
- <div className="imageBox-dot" style={{ background: (i === this._photoIndex ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
+ <div className="imageBox-dot" style={{ background: (i === this.Document.curPage ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
</div>
);
}
@@ -199,6 +200,22 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
}
}
_curSuffix = "_m";
+
+ resize(srcpath: string, layoutdoc: Doc) {
+ requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath)
+ .then((size: any) => {
+ let aspect = size.height / size.width;
+ if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) {
+ setTimeout(action(() => {
+ layoutdoc.height = layoutdoc[WidthSym]() * aspect;
+ layoutdoc.nativeHeight = size.height;
+ layoutdoc.nativeWidth = size.width;
+ }), 0);
+ }
+ })
+ .catch((err: any) => console.log(err));
+ }
+
render() {
// let transform = this.props.ScreenToLocalTransform().inverse();
let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
@@ -212,6 +229,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
// this._curSuffix = "";
// if (w > 20) {
+ Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
let alts = DocListCast(this.extensionDoc.Alternates);
let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
let field = this.dataDoc[this.props.fieldKey];
@@ -225,8 +243,10 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let rotation = NumCast(this.dataDoc.rotation, 0);
let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
- Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
- let srcpath = paths[Math.min(paths.length, this._photoIndex)];
+ let srcpath = paths[Math.min(paths.length, this.Document.curPage || 0)];
+
+ if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
+
return (
<div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
onPointerDown={this.onPointerDown}
@@ -235,7 +255,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
- // style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }}
+ // style={{ objectFit: (this.Document.curPage === 0 ? undefined : "contain") }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} />
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 4beb70284..0e798d291 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -99,8 +99,16 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let rows: JSX.Element[] = [];
let i = 0;
+ const self = this;
for (let key of Object.keys(ids).sort()) {
- rows.push(<KeyValuePair doc={realDoc} ref={(el) => { if (el) this.rows.push(el); }} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />);
+ rows.push(<KeyValuePair doc={realDoc} ref={(function () {
+ let oldEl: KeyValuePair | undefined;
+ return (el: KeyValuePair) => {
+ if (oldEl) self.rows.splice(self.rows.indexOf(oldEl), 1);
+ oldEl = el;
+ if (el) self.rows.push(el);
+ };
+ })()} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />);
}
return rows;
}
@@ -145,7 +153,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
}
getTemplate = async () => {
- let parent = Docs.FreeformDocument([], { width: 800, height: 800, title: "Template" });
+ let parent = Docs.StackingDocument([], { width: 800, height: 800, title: "Template" });
+ parent.singleColumn = false;
+ parent.columnWidth = 50;
for (let row of this.rows.filter(row => row.isChecked)) {
await this.createTemplateField(parent, row);
row.uncheck();
@@ -167,8 +177,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let template = Doc.MakeAlias(target);
template.proto = parent;
template.title = metaKey;
- template.nativeWidth = 300;
- template.nativeHeight = 300;
+ template.nativeWidth = 0;
+ template.nativeHeight = 0;
template.embed = true;
template.isTemplate = true;
template.templates = new List<string>([Templates.TitleBar(metaKey)]);
@@ -189,7 +199,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") {
return Docs.TextDocument(options);
} else if (field instanceof List) {
- return Docs.FreeformDocument([], options);
+ return Docs.StackingDocument([], options);
} else if (field instanceof ImageField) {
return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
}
@@ -218,4 +228,4 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
{dividerDragger}
</div>);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index 732e6de84..4dee6741f 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -7,7 +7,7 @@ import { undoBatch } from "../../util/UndoManager";
import './LinkMenu.scss';
import React = require("react");
import { Doc } from '../../../new_fields/Doc';
-import { StrCast, Cast } from '../../../new_fields/Types';
+import { StrCast, Cast, BoolCast, FieldValue } from '../../../new_fields/Types';
import { observable, action } from 'mobx';
import { LinkManager } from '../../util/LinkManager';
import { DragLinkAsDocument } from '../../util/DragManager';
@@ -32,10 +32,15 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
@undoBatch
onFollowLink = async (e: React.PointerEvent): Promise<void> => {
e.stopPropagation();
- if (DocumentManager.Instance.getDocumentView(this.props.destinationDoc)) {
- DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey);
+ let jumpToDoc = this.props.destinationDoc;
+ let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
+ if (pdfDoc) {
+ jumpToDoc = pdfDoc;
+ }
+ if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
} else {
- CollectionDockingView.Instance.AddRightSplit(this.props.destinationDoc, undefined);
+ CollectionDockingView.Instance.AddRightSplit(jumpToDoc, undefined);
}
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 8fb18e8fc..83dedb71d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -217,10 +217,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@action
onScroll = (e: React.UIEvent<HTMLDivElement>) => {
+
if (e.currentTarget) {
this._scrollY = e.currentTarget.scrollTop;
let ccv = this.props.ContainingCollectionView;
if (ccv) {
+ ccv.props.Document.panTransformType = "None";
ccv.props.Document.scrollY = this._scrollY;
}
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
new file mode 100644
index 000000000..73fca229b
--- /dev/null
+++ b/src/client/views/pdf/Annotation.tsx
@@ -0,0 +1,144 @@
+import React = require("react");
+import { Doc, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { AnnotationTypes, Viewer, scale } from "./PDFViewer";
+import { observer } from "mobx-react";
+import { observable, IReactionDisposer, reaction, action } from "mobx";
+import { BoolCast, NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import PDFMenu from "./PDFMenu";
+import { DocumentManager } from "../../util/DocumentManager";
+
+interface IAnnotationProps {
+ anno: Doc;
+ index: number;
+ parent: Viewer;
+}
+
+export default class Annotation extends React.Component<IAnnotationProps> {
+ render() {
+ let annotationDocs = DocListCast(this.props.anno.annotations);
+ let res = annotationDocs.map(a => {
+ let type = NumCast(a.type);
+ switch (type) {
+ // case AnnotationTypes.Pin:
+ // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ case AnnotationTypes.Region:
+ return <RegionAnnotation parent={this.props.parent} document={a} index={this.props.index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
+ default:
+ return <div></div>;
+ }
+ });
+ return res;
+ }
+}
+
+interface IRegionAnnotationProps {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ index: number;
+ parent: Viewer;
+ document: Doc;
+}
+
+@observer
+class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
+ @observable private _backgroundColor: string = "red";
+
+ private _reactionDisposer?: IReactionDisposer;
+ private _scrollDisposer?: IReactionDisposer;
+ private _mainCont: React.RefObject<HTMLDivElement>;
+
+ constructor(props: IRegionAnnotationProps) {
+ super(props);
+
+ this._mainCont = React.createRef();
+ }
+
+ componentDidMount() {
+ this._reactionDisposer = reaction(
+ () => BoolCast(this.props.document.delete),
+ () => {
+ if (BoolCast(this.props.document.delete)) {
+ if (this._mainCont.current) {
+ this._mainCont.current.style.display = "none";
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
+
+ this._scrollDisposer = reaction(
+ () => this.props.parent.Index,
+ () => {
+ if (this.props.parent.Index === this.props.index) {
+ this.props.parent.scrollTo(this.props.y - 50);
+ }
+ }
+ );
+ }
+
+ componentWillUnmount() {
+ this._reactionDisposer && this._reactionDisposer();
+ this._scrollDisposer && this._scrollDisposer();
+ }
+
+ deleteAnnotation = () => {
+ let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group && annotation.indexOf(group) !== -1) {
+ let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
+ this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
+ }
+
+ if (group) {
+ let groupAnnotations = DocListCast(group.annotations);
+ groupAnnotations.forEach(anno => anno.delete = true);
+ }
+
+ PDFMenu.Instance.fadeOut(true);
+ }
+
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+ if (e.button === 0) {
+ let targetDoc = Cast(this.props.document.target, Doc, null);
+ if (targetDoc) {
+ DocumentManager.Instance.jumpToDocument(targetDoc, true);
+ }
+ }
+ if (e.button === 2) {
+ PDFMenu.Instance.Status = "annotation";
+ PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ PDFMenu.Instance.Pinned = false;
+ PDFMenu.Instance.AddTag = this.addTag.bind(this);
+ PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ }
+ }
+
+ addTag = (key: string, value: string): boolean => {
+ let group = FieldValue(Cast(this.props.document.group, Doc));
+ if (group) {
+ let valNum = parseInt(value);
+ group[key] = isNaN(valNum) ? value : valNum;
+ return true;
+ }
+ return false;
+ }
+
+ render() {
+ return (
+ <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
+ style={{
+ top: this.props.y * scale,
+ left: this.props.x * scale,
+ width: this.props.width * scale,
+ height: this.props.height * scale,
+ pointerEvents: "all",
+ backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
+ }}></div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 11d3d7e27..0fde764d0 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -41,6 +41,9 @@
}
}
}
+.pdfViewer-viewerCont {
+ width:100%;
+}
.page-cont {
.textLayer {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bafc3cbae..bb148e738 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,6 +23,7 @@ import { UndoManager } from "../../util/UndoManager";
import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting";
import { ScriptField } from "../../../new_fields/ScriptField";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import Annotation from "./Annotation";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
export const scale = 2;
@@ -48,7 +49,7 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
render() {
return (
- <div ref={this._mainDiv}>
+ <div className="pdfViewer-viewerCont" ref={this._mainDiv}>
{!this._pdf ? (null) :
<Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />}
</div>
@@ -69,7 +70,7 @@ interface IViewerProps {
* Handles rendering and virtualization of the pdf
*/
@observer
-class Viewer extends React.Component<IViewerProps> {
+export class Viewer extends React.Component<IViewerProps> {
// _visibleElements is the array of JSX elements that gets rendered
@observable.shallow private _visibleElements: JSX.Element[] = [];
// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
@@ -192,10 +193,14 @@ class Viewer extends React.Component<IViewerProps> {
let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
this._isPage = Array<string>(this.props.pdf.numPages);
// this._textContent = Array<Pdfjs.TextContent>(this.props.pdf.numPages);
+ const proms: Pdfjs.PDFPromise<any>[] = [];
for (let i = 0; i < this.props.pdf.numPages; i++) {
- await this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
- // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
- let x = page.getViewport(scale);
+ proms.push(this.props.pdf.getPage(i + 1).then(page => runInAction(() => {
+ pageSizes[i] = {
+ width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale,
+ height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale
+ };
+ // let x = page.getViewport(scale);
// page.getTextContent().then((text: Pdfjs.TextContent) => {
// // let tc = new Pdfjs.TextContentItem()
// // let tc = {str: }
@@ -204,9 +209,10 @@ class Viewer extends React.Component<IViewerProps> {
// // tcStr += t.str;
// // })
// });
- pageSizes[i] = { width: x.width, height: x.height };
- }));
+ // pageSizes[i] = { width: x.width, height: x.height };
+ })));
}
+ await Promise.all(proms);
runInAction(() =>
Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages);
@@ -226,7 +232,8 @@ class Viewer extends React.Component<IViewerProps> {
let annoDocs: Doc[] = [];
let mainAnnoDoc = Docs.CreateInstance(new Doc(), "", {});
- mainAnnoDoc.page = Math.round(Math.random());
+ mainAnnoDoc.title = "Annotation on " + StrCast(this.props.parent.Document.title);
+ mainAnnoDoc.pdfDoc = this.props.parent.Document;
this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => {
for (let anno of value) {
let annoDoc = new Doc();
@@ -244,6 +251,7 @@ class Viewer extends React.Component<IViewerProps> {
}
});
+ mainAnnoDoc.y = Math.max((NumCast(annoDocs[0].y) * scale) - 100, 0);
mainAnnoDoc.annotations = new List<Doc>(annoDocs);
if (sourceDoc) {
DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title));
@@ -417,20 +425,8 @@ class Viewer extends React.Component<IViewerProps> {
}
}
- renderAnnotation = (anno: Doc, index: number): JSX.Element[] => {
- let annotationDocs = DocListCast(anno.annotations);
- let res = annotationDocs.map(a => {
- let type = NumCast(a.type);
- switch (type) {
- // case AnnotationTypes.Pin:
- // return <PinAnnotation parent={this} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- case AnnotationTypes.Region:
- return <RegionAnnotation parent={this} document={a} index={index} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />;
- default:
- return <div></div>;
- }
- });
- return res;
+ renderAnnotation = (anno: Doc, index: number): JSX.Element => {
+ return <Annotation anno={anno} index={index} parent={this} />;
}
@action
@@ -676,116 +672,6 @@ export enum AnnotationTypes {
Region
}
-interface IAnnotationProps {
- x: number;
- y: number;
- width: number;
- height: number;
- index: number;
- parent: Viewer;
- document: Doc;
-}
-
-@observer
-class RegionAnnotation extends React.Component<IAnnotationProps> {
- @observable private _backgroundColor: string = "red";
-
- private _reactionDisposer?: IReactionDisposer;
- private _scrollDisposer?: IReactionDisposer;
- private _mainCont: React.RefObject<HTMLDivElement>;
-
- constructor(props: IAnnotationProps) {
- super(props);
-
- this._mainCont = React.createRef();
- }
-
- componentDidMount() {
- this._reactionDisposer = reaction(
- () => BoolCast(this.props.document.delete),
- () => {
- if (BoolCast(this.props.document.delete)) {
- if (this._mainCont.current) {
- this._mainCont.current.style.display = "none";
- }
- }
- },
- { fireImmediately: true }
- );
-
- this._scrollDisposer = reaction(
- () => this.props.parent.Index,
- () => {
- if (this.props.parent.Index === this.props.index) {
- this.props.parent.scrollTo(this.props.y - 50);
- }
- }
- );
- }
-
- componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- this._scrollDisposer && this._scrollDisposer();
- }
-
- deleteAnnotation = () => {
- let annotation = DocListCast(this.props.parent.props.parent.Document.annotations);
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group && annotation.indexOf(group) !== -1) {
- let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc)));
- this.props.parent.props.parent.Document.annotations = new List<Doc>(newAnnotations);
- }
-
- if (group) {
- let groupAnnotations = DocListCast(group.annotations);
- groupAnnotations.forEach(anno => anno.delete = true);
- }
-
- PDFMenu.Instance.fadeOut(true);
- }
-
- @action
- onPointerDown = (e: React.PointerEvent) => {
- if (e.button === 0) {
- let targetDoc = Cast(this.props.document.target, Doc, null);
- if (targetDoc) {
- DocumentManager.Instance.jumpToDocument(targetDoc, true);
- }
- }
- if (e.button === 2) {
- PDFMenu.Instance.Status = "annotation";
- PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- PDFMenu.Instance.Pinned = false;
- PDFMenu.Instance.AddTag = this.addTag.bind(this);
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
- }
- }
-
- addTag = (key: string, value: string): boolean => {
- let group = FieldValue(Cast(this.props.document.group, Doc));
- if (group) {
- let valNum = parseInt(value);
- group[key] = isNaN(valNum) ? value : valNum;
- return true;
- }
- return false;
- }
-
- render() {
- return (
- <div className="pdfViewer-annotationBox" onPointerDown={this.onPointerDown} ref={this._mainCont}
- style={{
- top: this.props.y * scale,
- left: this.props.x * scale,
- width: this.props.width * scale,
- height: this.props.height * scale,
- pointerEvents: "all",
- backgroundColor: this.props.parent.Index === this.props.index ? "goldenrod" : StrCast(this.props.document.color)
- }}></div>
- );
- }
-}
-
class SimpleLinkService {
externalLinkTarget: any = null;
externalLinkRel: any = null;
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx
index 01aa96705..57e36be43 100644
--- a/src/client/views/pdf/Page.tsx
+++ b/src/client/views/pdf/Page.tsx
@@ -31,7 +31,7 @@ interface IPageProps {
makePin: (x: number, y: number, page: number) => void;
sendAnnotations: (annotations: HTMLDivElement[], page: number) => void;
createAnnotation: (div: HTMLDivElement, page: number) => void;
- makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc;
+ makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string, linkTo: boolean) => Doc;
getScrollFromPage: (page: number) => number;
}
@@ -137,7 +137,7 @@ export default class Page extends React.Component<IPageProps> {
@action
highlight = (targetDoc?: Doc, color: string = "red") => {
// creates annotation documents for current highlights
- let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color);
+ let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color, false);
let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc));
if (targetAnnotations === undefined) {
Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]);
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 4afc0210f..d63c0b066 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -13,12 +13,11 @@ import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons';
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
-
library.add(faArrowUp);
library.add(fileSolid);
library.add(faLocationArrow);
+library.add(fileRegular as any);
library.add(faSearch);
-library.add(fileRegular);
interface PresentationElementProps {
mainDocument: Doc;
diff --git a/src/client/views/search/FilterBox.scss b/src/client/views/search/FilterBox.scss
index 1eb8963d7..1b73916c9 100644
--- a/src/client/views/search/FilterBox.scss
+++ b/src/client/views/search/FilterBox.scss
@@ -105,4 +105,60 @@
border-top-style: solid;
padding-top: 10px;
}
+}
+
+.active-filters {
+ display: flex;
+ flex-direction: row-reverse;
+ justify-content: flex-end;
+ width: 100%;
+ margin-right: 30px;
+ position: relative;
+
+ .active-icon {
+ max-width: 40px;
+ flex: initial;
+
+ &.icon{
+ width: 40px;
+ text-align: center;
+ margin-bottom: 5px;
+ position: absolute;
+ }
+
+ &.container {
+ display: flex;
+ flex-direction: column;
+ width: 40px;
+ }
+
+ &.description {
+ text-align: center;
+ top: 40px;
+ position: absolute;
+ width: 40px;
+ font-size: 9px;
+ opacity: 0;
+ -webkit-transition: all 0.2s ease-in-out;
+ -moz-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ }
+
+ &.icon:hover + .description {
+ opacity: 1;
+ }
+ }
+
+ .col-icon {
+ height: 35px;
+ margin-left: 5px;
+ width: 35px;
+ background-color: black;
+ color: white;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
} \ No newline at end of file
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index cc1feeaf7..6053db595 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -2,8 +2,8 @@ import * as React from 'react';
import { observer } from 'mobx-react';
import { observable, action } from 'mobx';
import "./SearchBox.scss";
-import { faTimes } from '@fortawesome/free-solid-svg-icons';
-import { library} from '@fortawesome/fontawesome-svg-core';
+import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-svg-icons';
+import { library } from '@fortawesome/fontawesome-svg-core';
import { Doc } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { DocTypes } from '../../documents/Documents';
@@ -19,8 +19,11 @@ import { NaviconButton } from './NaviconButton';
import * as $ from 'jquery';
import "./FilterBox.scss";
import { SearchBox } from './SearchBox';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
library.add(faTimes);
+library.add(faCheckCircle);
+library.add(faObjectGroup);
export enum Keys {
TITLE = "title",
@@ -35,12 +38,16 @@ export class FilterBox extends React.Component {
public _allIcons: string[] = [DocTypes.AUDIO, DocTypes.COL, DocTypes.HIST, DocTypes.IMG, DocTypes.LINK, DocTypes.PDF, DocTypes.TEXT, DocTypes.VID, DocTypes.WEB];
//if true, any keywords can be used. if false, all keywords are required.
+ //this also serves as an indicator if the word status filter is applied
@observable private _basicWordStatus: boolean = true;
@observable private _filterOpen: boolean = false;
+ //if icons = all icons, then no icon filter is applied
@observable private _icons: string[] = this._allIcons;
+ //if all of these are true, no key filter is applied
@observable private _titleFieldStatus: boolean = true;
@observable private _authorFieldStatus: boolean = true;
@observable private _dataFieldStatus: boolean = true;
+ //this also serves as an indicator if the collection status filter is applied
@observable private _collectionStatus = false;
@observable private _collectionSelfStatus = true;
@observable private _collectionParentStatus = true;
@@ -57,7 +64,7 @@ export class FilterBox extends React.Component {
componentDidMount = () => {
document.addEventListener("pointerdown", (e) => {
- if (e.timeStamp !== this._pointerTime) {
+ if (!e.defaultPrevented && e.timeStamp !== this._pointerTime) {
SearchBox.Instance.closeSearch();
}
});
@@ -65,9 +72,9 @@ export class FilterBox extends React.Component {
setupAccordion() {
$('document').ready(function () {
- var acc = document.getElementsByClassName('filter-header');
-
- for (var i = 0; i < acc.length; i++) {
+ const acc = document.getElementsByClassName('filter-header');
+ // tslint:disable-next-line: prefer-for-of
+ for (let i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function (this: HTMLElement) {
this.classList.toggle("active");
@@ -96,6 +103,7 @@ export class FilterBox extends React.Component {
$('document').ready(function () {
var acc = document.getElementsByClassName('filter-header');
+ // tslint:disable-next-line: prefer-for-of
for (var i = 0; i < acc.length; i++) {
let classList = acc[i].classList;
if (classList.contains("active")) {
@@ -240,14 +248,48 @@ export class FilterBox extends React.Component {
filterDocsByType(docs: Doc[]) {
let finalDocs: Doc[] = [];
docs.forEach(doc => {
- let layoutresult = Cast(doc.type, "string", "");
- if (this._icons.includes(layoutresult)) {
+ let layoutresult = Cast(doc.type, "string");
+ if (!layoutresult || this._icons.includes(layoutresult)) {
finalDocs.push(doc);
}
});
return finalDocs;
}
+ getABCicon() {
+ return (
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35">
+ <path d="M25.4 47.9c-1.3 1.3-1.9 2.8-1.9 4.8 0 3.8 2.3 6.1 6.1 6.1 5.1 0 8-3.3 9-6.2 0.2-0.7 0.4-1.4 0.4-2.1v-6.1c-0.1 0-0.1 0-0.2 0C32.2 44.5 27.7 45.6 25.4 47.9z" />
+ <path d="M64.5 28.6c-2.2 0-4.1 1.5-4.7 3.8l0 0.2c-0.1 0.3-0.1 0.7-0.1 1.1v3.3c0 0.4 0.1 0.8 0.2 1.1 0.6 2.2 2.4 3.6 4.6 3.6 3.2 0 5.2-2.6 5.2-6.7C69.5 31.8 68 28.6 64.5 28.6z" />
+ <path d="M43.9 0C19.7 0 0 19.7 0 43.9s19.7 43.9 43.9 43.9 43.9-19.6 43.9-43.9S68.1 0 43.9 0zM40.1 65.5l-0.5-4c-3 3.1-7.4 4.9-12.1 4.9 -6.8 0-13.6-4.4-13.6-12.8 0-4 1.3-7.4 4-10 4.1-4.1 11.1-6.2 20.8-6.3 0-5.5-2.9-8.4-8.3-8.4 -3.6 0-7.4 1.1-10.2 2.9l-1.1 0.7 -2.4-6.9 0.7-0.4c3.7-2.4 8.9-3.8 14.1-3.8 10.9 0 16.7 6.2 16.7 17.9V54.6c0 4.1 0.2 7.2 0.7 9.7L49 65.5H40.1zM65.5 67.5c1.8 0 3-0.5 4-0.9l0.5-0.2 0.8 3.4 -0.3 0.2c-1 0.5-3 1.1-5.5 1.1 -5.8 0-9.7-4-9.7-9.9 0-6.1 4.3-10.3 10.4-10.3 2.1 0 4 0.5 4.9 1l0.3 0.2 -1 3.5 -0.5-0.3c-0.7-0.4-1.8-0.8-3.7-0.8 -3.7 0-6.1 2.6-6.1 6.6C59.5 64.8 61.9 67.5 65.5 67.5zM65 45.3c-2.5 0-4.5-0.9-5.9-2.7l-0.1 2.3h-3.8l0-0.5c0.1-1.2 0.2-3.1 0.2-4.8V16.7h4.3v10.8c1.4-1.6 3.5-2.5 6-2.5 2.2 0 4.1 0.8 5.5 2.3 1.8 1.8 2.8 4.5 2.8 7.7C73.8 42.1 69.3 45.3 65 45.3z" />
+ </svg>
+ );
+ }
+
+ getTypeIcon() {
+ return (
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35">
+ <path d="M43.9 0C19.7 0 0 19.7 0 43.9s19.7 43.9 43.9 43.9 43.9-19.6 43.9-43.9S68.1 0 43.9 0zM43.9 12.2c4.1 0 7.5 3.4 7.5 7.5 0 4.1-3.4 7.5-7.5 7.5 -4.1 0-7.5-3.4-7.5-7.5C36.4 15.5 39.7 12.2 43.9 12.2zM11.9 50.4l7.5-13 7.5 13H11.9zM47.6 75.7h-7.5l-3.7-6.5 3.8-6.5h7.5l3.8 6.5L47.6 75.7zM70.7 70.7c-0.2 0.2-0.4 0.3-0.7 0.3s-0.5-0.1-0.7-0.3l-25.4-25.4 -25.4 25.4c-0.2 0.2-0.4 0.3-0.7 0.3s-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1 0-1.4l25.4-25.4 -25.4-25.4c-0.4-0.4-0.4-1 0-1.4s1-0.4 1.4 0l25.4 25.4 25.4-25.4c0.4-0.4 1-0.4 1.4 0s0.4 1 0 1.4l-25.4 25.4 25.4 25.4C71.1 69.7 71.1 70.3 70.7 70.7zM61.4 51.4v-15h15v15H61.4z" />
+ </svg>
+ );
+ }
+
+ getKeyIcon() {
+ return (
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.8 87.8" height="35">
+ <path d="M38.5 32.4c0 3.4-2.7 6.1-6.1 6.1 -3.4 0-6.1-2.7-6.1-6.1 0-3.4 2.8-6.1 6.1-6.1C35.8 26.3 38.5 29 38.5 32.4zM87.8 43.9c0 24.2-19.6 43.9-43.9 43.9S0 68.1 0 43.9C0 19.7 19.7 0 43.9 0S87.8 19.7 87.8 43.9zM66.8 60.3L50.2 43.7c-0.5-0.5-0.6-1.2-0.4-1.8 2.4-5.6 1.1-12.1-3.2-16.5 -5.9-5.8-15.4-5.8-21.2 0l0 0c-4.3 4.3-5.6 10.8-3.2 16.5 3.2 7.6 12 11.2 19.7 8 0.6-0.3 1.4-0.1 1.8 0.4l3.1 3.1h3.9c1.2 0 2.2 1 2.2 2.2v3.6h3.6c1.2 0 2.2 1 2.2 2.2v4l1.6 1.6h6.5V60.3z" />
+ </svg>
+ );
+ }
+
+ getColIcon() {
+ return (
+ <div className="col-icon">
+ <FontAwesomeIcon icon={faObjectGroup} size="lg" />
+ </div>
+ );
+ }
+
@action.bound
openFilter = () => {
this._filterOpen = !this._filterOpen;
@@ -259,7 +301,7 @@ export class FilterBox extends React.Component {
@action.bound
handleWordQueryChange = () => { this._basicWordStatus = !this._basicWordStatus; }
- @action
+ @action.bound
getBasicWordStatus() { return this._basicWordStatus; }
@action.bound
@@ -315,6 +357,31 @@ export class FilterBox extends React.Component {
getAuthorStatus() { return this._authorFieldStatus; }
getDataStatus() { return this._dataFieldStatus; }
+ getActiveFilters() {
+ console.log(this._authorFieldStatus, this._titleFieldStatus, this._dataFieldStatus);
+ return (
+ <div className="active-filters">
+ {!this._basicWordStatus ? <div className="active-icon container">
+ <div className="active-icon icon">{this.getABCicon()}</div>
+ <div className="active-icon description">Required Words Applied</div>
+ </div> : undefined}
+ {!(this._icons.length === 9) ? <div className="active-icon container">
+ <div className="active-icon icon">{this.getTypeIcon()}</div>
+ <div className="active-icon description">Type Filters Applied</div>
+ </div> : undefined}
+ {!(this._authorFieldStatus && this._dataFieldStatus && this._titleFieldStatus) ?
+ <div className="active-icon container">
+ <div className="active-icon icon">{this.getKeyIcon()}</div>
+ <div className="active-icon description">Field Filters Applied</div>
+ </div> : undefined}
+ {this._collectionStatus ? <div className="active-icon container">
+ <div className="active-icon icon">{this.getColIcon()}</div>
+ <div className="active-icon description">Collection Filters Active</div>
+ </div> : undefined}
+ </div>
+ )
+ }
+
// Useful queries:
// Delegates of a document: {!join from=id to=proto_i}id:{protoId}
// Documents in a collection: {!join from=data_l to=id}id:{collectionProtoId} //id of collections prototype
@@ -323,6 +390,7 @@ export class FilterBox extends React.Component {
<div>
<div style={{ display: "flex", flexDirection: "row-reverse" }}>
<SearchBox />
+ {this.getActiveFilters()}
</div>
{this._filterOpen ? (
<div className="filter-form" onPointerDown={this.stopProp} id="filter-form" style={this._filterOpen ? { display: "flex" } : { display: "none" }}>
@@ -376,7 +444,8 @@ export class FilterBox extends React.Component {
<button className="reset-filter" onClick={this.resetFilters}>Reset Filters</button>
</div>
</div>
- ) : undefined}
+ ) :
+ undefined}
</div>
);
}
diff --git a/src/client/views/search/Pager.tsx b/src/client/views/search/Pager.tsx
index 1c62773b1..9bcfb1676 100644
--- a/src/client/views/search/Pager.tsx
+++ b/src/client/views/search/Pager.tsx
@@ -57,17 +57,17 @@ export class Pager extends React.Component {
return (
<div className="search-pager">
<div className="search-arrows">
- <div className="arrow"
- onPointerDown={this.onLeftClick} style={SearchBox.Instance._pageNum === 0 ? { opacity: .2 } : this._leftHover ? { opacity: 1 } : { opacity: .7 }}
- onMouseEnter={this.mouseInLeft} onMouseOut={this.mouseOutLeft}>
+ <div className = "arrow"
+ onPointerDown = {this.onLeftClick} style = {SearchBox.Instance._pageNum === 0 ? {opacity: .2} : this._leftHover ? {opacity: 1} : {opacity: .7}}
+ onMouseEnter = {this.mouseInLeft} onMouseLeave = {this.mouseOutLeft}>
<FontAwesomeIcon className="fontawesome-icon" icon={faArrowCircleLeft} />
</div>
<div className="pager-title">
page {SearchBox.Instance._pageNum + 1} of {SearchBox.Instance._maxNum}
</div>
- <div className="arrow"
- onPointerDown={this.onRightClick} style={SearchBox.Instance._pageNum === SearchBox.Instance._maxNum - 1 ? { opacity: .2 } : this._rightHover ? { opacity: 1 } : { opacity: .7 }}
- onMouseEnter={this.mouseInRight} onMouseOut={this.mouseOutRight}>
+ <div className = "arrow"
+ onPointerDown = {this.onRightClick} style = {SearchBox.Instance._pageNum === SearchBox.Instance._maxNum-1 ? {opacity: .2} : this._rightHover ? {opacity: 1} : {opacity: .7}}
+ onMouseEnter = {this.mouseInRight} onMouseLeave = {this.mouseOutRight}>
<FontAwesomeIcon className="fontawesome-icon" icon={faArrowCircleRight} />
</div>
</div>
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 2a27bbe62..7541b328a 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -46,9 +46,10 @@
display: flex;
flex-direction: column;
margin-right: 72px;
- height: 560px;
- overflow: hidden;
- overflow-y: auto;
+ // height: 560px;
+ height: 100%;
+ // overflow: hidden;
+ // overflow-y: auto;
.no-result {
width: 500px;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index f53a4e34f..dc21e4a3c 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -15,7 +15,6 @@ import { Id } from '../../../new_fields/FieldSymbols';
import { SearchUtil } from '../../util/SearchUtil';
import { RouteStore } from '../../../server/RouteStore';
import { FilterBox } from './FilterBox';
-import { Pager } from './Pager';
@observer
export class SearchBox extends React.Component {
@@ -169,7 +168,6 @@ export class SearchBox extends React.Component {
@action.bound
closeSearch = () => {
- console.log("closing search");
FilterBox.Instance.closeFilter();
this.closeResults();
}
@@ -198,9 +196,9 @@ export class SearchBox extends React.Component {
) :
this._openNoResults ? (<div className="no-result">No Search Results</div>) : null}
</div>
- {/* <div style={this._results.length !== 0 ? { display: "flex" } : { display: "none" }}>
- <Pager />
- </div> */}
+ <div style={this._results.length !== 0 ? { display: "flex" } : { display: "none" }}>
+ {/* <Pager /> */}
+ </div>
</div>
);
}
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 946680f0e..fa4c9cb38 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -5,6 +5,7 @@
flex-direction: row-reverse;
justify-content: flex-end;
height: 70px;
+ z-index: 0;
.search-item {
width: 500px;
@@ -13,6 +14,7 @@
border-bottom-style: solid;
padding: 10px;
height: 70px;
+ z-index: 0;
display: inline-block;
.main-search-info {
@@ -23,16 +25,17 @@
.search-title {
text-transform: uppercase;
text-align: left;
- width: 80%;
+ width: 100%;
font-weight: bold;
}
.search-info {
display: flex;
justify-content: flex-end;
- width: 40%;
.link-container.item {
+ margin-left: auto;
+ margin-right: auto;
height: 26px;
width: 26px;
border-radius: 13px;
@@ -41,7 +44,6 @@
display: flex;
justify-content: center;
align-items: center;
- right: 15px;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
@@ -84,26 +86,42 @@
.link-container.item:hover .link-extended {
opacity: 1;
}
+
+ .icon-icons {
+ width:50px
+ }
+ .icon-live {
+ width:175px;
+ }
- .icon {
-
+ .icon-icons, .icon-live {
+ height:50px;
+ margin:auto;
+ overflow: hidden;
.search-type {
- width: 25PX;
- height: 25PX;
- display: flex;
+ display: inline-block;
+ width:100%;
+ position: absolute;
justify-content: center;
align-items: center;
position: relative;
margin-right: 5px;
}
+ .pdfBox-cont {
+ overflow: hidden;
+
+ img {
+ width:100% !important;
+ height:auto !important;
+ }
+ }
.search-type:hover+.search-label {
opacity: 1;
}
.search-label {
font-size: 10;
- padding: 5px;
position: relative;
right: 0px;
text-transform: capitalize;
@@ -114,6 +132,18 @@
transition: opacity 0.2s ease-in-out;
}
}
+
+ .icon-live:hover {
+ height:175px;
+ .pdfBox-cont {
+ img {
+ width:100% !important;
+ }
+ }
+ }
+ }
+ .search-info:hover {
+ width:60%;
}
}
}
@@ -143,7 +173,15 @@
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
- // height: 100%
}
+}
+.search-overview:hover {
+ z-index: 1;
+}
+.collection {
+ display:flex;
+}
+.collection-item {
+ width:35px;
} \ No newline at end of file
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 5160d9469..f4ea3ee09 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -1,24 +1,26 @@
import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia } from '@fortawesome/free-solid-svg-icons';
+import { faCaretUp, faChartBar, faFilePdf, faFilm, faGlobeAsia, faImage, faLink, faMusic, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Cast, NumCast } from "../../../new_fields/Types";
-import { observable, runInAction, computed, action } from "mobx";
-import { listSpec } from "../../../new_fields/Schema";
-import { Doc } from "../../../new_fields/Doc";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction, returnFalse, returnOne } from "../../../Utils";
+import { DocTypes } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { SetupDrag } from "../../util/DragManager";
+import { LinkManager } from "../../util/LinkManager";
import { SearchUtil } from "../../util/SearchUtil";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { observer } from "mobx-react";
-import "./SearchItem.scss";
+import { Transform } from "../../util/Transform";
+import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss";
import { CollectionViewType } from "../collections/CollectionBaseView";
-import { DocTypes } from "../../documents/Documents";
-import { FilterBox } from "./FilterBox";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
import { DocumentView } from "../nodes/DocumentView";
-import "./SelectorContextMenu.scss";
import { SearchBox } from "./SearchBox";
+import "./SearchItem.scss";
+import "./SelectorContextMenu.scss";
export interface SearchItemProps {
doc: Doc;
@@ -69,18 +71,63 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
CollectionDockingView.Instance.AddRightSplit(col, undefined);
};
}
-
render() {
return (
- < div className="parents">
+ <div className="parents">
<p className="contexts">Contexts:</p>
- {this._docs.map(doc => <div className="collection"><a className="title" onClick={this.getOnClick(doc)}>{doc.col.title}</a></div>)}
- {this._otherDocs.map(doc => <div className="collection"><a className="title" onClick={this.getOnClick(doc)}>{doc.col.title}</a></div>)}
+ {[...this._docs, ...this._otherDocs].map(doc => {
+ let item = React.createRef<HTMLDivElement>();
+ return <div className="collection" key={doc.col[Id] + doc.target[Id]} ref={item}>
+ <div className="collection-item" onPointerDown={
+ SetupDrag(item, () => doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
+ <FontAwesomeIcon icon={faStickyNote} />
+ </div>
+ <a onClick={this.getOnClick(doc)}>{doc.col.title}</a>
+ </div>;
+ })}
</div>
);
}
}
+export interface LinkMenuProps {
+ doc1: Doc;
+ doc2: Doc;
+}
+
+@observer
+export class LinkContextMenu extends React.Component<LinkMenuProps> {
+
+ highlightDoc = (doc: Doc) => {
+ return () => {
+ doc.libraryBrush = true;
+ };
+ }
+
+ unHighlightDoc = (doc: Doc) => {
+ return () => {
+ doc.libraryBrush = false;
+ };
+ }
+
+ getOnClick(col: Doc) {
+ return () => {
+ CollectionDockingView.Instance.AddRightSplit(col, undefined);
+ };
+ }
+
+ render() {
+ return (
+ <div className="parents">
+ <p className="contexts">Anchors:</p>
+ <div className = "collection"><a onMouseEnter = {this.highlightDoc(this.props.doc1)} onMouseLeave = {this.unHighlightDoc(this.props.doc1)} onClick = {this.getOnClick(this.props.doc1)}>Doc 1: {this.props.doc2.title}</a></div>
+ <div><a onMouseEnter = {this.highlightDoc(this.props.doc2)} onMouseLeave = {this.unHighlightDoc(this.props.doc2)} onClick = {this.getOnClick(this.props.doc2)}>Doc 2: {this.props.doc1.title}</a></div>
+ </div>
+ )
+ }
+
+}
+
@observer
export class SearchItem extends React.Component<SearchItemProps> {
@@ -89,10 +136,56 @@ export class SearchItem extends React.Component<SearchItemProps> {
onClick = () => {
DocumentManager.Instance.jumpToDocument(this.props.doc, false);
}
+ @observable _useIcons = true;
+ @observable _displayDim = 50;
@computed
public get DocumentIcon() {
- let layoutresult = Cast(this.props.doc.type, "string", "");
+ let layoutresult = StrCast(this.props.doc.type);
+ if (!this._useIcons) {
+ let renderDoc = this.props.doc;
+ let box: number[] = [];
+ if (layoutresult.indexOf(DocTypes.COL) !== -1) {
+ renderDoc = Doc.MakeDelegate(renderDoc);
+ let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => {
+ var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+ let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+ 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.MIN_VALUE, b: Number.MIN_VALUE });
+ box = [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim];
+ }
+ let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
+ let returnYDimension = () => this._displayDim;
+ let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+ return <div
+ onPointerDown={action(() => { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })}
+ onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
+ onPointerLeave={action(() => this._displayDim = 50)} >
+ <DocumentView
+ fitToBox={box}
+ Document={renderDoc}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={Transform.Identity}
+ addDocTab={returnFalse}
+ renderDepth={1}
+ PanelWidth={returnXDimension}
+ PanelHeight={returnYDimension}
+ focus={emptyFunction}
+ selectOnLoad={false}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ ContainingCollectionView={undefined}
+ ContentScaling={scale}
+ />
+ </div>;
+ }
let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf :
layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage :
@@ -104,7 +197,9 @@ export class SearchItem extends React.Component<SearchItemProps> {
layoutresult.indexOf(DocTypes.HIST) !== -1 ? faChartBar :
layoutresult.indexOf(DocTypes.WEB) !== -1 ? faGlobeAsia :
faCaretUp;
- return <FontAwesomeIcon icon={button} size="2x" />;
+ return <div onPointerDown={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
+ <FontAwesomeIcon icon={button} size="2x" />
+ </div>;
}
collectionRef = React.createRef<HTMLDivElement>();
@@ -119,7 +214,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
}
@computed
- get linkCount() { return Cast(this.props.doc.linkedToDocs, listSpec(Doc), []).length + Cast(this.props.doc.linkedFromDocs, listSpec(Doc), []).length; }
+ get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; }
@computed
get linkString(): string {
@@ -130,45 +225,65 @@ export class SearchItem extends React.Component<SearchItemProps> {
return num.toString() + " links";
}
- pointerDown = (e: React.PointerEvent) => { SearchBox.Instance.openSearch(e); };
+ @action
+ pointerDown = (e: React.PointerEvent) => SearchBox.Instance.openSearch(e)
highlightDoc = (e: React.PointerEvent) => {
- let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc);
- docViews.forEach(element => {
- element.props.Document.libraryBrush = true;
- });
+ if (this.props.doc.type === DocTypes.LINK) {
+ if (this.props.doc.anchor1 && this.props.doc.anchor2) {
+
+ let doc1 = Cast(this.props.doc.anchor1, Doc, new Doc());
+ let doc2 = Cast(this.props.doc.anchor2, Doc, new Doc());
+ doc1.libraryBrush = true;
+ doc2.libraryBrush = true;
+ }
+ } else {
+ let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc);
+ docViews.forEach(element => {
+ element.props.Document.libraryBrush = true;
+ });
+ }
}
unHighlightDoc = (e: React.PointerEvent) => {
- let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc);
- docViews.forEach(element => {
- element.props.Document.libraryBrush = false;
- });
+ if (this.props.doc.type === DocTypes.LINK) {
+ if (this.props.doc.anchor1 && this.props.doc.anchor2) {
+
+ let doc1 = Cast(this.props.doc.anchor1, Doc, new Doc());
+ let doc2 = Cast(this.props.doc.anchor2, Doc, new Doc());
+ doc1.libraryBrush = false;
+ doc2.libraryBrush = false;
+ }
+ } else {
+ let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc);
+ docViews.forEach(element => {
+ element.props.Document.libraryBrush = false;
+ });
+ }
}
render() {
return (
<div className="search-overview" onPointerDown={this.pointerDown}>
- <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} ref={this.collectionRef} id="result" onClick={this.onClick} onPointerDown={() => {
- this.pointerDown;
- SetupDrag(this.collectionRef, this.startDocDrag);
- }} >
+ <div className="search-item" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} ref={this.collectionRef} id="result"
+ onClick={this.onClick} onPointerDown={this.pointerDown} >
<div className="main-search-info">
<div className="search-title" id="result" >{this.props.doc.title}</div>
- <div className="search-info">
+ <div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
+ <div className={`icon-${this._useIcons ? "icons" : "live"}`}>
+ <div className="search-type" >{this.DocumentIcon}</div>
+ <div className="search-label">{this.props.doc.type}</div>
+ </div>
<div className="link-container item">
<div className="link-count">{this.linkCount}</div>
<div className="link-extended">{this.linkString}</div>
</div>
- <div className="icon">
- <div className="search-type" >{this.DocumentIcon}</div>
- <div className="search-label">{this.props.doc.type}</div>
- </div>
</div>
</div>
</div>
<div className="searchBox-instances">
- <SelectorContextMenu {...this.props} />
+ {this.props.doc.type === DocTypes.LINK ? <LinkContextMenu doc1 = {Cast(this.props.doc.anchor1, Doc, new Doc())} doc2 = {Cast(this.props.doc.anchor2, Doc, new Doc())}/> :
+ <SelectorContextMenu {...this.props} /> }
</div>
</div>
);
diff --git a/src/client/views/search/SelectorContextMenu.scss b/src/client/views/search/SelectorContextMenu.scss
index 49f77b9bf..48cacc608 100644
--- a/src/client/views/search/SelectorContextMenu.scss
+++ b/src/client/views/search/SelectorContextMenu.scss
@@ -3,6 +3,7 @@
.parents {
background: $lighter-alt-accent;
padding: 10px;
+ // width: 300px;
.contexts {
text-transform: uppercase;
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx
index 178578c5c..a30104089 100644
--- a/src/client/views/search/ToggleBar.tsx
+++ b/src/client/views/search/ToggleBar.tsx
@@ -59,6 +59,7 @@ export class ToggleBar extends React.Component<ToggleBarProps>{
this._forwardTimeline.play();
this._forwardTimeline.reverse();
this.props.handleChange();
+ console.log(this.props.getStatus())
}
@action.bound