aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/AntimodeMenu.tsx10
-rw-r--r--src/client/views/ContextMenu.tsx9
-rw-r--r--src/client/views/ContextMenuItem.tsx21
-rw-r--r--src/client/views/DictationOverlay.tsx2
-rw-r--r--src/client/views/DocComponent.tsx11
-rw-r--r--src/client/views/DocumentButtonBar.tsx109
-rw-r--r--src/client/views/DocumentDecorations.scss87
-rw-r--r--src/client/views/DocumentDecorations.tsx199
-rw-r--r--src/client/views/EditableView.tsx111
-rw-r--r--src/client/views/GestureOverlay.tsx75
-rw-r--r--src/client/views/GlobalKeyHandler.ts132
-rw-r--r--src/client/views/InkStrokeProperties.ts268
-rw-r--r--src/client/views/InkingStroke.tsx98
-rw-r--r--src/client/views/MainView.scss103
-rw-r--r--src/client/views/MainView.tsx705
-rw-r--r--src/client/views/MainViewModal.tsx2
-rw-r--r--src/client/views/OverlayView.tsx13
-rw-r--r--src/client/views/Palette.tsx3
-rw-r--r--src/client/views/PreviewCursor.tsx48
-rw-r--r--src/client/views/PropertiesButtons.scss7
-rw-r--r--src/client/views/PropertiesButtons.tsx487
-rw-r--r--src/client/views/PropertiesDocContextSelector.scss0
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx58
-rw-r--r--src/client/views/PropertiesView.scss (renamed from src/client/views/collections/collectionFreeForm/PropertiesView.scss)71
-rw-r--r--src/client/views/PropertiesView.tsx (renamed from src/client/views/collections/collectionFreeForm/PropertiesView.tsx)330
-rw-r--r--src/client/views/ScriptingRepl.tsx17
-rw-r--r--src/client/views/TemplateMenu.tsx9
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx2
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx5
-rw-r--r--src/client/views/collections/CollectionDockingView.scss39
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx1049
-rw-r--r--src/client/views/collections/CollectionDockingViewMenu.scss (renamed from src/client/views/collections/ParentDocumentSelector.scss)41
-rw-r--r--src/client/views/collections/CollectionDockingViewMenu.tsx48
-rw-r--r--src/client/views/collections/CollectionLinearView.scss2
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx14
-rw-r--r--src/client/views/collections/CollectionMapView.tsx19
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx2
-rw-r--r--src/client/views/collections/CollectionMenu.scss69
-rw-r--r--src/client/views/collections/CollectionMenu.tsx351
-rw-r--r--src/client/views/collections/CollectionPileView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx877
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx236
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx50
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss108
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx120
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx39
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx24
-rw-r--r--src/client/views/collections/CollectionSubView.tsx95
-rw-r--r--src/client/views/collections/CollectionTreeView.scss9
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx234
-rw-r--r--src/client/views/collections/CollectionView.tsx207
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx138
-rw-r--r--src/client/views/collections/SchemaTable.tsx216
-rw-r--r--src/client/views/collections/TabDocView.scss22
-rw-r--r--src/client/views/collections/TabDocView.tsx383
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx215
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.scss68
-rw-r--r--src/client/views/collections/collectionFreeForm/FormatShapePane.tsx558
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx18
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx72
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.scss11
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx6
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx1
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx1
-rw-r--r--src/client/views/globalCssVariables.scss8
-rw-r--r--src/client/views/globalCssVariables.scss.d.ts2
-rw-r--r--src/client/views/linking/LinkEditor.tsx35
-rw-r--r--src/client/views/linking/LinkMenu.scss2
-rw-r--r--src/client/views/linking/LinkMenu.tsx21
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx3
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx30
-rw-r--r--src/client/views/nodes/AudioBox.tsx24
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx56
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx10
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx54
-rw-r--r--src/client/views/nodes/DocumentView.scss7
-rw-r--r--src/client/views/nodes/DocumentView.tsx221
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx2
-rw-r--r--src/client/views/nodes/FieldView.tsx6
-rw-r--r--src/client/views/nodes/FilterBox.scss54
-rw-r--r--src/client/views/nodes/FilterBox.tsx215
-rw-r--r--src/client/views/nodes/FontIconBox.scss24
-rw-r--r--src/client/views/nodes/FontIconBox.tsx11
-rw-r--r--src/client/views/nodes/ImageBox.tsx101
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx7
-rw-r--r--src/client/views/nodes/LabelBox.scss1
-rw-r--r--src/client/views/nodes/LinkAnchorBox.scss5
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx10
-rw-r--r--src/client/views/nodes/LinkBox.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx24
-rw-r--r--src/client/views/nodes/MenuIconBox.scss49
-rw-r--r--src/client/views/nodes/MenuIconBox.tsx33
-rw-r--r--src/client/views/nodes/PDFBox.tsx25
-rw-r--r--src/client/views/nodes/PresBox.scss8
-rw-r--r--src/client/views/nodes/PresBox.tsx509
-rw-r--r--src/client/views/nodes/RadialMenuItem.tsx8
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx8
-rw-r--r--src/client/views/nodes/SliderBox.tsx5
-rw-r--r--src/client/views/nodes/VideoBox.tsx40
-rw-r--r--src/client/views/nodes/WebBox.scss68
-rw-r--r--src/client/views/nodes/WebBox.tsx108
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx12
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx219
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx40
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts8
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx107
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts14
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx6
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts4
-rw-r--r--src/client/views/pdf/Annotation.tsx18
-rw-r--r--src/client/views/pdf/PDFMenu.tsx18
-rw-r--r--src/client/views/pdf/PDFViewer.tsx95
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx16
-rw-r--r--src/client/views/search/IconBar.tsx25
-rw-r--r--src/client/views/search/IconButton.tsx140
-rw-r--r--src/client/views/search/SearchBox.scss398
-rw-r--r--src/client/views/search/SearchBox.tsx1052
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx24
123 files changed, 5195 insertions, 7150 deletions
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 68ccefcb5..5acb3e4c8 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,12 +1,14 @@
import React = require("react");
-import { observable, action } from "mobx";
+import { observable, action, runInAction } from "mobx";
import "./AntimodeMenu.scss";
+export interface AntimodeMenuProps {
+}
/**
* This is an abstract class that serves as the base for a PDF-style or Marquee-style
* menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example.
*/
-export default abstract class AntimodeMenu extends React.Component {
+export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Component<T, {}> {
protected _offsetY: number = 0;
protected _offsetX: number = 0;
protected _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -18,7 +20,7 @@ export default abstract class AntimodeMenu extends React.Component {
@observable protected _transitionProperty: string = "opacity";
@observable protected _transitionDuration: string = "0.5s";
@observable protected _transitionDelay: string = "";
- @observable protected _canFade: boolean = true;
+ @observable protected _canFade: boolean = false;
@observable public Pinned: boolean = false;
@@ -84,7 +86,7 @@ export default abstract class AntimodeMenu extends React.Component {
@action
protected togglePin = (e: React.MouseEvent) => {
- this.Pinned = !this.Pinned;
+ runInAction(() => this.Pinned = !this.Pinned);
}
protected dragStart = (e: React.PointerEvent) => {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 07f7b8e6d..952100cb0 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -3,14 +3,9 @@ import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextM
import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import "./ContextMenu.scss";
-import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons';
import Measure from "react-measure";
-library.add(faSearch);
-library.add(faCircle);
-
@observer
export class ContextMenu extends React.Component {
static Instance: ContextMenu;
@@ -100,7 +95,7 @@ export class ContextMenu extends React.Component {
}
@action
moveAfter(item: ContextMenuProps, after: ContextMenuProps) {
- if (this.findByDescription(after.description)) {
+ if (after && this.findByDescription(after.description)) {
const curInd = this._items.findIndex((i) => i.description === item.description);
this._items.splice(curInd, 1);
const afterInd = this._items.findIndex((i) => i.description === after.description);
@@ -220,7 +215,7 @@ export class ContextMenu extends React.Component {
@computed get menuItems() {
if (!this._searchString) {
- return this._items.map(item => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} />);
+ return this._items.map(item => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} pageX={this.pageX} />);
}
return this.filteredViews;
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 7e233ec04..eddecb7a7 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -1,12 +1,10 @@
import React = require("react");
import { observable, action } from "mobx";
import { observer } from "mobx-react";
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { UndoManager } from "../util/UndoManager";
-
-library.add(faAngleRight);
+import { NumberLiteralType } from "typescript";
export interface OriginalMenuProps {
description: string;
@@ -31,6 +29,7 @@ export type ContextMenuProps = OriginalMenuProps | SubmenuProps;
export class ContextMenuItem extends React.Component<ContextMenuProps & { selected?: boolean }> {
@observable private _items: Array<ContextMenuProps> = [];
@observable private overItem = false;
+ @observable private subRef = React.createRef<HTMLDivElement>();
constructor(props: ContextMenuProps | SubmenuProps) {
super(props);
@@ -54,6 +53,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
currentTimeout?: any;
static readonly timeout = 300;
_overPosY = 0;
+ _overPosX = 0;
onPointerEnter = (e: React.MouseEvent) => {
if (this.currentTimeout) {
clearTimeout(this.currentTimeout);
@@ -63,6 +63,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
return;
}
this._overPosY = e.clientY;
+ this._overPosX = e.clientX;
this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout);
}
@@ -78,6 +79,9 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
}
render() {
+
+
+
if ("event" in this.props) {
return (
<div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}>
@@ -94,8 +98,13 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
} else if ("subitems" in this.props) {
const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center";
const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : "";
+
+ // here
const submenu = !this.overItem ? (null) :
- <div className="contextMenu-subMenu-cont" style={{ marginLeft: "90%", left: "0px", marginTop }}>
+ <div className="contextMenu-subMenu-cont"
+ style={{
+ marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? "90%" : "20%", marginTop
+ }}>
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
if (!("noexpand" in this.props)) {
@@ -115,7 +124,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
<div className="contextMenu-description" onMouseEnter={this.onPointerEnter}
style={{ alignItems: "center" }} >
{this.props.description}
- <FontAwesomeIcon icon={faAngleRight} size="lg" style={{ position: "absolute", right: "10px" }} />
+ <FontAwesomeIcon icon={"angle-right"} size="lg" style={{ position: "absolute", right: "10px" }} />
</div>
{submenu}
</div>
diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx
index 9ed14509f..f4f96da8a 100644
--- a/src/client/views/DictationOverlay.tsx
+++ b/src/client/views/DictationOverlay.tsx
@@ -4,7 +4,7 @@ import "normalize.css";
import * as React from 'react';
import { DictationManager } from '../util/DictationManager';
import "./Main.scss";
-import MainViewModal from './MainViewModal';
+import { MainViewModal } from './MainViewModal';
@observer
export class DictationOverlay extends React.Component {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8c8bb6fde..aa888b34a 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -58,7 +58,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor:
lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result;
- active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);// && !Doc.SelectedTool(); // bcz: inking state shouldn't affect static tools
+ active = (outsideReaction?: boolean) => !this.props.Document._isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);// && !Doc.SelectedTool(); // bcz: inking state shouldn't affect static tools
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
}
return Component;
@@ -131,11 +131,10 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
toRemove.forEach(doc => {
Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
- doc.deleted = true;
});
return true;
}
@@ -179,8 +178,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
added.map(doc => doc.context = this.props.Document);
(targetDataDoc[this.annotationKey] as List<Doc>).push(...added);
targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
- const lastModified = "lastModified";
- targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
}
}
}
@@ -188,9 +185,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
}
whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
- active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document.isBackground) &&
+ active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document._) &&
(this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0 || BoolCast((this.layoutDoc as any).forceActive)) ? true : false)
- annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.Document.isBackground && this.props.active()) ||
+ annotationsActive = (outsideReaction?: boolean) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.Document._isBackground && this.props.active()) ||
(this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
}
return Component;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 8748b1880..cf2bd5176 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,45 +1,34 @@
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../fields/Doc";
+import { Doc } from "../../fields/Doc";
import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
import { emptyFunction, setupMoveUpEvents } from "../../Utils";
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
-import { Docs, DocUtils } from '../documents/Documents';
+import { Docs } from '../documents/Documents';
+import { DocumentType } from '../documents/DocumentTypes';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { DragManager } from '../util/DragManager';
-import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView';
-import { ParentDocSelector } from './collections/ParentDocumentSelector';
-import './collections/ParentDocumentSelector.scss';
+import { SelectionManager } from '../util/SelectionManager';
+import { SharingManager } from '../util/SharingManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { TabDocView } from './collections/TabDocView';
import './DocumentButtonBar.scss';
import { MetadataEntryMenu } from './MetadataEntryMenu';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView } from './nodes/DocumentView';
import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
import { TemplateMenu } from "./TemplateMenu";
import { Template, Templates } from "./Templates";
import React = require("react");
-import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { Tooltip } from '@material-ui/core';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faLink);
-library.add(faTag);
-library.add(faTimes);
-library.add(faArrowAltCircleDown);
-library.add(faArrowAltCircleUp);
-library.add(faArrowAltCircleRight);
-library.add(faStopCircle);
-library.add(faCheckCircle);
-library.add(faCloudUploadAlt);
-library.add(faSyncAlt);
-library.add(faShare);
-library.add(faPhotoVideo);
-
const cloud: IconProp = "cloud-upload-alt";
const fetch: IconProp = "sync-alt";
@@ -164,11 +153,11 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
e.preventDefault();
let googleDoc = await Cast(dataDoc.googleDoc, Doc);
if (!googleDoc) {
- const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
+ const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, useCors: false };
googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
dataDoc.googleDoc = googleDoc;
}
- CollectionDockingView.AddRightSplit(googleDoc);
+ CollectionDockingView.AddSplit(googleDoc, "right");
} else if (e.altKey) {
e.preventDefault();
window.open(googleDocUrl);
@@ -195,17 +184,57 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
@computed
get pinButton() {
const targetDoc = this.view0?.props.Document;
- const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+ let isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+ // More than 1 document selected then all must be in presentation for isPinned to be true (then it will unpin all)
+ if (SelectionManager.SelectedDocuments().length > 1) {
+ SelectionManager.SelectedDocuments().forEach((docView: DocumentView) => {
+ if (Doc.isDocPinned(docView.props.Document)) isPinned = true;
+ else isPinned = false;
+ });
+ }
return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div></>}>
<div className="documentButtonBar-linker"
style={{ backgroundColor: isPinned ? "white" : "", color: isPinned ? "black" : "white", border: isPinned ? "black 1px solid " : "" }}
- onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
+ onClick={e => this.props.views().map(view => view && TabDocView.PinDoc(view.props.Document, isPinned))}>
<FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin"
/>
</div></Tooltip>;
}
@computed
+ get shareButton() {
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
+ <div className="documentButtonBar-linker" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="users"
+ />
+ </div></Tooltip >;
+ }
+
+ @computed
+ get annotateButton() {
+ const targetDoc = this.view0?.props.Document;
+ const isAnnotating = targetDoc?.isAnnotating;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${isAnnotating ? "Exit" : "Enter"} annotation mode`}</div></>}>
+ <div className="documentButtonBar-linker" style={{ backgroundColor: isAnnotating ? "white" : "", color: isAnnotating ? "black" : "white", }}
+ onClick={e => targetDoc.isAnnotating = !targetDoc.isAnnotating}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="edit"
+ />
+ </div></Tooltip >;
+ }
+
+ @computed
+ get moreButton() {
+ const targetDoc = this.view0?.props.Document;
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Properties Panel"}</div></>}>
+ <div className="documentButtonBar-linker" style={{ color: "white" }} onClick={action(e =>
+ CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="ellipsis-h"
+ />
+ </div></Tooltip >;
+ }
+
+ @computed
get metadataButton() {
const view0 = this.view0;
return !view0 ? (null) : <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>}>
@@ -218,17 +247,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
</Flyout>
</div></Tooltip>;
}
-
- @computed
- get contextButton() {
- return !this.view0 ? (null) : <ParentDocSelector Document={this.view0.props.Document} addDocTab={(doc, where) => {
- where === "onRight" ? CollectionDockingView.AddRightSplit(doc) :
- this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc) :
- this.view0?.props.addDocTab(doc, "onRight");
- return true;
- }} />;
- }
-
@observable _aliasDown = false;
onAliasButtonDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
@@ -293,12 +311,21 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
<div className="documentButtonBar-button">
{this.pinButton}
</div>
- {/* <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
+ {!Doc.UserDoc()["documentLinksButton-hideEnd"] ? (null) : <div className="documentButtonBar-button">
+ {this.shareButton}
+ </div>}
+ {![DocumentType.VID, DocumentType.WEB].includes(StrCast(this.view0.props.Document.type) as DocumentType) ? (null) : <div className="documentButtonBar-button">
+ {this.annotateButton}
+ </div>}
+ <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
</div>
<div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
{this.considerGoogleDocsPull}
- </div> */}
+ </div>
+ <div className="documentButtonBar-button">
+ {this.moreButton}
+ </div>
</div>;
}
}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 1e8cfdff4..e02408559 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -16,22 +16,12 @@ $linkGap : 3px;
grid-template-columns: 8px 16px 1fr 8px 8px;
pointer-events: none;
- #documentDecorations-centerCont {
+ .documentDecorations-centerCont {
grid-column: 3;
background: none;
}
- .documentDecorations-resizer {
- pointer-events: auto;
- background: $alt-accent;
- opacity: 0.1;
- }
-
- .documentDecorations-resizer:hover {
- opacity: 1;
- }
-
- .documentDecorations-selector {
+ .documentDecorations-levelSelector {
pointer-events: auto;
height: 15px;
width: 15px;
@@ -49,27 +39,43 @@ $linkGap : 3px;
grid-row: 4;
}
- #documentDecorations-topLeftResizer,
- #documentDecorations-leftResizer,
- #documentDecorations-bottomLeftResizer {
+ .documentDecorations-topLeftResizer,
+ .documentDecorations-topRightResizer,
+ .documentDecorations-bottomLeftResizer,
+ .documentDecorations-bottomRightResizer,
+ .documentDecorations-leftResizer,
+ .documentDecorations-topResizer,
+ .documentDecorations-bottomResizer,
+ .documentDecorations-rightResizer {
+ pointer-events: auto;
+ background: $alt-accent;
+ opacity: 0.1;
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ .documentDecorations-topLeftResizer,
+ .documentDecorations-leftResizer,
+ .documentDecorations-bottomLeftResizer {
grid-column: 1
}
- #documentDecorations-topResizer,
- #documentDecorations-bottomResizer {
+ .documentDecorations-topResizer,
+ .documentDecorations-bottomResizer {
grid-column-start: 2;
grid-column-end: 5;
}
- #documentDecorations-bottomRightResizer,
- #documentDecorations-topRightResizer,
- #documentDecorations-rightResizer {
+ .documentDecorations-bottomRightResizer,
+ .documentDecorations-topRightResizer,
+ .documentDecorations-rightResizer {
grid-column-start: 5;
grid-column-end: 7;
}
- #documentDecorations-rotation,
- #documentDecorations-borderRadius {
+ .documentDecorations-rotation,
+ .documentDecorations-borderRadius {
grid-column: 5;
grid-row: 4;
border-radius: 100%;
@@ -79,6 +85,7 @@ $linkGap : 3px;
top: 12;
position: relative;
pointer-events: all;
+ cursor: grab;
.borderRadiusTooltip {
width: 10px;
@@ -86,69 +93,69 @@ $linkGap : 3px;
position: absolute;
}
}
- #documentDecorations-rotation {
+ .documentDecorations-rotation {
background: transparent;
right: -15;
}
- #documentDecorations-topLeftResizer,
- #documentDecorations-bottomRightResizer {
+ .documentDecorations-topLeftResizer,
+ .documentDecorations-bottomRightResizer {
cursor: nwse-resize;
background: unset;
opacity: 1;
}
- #documentDecorations-topLeftResizer {
+ .documentDecorations-topLeftResizer {
border-left: 2px solid;
border-top: solid 2px;
}
- #documentDecorations-bottomRightResizer {
+ .documentDecorations-bottomRightResizer {
border-right: 2px solid;
border-bottom: solid 2px;
}
- #documentDecorations-topLeftResizer:hover,
- #documentDecorations-bottomRightResizer:hover {
+ .documentDecorations-topLeftResizer:hover,
+ .documentDecorations-bottomRightResizer:hover {
opacity: 1;
}
- #documentDecorations-bottomRightResizer {
+ .documentDecorations-bottomRightResizer {
grid-row: 4;
}
- #documentDecorations-topRightResizer,
- #documentDecorations-bottomLeftResizer {
+ .documentDecorations-topRightResizer,
+ .documentDecorations-bottomLeftResizer {
cursor: nesw-resize;
background: unset;
opacity: 1;
}
- #documentDecorations-topRightResizer {
+ .documentDecorations-topRightResizer {
border-right: 2px solid;
border-top: 2px solid;
}
- #documentDecorations-bottomLeftResizer {
+ .documentDecorations-bottomLeftResizer {
border-left: 2px solid;
border-bottom: 2px solid;
}
- #documentDecorations-topRightResizer:hover,
- #documentDecorations-bottomLeftResizer:hover {
+ .documentDecorations-topRightResizer:hover,
+ .documentDecorations-bottomLeftResizer:hover {
cursor: nesw-resize;
background: dimGray;
opacity: 1;
}
- #documentDecorations-topResizer,
- #documentDecorations-bottomResizer {
+ .documentDecorations-topResizer,
+ .documentDecorations-bottomResizer {
cursor: ns-resize;
}
- #documentDecorations-leftResizer,
- #documentDecorations-rightResizer {
+ .documentDecorations-leftResizer,
+ .documentDecorations-rightResizer {
cursor: ew-resize;
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 03746a1d2..81c1676b0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,56 +1,30 @@
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction, get } from "mobx";
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DataSym, Field, WidthSym, HeightSym, AclEdit, AclAdmin } from "../../fields/Doc";
+import { AclAdmin, AclEdit, DataSym, Doc, Field, WidthSym, HeightSym } from "../../fields/Doc";
import { Document } from '../../fields/documentSchemas';
+import { HtmlField } from '../../fields/HtmlField';
+import { InkField } from "../../fields/InkField";
import { ScriptField } from '../../fields/ScriptField';
-import { Cast, StrCast, NumCast } from "../../fields/Types";
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils";
-import { DocUtils } from "../documents/Documents";
+import { Cast, NumCast } from "../../fields/Types";
+import { GetEffectiveAcl } from '../../fields/util';
+import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
+import { DocUtils, Docs } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from "../util/DragManager";
import { SelectionManager } from "../util/SelectionManager";
+import { SnappingManager } from '../util/SnappingManager';
import { undoBatch, UndoManager } from "../util/UndoManager";
+import { CollectionDockingView } from './collections/CollectionDockingView';
import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
import e = require('express');
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { SnappingManager } from '../util/SnappingManager';
-import { HtmlField } from '../../fields/HtmlField';
-import { InkField } from "../../fields/InkField";
-import { Tooltip } from '@material-ui/core';
-import { GetEffectiveAcl } from '../../fields/util';
-import { DocumentIcon } from './nodes/DocumentIcon';
-import { render } from 'react-dom';
-import { createLessThan } from 'typescript';
-import FormatShapePane from './collections/collectionFreeForm/FormatShapePane';
-import { PropertiesView } from './collections/collectionFreeForm/PropertiesView';
-
-library.add(faCaretUp);
-library.add(faObjectGroup);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm, faTextHeight);
-library.add(faLink);
-library.add(faTag);
-library.add(faTimes);
-library.add(faArrowAltCircleDown);
-library.add(faArrowAltCircleUp);
-library.add(faStopCircle);
-library.add(faCheckCircle);
-library.add(faCloudUploadAlt);
-library.add(faSyncAlt);
-library.add(faShare);
-library.add(faAngleDoubleLeft);
-library.add(faAngleDoubleRight);
-library.add(faAngleLeft);
-library.add(faAngleRight);
-library.add(faPause);
-library.add(faExternalLinkAlt);
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { InkStrokeProperties } from './InkStrokeProperties';
@observer
export class DocumentDecorations extends React.Component<{}, { value: string }> {
@@ -121,7 +95,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
this._titleControlString = this._accumulatedTitle;
} else if (this._titleControlString.startsWith("#")) {
const selectionTitleFieldKey = this._titleControlString.substring(1);
- selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._accumulatedTitle.startsWith("-"));
+ selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
UndoManager.RunInBatch(() => selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d => {
const value = typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle;
Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true);
@@ -212,8 +186,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (e.button === 0) {
const selectedDocs = SelectionManager.SelectedDocuments();
if (selectedDocs.length) {
- //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0], selectedDocs[0].props.LibraryPath);
- CollectionDockingView.AddRightSplit(Doc.MakeAlias(selectedDocs[0].props.Document), selectedDocs[0].props.LibraryPath);
+ if (e.ctrlKey) {
+ const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+ alias.context = undefined;
+ //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
+ CollectionDockingView.AddSplit(alias, "right");
+ } else if (e.shiftKey) {
+ const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+ alias.context = undefined;
+ alias.x = -alias[WidthSym]() / 2;
+ alias.y = -alias[HeightSym]() / 2;
+ //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
+ } else {
+ CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
+ }
}
}
SelectionManager.DeselectAll();
@@ -310,7 +297,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const newY = Math.sin(angle) * (ink.X - this._centerPoints[index].X) + Math.cos(angle) * (ink.Y - this._centerPoints[index].Y) + this._centerPoints[index].Y;
newPoints.push({ X: newX, Y: newY });
}
- doc.data = new InkField(newPoints);
+ Doc.GetProto(doc).data = new InkField(newPoints);
const xs = newPoints.map(p => p.X);
const ys = newPoints.map(p => p.Y);
const left = Math.min(...xs);
@@ -349,7 +336,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const doc = Document(element.rootDoc);
if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) {
this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height });
- if (FormatShapePane.Instance._lock) {
+ if (InkStrokeProperties.Instance?._lock) {
doc._nativeHeight = doc._height;
doc._nativeWidth = doc._width;
}
@@ -358,13 +345,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
if (e.button === 0) {
- this._resizeHdlId = e.currentTarget.id;
+ this._resizeHdlId = e.currentTarget.className;
const bounds = e.currentTarget.getBoundingClientRect();
this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
this.Interacting = true;
this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
- SelectionManager.SelectedDocuments()[0].props.setupDragLines?.();
+ SelectionManager.SelectedDocuments()[0].props.setupDragLines?.(e.ctrlKey || e.shiftKey);
}
this._snapX = e.pageX;
this._snapY = e.pageY;
@@ -382,7 +369,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
var fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0;
SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => {
const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK && doc._width && doc._height && FormatShapePane.Instance._lock) {
+ if (doc.type === DocumentType.INK && doc._width && doc._height && InkStrokeProperties.Instance?._lock) {
fixedAspect = NumCast(doc._nativeWidth) / NumCast(doc._nativeHeight);
}
}));
@@ -492,10 +479,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = nheight = doc._height || 0;
}
const anno = Cast(doc.annotationOn, Doc, null);
- if (e.ctrlKey && anno) {
+ if (e.ctrlKey && (anno || doc.type === DocumentType.IMG)) {
dW !== 0 && runInAction(() => {
- const dataDoc = anno[DataSym];
- const annoFieldKey = Doc.LayoutFieldKey(anno);
+ const dataDoc = (anno ?? doc)[DataSym];
+ const annoFieldKey = Doc.LayoutFieldKey(anno ?? doc);
const nw = NumCast(dataDoc[annoFieldKey + "-nativeWidth"]);
const nh = NumCast(dataDoc[annoFieldKey + "-nativeHeight"]);
dataDoc[annoFieldKey + "-nativeWidth"] = nw + (dW > 0 ? 10 : -10);
@@ -558,7 +545,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const newY = ((doc.y || 0) - this._inkDocs[index].y) + (i.Y * (doc._height || 0)) / this._inkDocs[index].height;
newPoints.push({ X: newX, Y: newY });
});
- doc.data = new InkField(newPoints);
+ Doc.GetProto(doc).data = new InkField(newPoints);
}
doc._nativeWidth = 0;
@@ -591,19 +578,19 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
}
public static DocumentIcon(layout: string) {
- const 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;
+ const button = layout.indexOf("PDFBox") !== -1 ? "file-pdf" :
+ layout.indexOf("ImageBox") !== -1 ? "image" :
+ layout.indexOf("Formatted") !== -1 ? "sticky-note" :
+ layout.indexOf("Video") !== -1 ? "film" :
+ layout.indexOf("Collection") !== -1 ? "object-group" :
+ "caret-up";
return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
}
render() {
- const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined;
+ const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined;
const bounds = this.Bounds;
const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
- if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+ if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
const canDelete = SelectionManager.SelectedDocuments().some(docView => {
@@ -611,39 +598,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
return collectionAcl === AclAdmin || collectionAcl === AclEdit;
});
const minimal = bounds.r - bounds.x < 100 ? true : false;
- const maximizeIcon = minimal ? (
- <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top">
- <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
- <FontAwesomeIcon size="lg" icon="cog" />
- </div></Tooltip>) : canDelete ? (
- <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top">
- <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
- {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
- <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
- </div></Tooltip>) : (null);
+ const closeIcon = canDelete ? (
+ <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top">
+ <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
+ <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
+ </div></Tooltip>) : (null);
const titleArea = this._edtingTitle ?
+ <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
+ onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
<>
- <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
- onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
- {minimal ? (null) : <div className="publishBox" // title="make document referenceable by its title"
- // onPointerDown={action(e => {
- // if (!seldoc.props.Document.customTitle) {
- // seldoc.props.Document.customTitle = true;
- // StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1));
- // this._accumulatedTitle = StrCast(seldoc.props.Document.title);
- // }
- // DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument);
- // })}
- >
- {/* <FontAwesomeIcon size="lg" color={SelectionManager.SelectedDocuments()[0].props.Document.title === SelectionManager.SelectedDocuments()[0].props.Document[Id] ? "green" : undefined} icon="sticky-note"></FontAwesomeIcon> */}
- </div>}
- </> :
- <>
- {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
- <FontAwesomeIcon size="lg" icon="cog" />
+ {minimal ? (null) : <Tooltip title={<div className="dash-tooltip">Show context menu</div>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
+ <FontAwesomeIcon size="lg" icon="bars" />
</div></Tooltip>}
- <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
+ <div className="documentDecorations-title" style={{ gridColumnEnd: minimal ? 4 : 5, gridColumnStart: minimal ? 2 : 3 }} key="title" onPointerDown={this.onTitleDown} >
<span style={{ width: "100%", display: "inline-block", cursor: "move" }}>{`${this.selectionTitle}`}</span>
</div>
</>;
@@ -659,8 +627,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
if (bounds.y > bounds.b) {
bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight);
}
- var offset = 0;
- let useRotation = seldoc.rootDoc.type === DocumentType.INK;
+ const useRotation = seldoc.rootDoc.type === DocumentType.INK;
return (<div className="documentDecorations" style={{ background: darkScheme }} >
<div className="documentDecorations-background" style={{
@@ -674,45 +641,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
</div>
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
<div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth + offset) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + offset) + "px",
- left: bounds.x - this._resizeBorderWidth / 2 - offset / 2,
- top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight - offset / 2,
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
}}>
- {maximizeIcon}
+ {closeIcon}
{titleArea}
- {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
- <Tooltip title={<><div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div></>} placement="top">
+ {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal ? (null) :
+ <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
<div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
<FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
</div></Tooltip>}
- <Tooltip title={<><div className="dash-tooltip">Open In a New Pane</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
+ <Tooltip title={<div className="dash-tooltip">Open In a New Pane</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
{SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
</div></Tooltip>
- <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-centerCont"></div>
- <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-centerCont"></div>
+ <div className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+ <div className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
{seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
- <Tooltip title={<><div className="dash-tooltip">tap to select containing document</div></>} placement="top">
- <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+ <Tooltip title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
+ <div className="documentDecorations-levelSelector"
onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
- <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+ <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
</div></Tooltip>}
- <div id={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+ <div className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div>
</div >
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ec3e754fb..1b4b9a2be 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -4,10 +4,8 @@ import { observer } from 'mobx-react';
import * as Autosuggest from 'react-autosuggest';
import { ObjectField } from '../../fields/ObjectField';
import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
-import "./EditableView.scss";
import { DragManager } from '../util/DragManager';
-import { ComputedField } from '../../fields/ScriptField';
-import { FieldValue } from '../../fields/Types';
+import "./EditableView.scss";
export interface EditableProps {
/**
@@ -54,10 +52,6 @@ export interface EditableProps {
color?: string | undefined;
onDrop?: any;
placeholder?: string;
- highlight?: boolean;
- positions?: number[];
- search?: string;
- bing?: () => string | undefined;
}
/**
@@ -96,25 +90,30 @@ export class EditableView extends React.Component<EditableProps> {
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key === "Tab") {
- e.stopPropagation();
- this.finalizeEdit(e.currentTarget.value, e.shiftKey, false);
- this.props.OnTab && this.props.OnTab(e.shiftKey);
- } else if (e.key === "Enter") {
- e.stopPropagation();
- if (!e.ctrlKey) {
+ switch (e.key) {
+ case "Tab":
+ e.stopPropagation();
this.finalizeEdit(e.currentTarget.value, e.shiftKey, false);
- } else if (this.props.OnFillDown) {
- this.props.OnFillDown(e.currentTarget.value);
+ this.props.OnTab && this.props.OnTab(e.shiftKey);
+ break;
+ case "Enter":
+ e.stopPropagation();
+ if (!e.ctrlKey) {
+ this.finalizeEdit(e.currentTarget.value, e.shiftKey, false);
+ } else if (this.props.OnFillDown) {
+ this.props.OnFillDown(e.currentTarget.value);
+ this._editing = false;
+ this.props.isEditingCallback?.(false);
+ }
+ break;
+ case "Escape":
+ e.stopPropagation();
this._editing = false;
this.props.isEditingCallback?.(false);
- }
- } else if (e.key === "Escape") {
- e.stopPropagation();
- this._editing = false;
- this.props.isEditingCallback?.(false);
- } else if (e.key === ":") {
- this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
+ break;
+ case ":":
+ this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
+ break;
}
}
@@ -123,11 +122,9 @@ export class EditableView extends React.Component<EditableProps> {
e.nativeEvent.stopPropagation();
if (this._ref.current && this.props.showMenuOnLoad) {
this.props.menuCallback?.(this._ref.current.getBoundingClientRect().x, this._ref.current.getBoundingClientRect().y);
- } else {
- if (!this.props.onClick?.(e)) {
- this._editing = true;
- this.props.isEditingCallback?.(true);
- }
+ } else if (!this.props.onClick?.(e)) {
+ this._editing = true;
+ this.props.isEditingCallback?.(true);
}
e.stopPropagation();
}
@@ -154,7 +151,7 @@ export class EditableView extends React.Component<EditableProps> {
@action
setIsFocused = (value: boolean) => {
const wasFocused = this._editing;
- //this._editing = value;
+ this._editing = value;
return wasFocused !== this._editing;
}
@@ -171,6 +168,7 @@ export class EditableView extends React.Component<EditableProps> {
onPointerDown: this.stopPropagation,
onClick: this.stopPropagation,
onPointerUp: this.stopPropagation,
+ onKeyPress: this.stopPropagation,
value: this.props.autosuggestProps.value,
onChange: this.props.autosuggestProps.onChange
}}
@@ -179,6 +177,7 @@ export class EditableView extends React.Component<EditableProps> {
defaultValue={this.props.GetValue()}
onKeyDown={this.onKeyDown}
autoFocus={true}
+ onKeyPress={e => e.stopPropagation()}
onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)}
onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
style={{ display: this.props.display, fontSize: this.props.fontSize, minWidth: 20 }}
@@ -186,51 +185,25 @@ export class EditableView extends React.Component<EditableProps> {
/>;
}
- returnHighlights() {
- const results = [];
- const contents = this.props.bing!();
-
- if (contents !== undefined) {
- if (this.props.positions !== undefined) {
- const positions = this.props.positions;
- const length = this.props.search!.length;
-
- // contents = String(this.props.contents.valueOf());
-
- results.push(<span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(0, this.props.positions[0]) : this.props.placeholder?.valueOf()}</span>);
- positions.forEach((num, cur) => {
- results.push(<span style={{ backgroundColor: "#FFFF00", fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(num, num + length) : this.props.placeholder?.valueOf()}</span>);
- let end = 0;
- cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1];
- results.push(<span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(num + length, end) : this.props.placeholder?.valueOf()}</span>);
- }
- );
- }
- return results;
- }
- else {
- return <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>;
- }
- }
-
render() {
if (this._editing && this.props.GetValue() !== undefined) {
return this.props.sizeToContent ?
<div style={{ display: "grid", minWidth: 100 }}>
- <div style={{ display: "inline-block", position: "relative", height: 0, width: "100%", overflow: "hidden" }}>{this.props.GetValue()}</div>
+ <div style={{ display: "inline-block", position: "relative", height: 0, width: "100%", overflow: "hidden" }}>
+ {this.props.GetValue()}
+ </div>
{this.renderEditor()}
- </div> : this.renderEditor();
- } else {
- setTimeout(() => this.props.autosuggestProps?.resetValue(), 0);
- return (this.props.contents instanceof ObjectField ? (null) :
- <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
- ref={this._ref}
- style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
- onClick={this.onClick} placeholder={this.props.placeholder}>
- {this.props.highlight === undefined || this.props.positions === undefined || this.props.bing === undefined ? <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
- : this.returnHighlights()}
- </div>
- );
+ </div> :
+ this.renderEditor();
}
+ setTimeout(() => this.props.autosuggestProps?.resetValue(), 0);
+ return this.props.contents instanceof ObjectField ? (null) :
+ <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`} ref={this._ref}
+ style={{ display: this.props.display, minHeight: "17px", whiteSpace: "nowrap", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
+ onClick={this.onClick} placeholder={this.props.placeholder}>
+ <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{
+ this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}
+ </span>
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 76e786257..63711a3cb 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -7,7 +7,7 @@ import { Cast, FieldValue, NumCast } from "../../fields/Types";
import MobileInkOverlay from "../../mobile/MobileInkOverlay";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents, returnEmptyDoclist } from "../../Utils";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { DocUtils } from "../documents/Documents";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
@@ -26,7 +26,7 @@ import * as fitCurve from 'fit-curve';
import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
@observer
-export default class GestureOverlay extends Touchable {
+export class GestureOverlay extends Touchable {
static Instance: GestureOverlay;
@observable public InkShape: string = "";
@@ -613,10 +613,10 @@ export default class GestureOverlay extends Touchable {
}
}
// if we're not drawing in a toolglass try to recognize as gesture
- else {
+ else { // need to decide when to turn gestures back on
const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points));
let actionPerformed = false;
- if (result && result.Score > 0.7) {
+ if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) {
switch (result.Name) {
case GestureUtils.Gestures.Box: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box); break;
case GestureUtils.Gestures.StartBracket: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket); break;
@@ -754,20 +754,64 @@ export default class GestureOverlay extends Touchable {
break;
case "circle":
+ // const centerX = (right + left) / 2;
+ // const centerY = (bottom + top) / 2;
+ // const radius = bottom - centerY;
+
+
+ // for (var y = top; y < bottom; y++) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // this._points.push({ X: x, Y: y });
+ // }
+ // for (var y = bottom; y > top; y--) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // const newX = centerX - (x - centerX);
+ // this._points.push({ X: newX, Y: y });
+ // }
+ // this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ // this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
const centerX = (right + left) / 2;
const centerY = (bottom + top) / 2;
- const radius = bottom - centerY;
- for (var y = top; y < bottom; y++) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- this._points.push({ X: x, Y: y });
- }
- for (var y = bottom; y > top; y--) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- const newX = centerX - (x - centerX);
- this._points.push({ X: newX, Y: y });
+ if ((bottom - centerY) < (right - centerX)) {
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ this._points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ this._points.push({ X: newX, Y: y });
+ }
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
+ } else {
+ //right = bottom
+ //left = top
+ const radius = right - centerX;
+ for (var x = left; x < right; x++) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ this._points.push({ X: x, Y: y });
+ }
+ for (var x = right; x > left; x--) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ const newY = centerY - (y - centerY);
+ this._points.push({ X: x, Y: newY });
+ }
+ this._points.push({ X: left, Y: Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY });
+ this._points.push({ X: left, Y: (Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY) - 1 });
+
+
}
- this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
- this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 });
+
+
+
+
+
+
+
break;
case "line":
@@ -891,6 +935,7 @@ export default class GestureOverlay extends Touchable {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
/>;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index be6aa6be2..83c02b09b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -6,23 +6,26 @@ import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, PromiseValue } from "../../fields/Types";
-import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
+import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
import { DocServer } from "../DocServer";
import { DocumentType } from "../documents/DocumentTypes";
import { DictationManager } from "../util/DictationManager";
import { DragManager } from "../util/DragManager";
+import { GroupManager } from "../util/GroupManager";
import { SelectionManager } from "../util/SelectionManager";
-import SharingManager from "../util/SharingManager";
+import { SharingManager } from "../util/SharingManager";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { CollectionDockingView } from "./collections/CollectionDockingView";
+import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
+import { ContextMenu } from "./ContextMenu";
import { DocumentDecorations } from "./DocumentDecorations";
+import { InkStrokeProperties } from "./InkStrokeProperties";
import { MainView } from "./MainView";
-import { DocumentView } from "./nodes/DocumentView";
import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
-import PDFMenu from "./pdf/PDFMenu";
-import { ContextMenu } from "./ContextMenu";
-import GroupManager from "../util/GroupManager";
-import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
+import { DocumentView } from "./nodes/DocumentView";
+import { PDFMenu } from "./pdf/PDFMenu";
+import { SnappingManager } from "../util/SnappingManager";
+import { SearchBox } from "./search/SearchBox";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -31,7 +34,7 @@ type KeyControlInfo = {
stopPropagation: boolean
};
-export default class KeyManager {
+export class KeyManager {
public static Instance: KeyManager = new KeyManager();
private router = new Map<string, KeyHandler>();
@@ -81,26 +84,18 @@ export default class KeyManager {
// MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
break;
case "escape":
- // if (DocumentLinksButton.StartLink) {
- // if (DocumentLinksButton.StartLink.Document) {
- // action((e: React.PointerEvent<HTMLDivElement>) => {
- // Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
- // });
- // }
- // }
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false);
- const main = MainView.Instance;
Doc.SetSelectedTool(InkTool.None);
var doDeselect = true;
- if (main.isPointerDown) {
+ if (SnappingManager.GetIsDragging()) {
DragManager.AbortDrag();
+ } else if (CollectionDockingView.Instance.HasFullScreen) {
+ CollectionDockingView.Instance.CloseFullScreen();
} else {
- if (CollectionDockingView.Instance.HasFullScreen()) {
- CollectionDockingView.Instance.CloseFullScreen();
- } else {
- doDeselect = !ContextMenu.Instance.closeMenu();
- }
+ doDeselect = !ContextMenu.Instance.closeMenu();
}
doDeselect && SelectionManager.DeselectAll();
DictationManager.Controls.stop();
@@ -113,30 +108,18 @@ export default class KeyManager {
break;
case "delete":
case "backspace":
- if (document.activeElement) {
- if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") {
- return { stopPropagation: false, preventDefault: false };
- }
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
}
const selected = SelectionManager.SelectedDocuments().slice();
- UndoManager.RunInBatch(() => {
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
- }, "delete");
+ UndoManager.RunInBatch(() => selected.map(dv => dv.props.removeDocument?.(dv.props.Document)), "delete");
SelectionManager.DeselectAll();
break;
- case "arrowleft":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left");
- break;
- case "arrowright":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(1, 0)), "nudge right");
- break;
- case "arrowup":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -1)), "nudge up");
- break;
- case "arrowdown":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 1)), "nudge down");
- break;
+ case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left"); break;
+ case "arrowright": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(1, 0)), "nudge right"); break;
+ case "arrowup": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -1)), "nudge up"); break;
+ case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 1)), "nudge down"); break;
}
return {
@@ -150,22 +133,10 @@ export default class KeyManager {
const preventDefault = false;
switch (keyname) {
- // case "~":
- // DictationManager.Controls.listen({ useOverlay: true, tryExecute: true });
- // stopPropagation = true;
- // preventDefault = true;
- case "arrowleft":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-10, 0)), "nudge left");
- break;
- case "arrowright":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(10, 0)), "nudge right");
- break;
- case "arrowup":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -10)), "nudge up");
- break;
- case "arrowdown":
- UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 10)), "nudge down");
- break;
+ case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-10, 0)), "nudge left"); break;
+ case "arrowright": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(10, 0)), "nudge right"); break;
+ case "arrowup": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -10)), "nudge up"); break;
+ case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 10)), "nudge down"); break;
}
return {
@@ -204,55 +175,32 @@ export default class KeyManager {
switch (keyname) {
case "arrowright":
- if (document.activeElement) {
- if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") {
- return { stopPropagation: false, preventDefault: false };
- }
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
}
- MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform);
+ MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right");
break;
case "arrowleft":
- if (document.activeElement) {
- if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") {
- return { stopPropagation: false, preventDefault: false };
- }
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
}
- MainView.Instance.mainFreeform && CollectionDockingView.CloseRightSplit(MainView.Instance.mainFreeform);
+ MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform);
break;
case "backspace":
- if (document.activeElement) {
- if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") {
- return { stopPropagation: false, preventDefault: false };
- }
+ if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+ return { stopPropagation: false, preventDefault: false };
}
break;
case "t":
PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
- if (MainView.Instance.flyoutWidth === 240) {
- MainView.Instance.flyoutWidth = 0;
- } else {
- MainView.Instance.flyoutWidth = 240;
- }
- break;
- case "l":
- PromiseValue(Cast(Doc.UserDoc()["tabs-button-library"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
- if (MainView.Instance.flyoutWidth === 250) {
- MainView.Instance.flyoutWidth = 0;
- } else {
- MainView.Instance.flyoutWidth = 250;
- }
break;
case "f":
- PromiseValue(Cast(Doc.UserDoc()["tabs-button-search"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
- if (MainView.Instance.flyoutWidth === 400) {
- MainView.Instance.flyoutWidth = 0;
- } else {
- MainView.Instance.flyoutWidth = 400;
- }
+ SearchBox.Instance._searchFullDB = "My Stuff";
+ SearchBox.Instance.enter(undefined);
break;
case "o":
const target = SelectionManager.SelectedDocuments()[0];
- target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
+ target && CollectionDockingView.OpenFullScreen(target.props.Document);
break;
case "r":
preventDefault = false;
@@ -323,8 +271,6 @@ export default class KeyManager {
undoBatch(() => {
targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
- const lastModified = "lastModified";
- targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
})();
}
}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
new file mode 100644
index 000000000..ad5c70fb1
--- /dev/null
+++ b/src/client/views/InkStrokeProperties.ts
@@ -0,0 +1,268 @@
+import { action, computed, observable } from "mobx";
+import { ColorState } from 'react-color';
+import { Doc, Field, Opt } from "../../fields/Doc";
+import { Document } from "../../fields/documentSchemas";
+import { InkField } from "../../fields/InkField";
+import { Cast, NumCast } from "../../fields/Types";
+import { DocumentType } from "../documents/DocumentTypes";
+import { SelectionManager } from "../util/SelectionManager";
+import { undoBatch } from "../util/UndoManager";
+
+export class InkStrokeProperties {
+ static Instance: InkStrokeProperties | undefined;
+
+ private _lastFill = "#D0021B";
+ private _lastLine = "#D0021B";
+ private _lastDash = "2";
+
+ @observable _lock = false;
+ @observable _controlBtn = false;
+ @observable _currPoint = -1;
+
+ getField(key: string) {
+ return this.selectedInk?.reduce((p, i) =>
+ (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
+ }
+
+ @computed get selectedInk() {
+ const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK);
+ return inks.length ? inks : undefined;
+ }
+ @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
+ @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
+ @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
+ @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
+ @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; }
+ @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
+ @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
+ @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
+ @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
+ @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
+ @computed get shapeHgt() { return this.getField("_height"); }
+ @computed get shapeWid() { return this.getField("_width"); }
+ @computed get shapeXps() { return this.getField("x"); }
+ @computed get shapeYps() { return this.getField("y"); }
+ @computed get shapeRot() { return this.getField("rotation"); }
+ set unFilled(value) { this.colorFil = value ? "" : this._lastFill; }
+ set solidFil(value) { this.unFilled = !value; }
+ set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
+ set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); }
+ set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); }
+ set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); }
+ set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
+ set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
+ set dashdStk(value) {
+ value && (this._lastDash = value) && (this.unStrokd = false);
+ this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
+ }
+ set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); }
+ set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); }
+ set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); }
+ set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
+ set shapeWid(value) {
+ this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ const oldWidth = NumCast(i.rootDoc._width);
+ i.rootDoc._width = Number(value);
+ this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth);
+ });
+ }
+ set shapeHgt(value) {
+ this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
+ const oldHeight = NumCast(i.rootDoc._height);
+ i.rootDoc._height = Number(value);
+ this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight);
+ });
+ }
+
+ constructor() {
+ InkStrokeProperties.Instance = this;
+ }
+
+ @undoBatch
+ @action
+ addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => {
+ this.selectedInk?.forEach(action(inkView => {
+ if (this.selectedInk?.length === 1) {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+ const newPoints: { X: number, Y: number }[] = [];
+ var counter = 0;
+ for (var k = 0; k < index; k++) {
+ control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++);
+ }
+ //decide where to put the new coordinate
+ const spNum = Math.floor(counter / 2) * 4 + 2;
+
+ for (var i = 0; i < spNum; i++) {
+ ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ for (var j = 0; j < 4; j++) {
+ newPoints.push({ X: x, Y: y });
+
+ }
+ for (var i = spNum; i < ink.length; i++) {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ this._currPoint = -1;
+ Doc.GetProto(doc).data = new InkField(newPoints);
+ }
+ }
+ }
+ }));
+ }
+
+ @undoBatch
+ @action
+ deletePoints = () => {
+ this.selectedInk?.forEach(action(inkView => {
+ if (this.selectedInk?.length === 1 && this._currPoint !== -1) {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink && ink.length > 4) {
+ const newPoints: { X: number, Y: number }[] = [];
+ const toRemove = Math.floor(((this._currPoint + 2) / 4));
+ for (var i = 0; i < ink.length; i++) {
+ if (Math.floor((i + 2) / 4) !== toRemove) {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ }
+ this._currPoint = -1;
+ Doc.GetProto(doc).data = new InkField(newPoints);
+ if (newPoints.length === 4) {
+ const newerPoints: { X: number, Y: number }[] = [];
+ newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y });
+ newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y });
+ newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y });
+ newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y });
+ Doc.GetProto(doc).data = new InkField(newerPoints);
+
+ }
+ }
+ }
+ }
+ }));
+ }
+
+ @undoBatch
+ @action
+ rotate = (angle: number) => {
+ const _centerPoints: { X: number, Y: number }[] = [];
+ SelectionManager.SelectedDocuments().forEach(action(inkView => {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+ const xs = ink.map(p => p.X);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+ _centerPoints.push({ X: left, Y: top });
+ }
+ }
+ }));
+
+ var index = 0;
+ SelectionManager.SelectedDocuments().forEach(action(inkView => {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ doc.rotation = Number(doc.rotation) + Number(angle);
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+
+ const newPoints: { X: number, Y: number }[] = [];
+ ink.forEach(i => {
+ const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X;
+ const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
+ newPoints.push({ X: newX, Y: newY });
+ });
+ Doc.GetProto(doc).data = new InkField(newPoints);
+ const xs = newPoints.map(p => p.X);
+ const ys = newPoints.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ const right = Math.max(...xs);
+ const bottom = Math.max(...ys);
+
+ doc._height = (bottom - top);
+ doc._width = (right - left);
+ }
+ index++;
+ }
+ }));
+ }
+
+ @undoBatch
+ @action
+ control = (xDiff: number, yDiff: number, controlNum: number) => {
+ this.selectedInk?.forEach(action(inkView => {
+ if (this.selectedInk?.length === 1) {
+ const doc = Document(inkView.rootDoc);
+ if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
+ const ink = Cast(doc.data, InkField)?.inkData;
+ if (ink) {
+
+ const newPoints: { X: number, Y: number }[] = [];
+ const order = controlNum % 4;
+ for (var i = 0; i < ink.length; i++) {
+ if (controlNum === i ||
+ (order === 0 && i === controlNum + 1) ||
+ (order === 0 && controlNum !== 0 && i === controlNum - 2) ||
+ (order === 0 && controlNum !== 0 && i === controlNum - 1) ||
+ (order === 3 && i === controlNum - 1) ||
+ (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) ||
+ (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2)
+ || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
+ ) {
+ newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) });
+ }
+ else {
+ newPoints.push({ X: ink[i].X, Y: ink[i].Y });
+ }
+ }
+ const oldx = doc.x;
+ const oldy = doc.y;
+ const xs = ink.map(p => p.X);
+ const ys = ink.map(p => p.Y);
+ const left = Math.min(...xs);
+ const top = Math.min(...ys);
+ Doc.GetProto(doc).data = new InkField(newPoints);
+ const xs2 = newPoints.map(p => p.X);
+ const ys2 = newPoints.map(p => p.Y);
+ const left2 = Math.min(...xs2);
+ const top2 = Math.min(...ys2);
+ const right2 = Math.max(...xs2);
+ const bottom2 = Math.max(...ys2);
+ doc._height = (bottom2 - top2);
+ doc._width = (right2 - left2);
+ //if points move out of bounds
+
+ doc.x = oldx - (left - left2);
+ doc.y = oldy - (top - top2);
+
+ }
+ }
+ }
+ }));
+ }
+
+ @undoBatch
+ @action
+ switchStk = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorStk = val;
+ return true;
+ }
+
+ @undoBatch
+ @action
+ switchFil = (color: ColorState) => {
+ const val = String(color.hex);
+ this.colorFil = val;
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 41311ed86..da98eca73 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -1,27 +1,22 @@
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faPaintBrush } from "@fortawesome/free-solid-svg-icons";
+import { action } from "mobx";
import { observer } from "mobx-react";
+import { Doc } from "../../fields/Doc";
import { documentSchema } from "../../fields/documentSchemas";
import { InkData, InkField, InkTool } from "../../fields/InkField";
import { makeInterface } from "../../fields/Schema";
import { Cast, StrCast } from "../../fields/Types";
import { TraceMobx } from "../../fields/util";
+import { setupMoveUpEvents } from "../../Utils";
import { CognitiveServices } from "../cognitive_services/CognitiveServices";
import { InteractionUtils } from "../util/InteractionUtils";
+import { Scripting } from "../util/Scripting";
+import { UndoManager } from "../util/UndoManager";
import { ContextMenu } from "./ContextMenu";
import { ViewBoxBaseComponent } from "./DocComponent";
import "./InkingStroke.scss";
import { FieldView, FieldViewProps } from "./nodes/FieldView";
import React = require("react");
-import { Scripting } from "../util/Scripting";
-import { Doc } from "../../fields/Doc";
-import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
-import { action } from "mobx";
-import { setupMoveUpEvents } from "../../Utils";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-
-
-library.add(faPaintBrush);
+import { InkStrokeProperties } from "./InkStrokeProperties";
type InkDocument = makeInterface<[typeof documentSchema]>;
const InkDocument = makeInterface(documentSchema);
@@ -47,11 +42,6 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
this.props.Document.isInkMask = true;
}
- @action
- private formatShape = () => {
- FormatShapePane.Instance.Pinned = true;
- }
-
public _prevX = 0;
public _prevY = 0;
private _controlNum = 0;
@@ -66,15 +56,17 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
@action
changeCurrPoint = (i: number) => {
- FormatShapePane.Instance._currPoint = i;
+ if (!InkStrokeProperties.Instance) return;
+ InkStrokeProperties.Instance._currPoint = i;
document.addEventListener("keydown", this.delPts, true);
}
@action
onControlMove = (e: PointerEvent, down: number[]): boolean => {
+ if (!InkStrokeProperties.Instance) return false;
const xDiff = this._prevX - e.clientX;
const yDiff = this._prevY - e.clientY;
- FormatShapePane.Instance.control(xDiff, yDiff, this._controlNum);
+ InkStrokeProperties.Instance.control(xDiff, yDiff, this._controlNum);
this._prevX = e.clientX;
this._prevY = e.clientY;
return false;
@@ -89,8 +81,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
}
@action
delPts = (e: KeyboardEvent | React.PointerEvent | undefined) => {
- if (e instanceof KeyboardEvent ? e.key === "-" : true) {
- FormatShapePane.Instance.deletePoints();
+ if (InkStrokeProperties.Instance && (e instanceof KeyboardEvent ? e.key === "-" : true)) {
+ InkStrokeProperties.Instance.deletePoints();
}
}
@@ -98,34 +90,40 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
public static MaskDim = 50000;
render() {
TraceMobx();
+ const formatInstance = InkStrokeProperties.Instance;
+ if (!formatInstance) return (null);
const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
// const strokeWidth = Number(StrCast(this.layoutDoc.strokeWidth, ActiveInkWidth()));
const strokeWidth = Number(this.layoutDoc.strokeWidth);
const xs = data.map(p => p.X);
const ys = data.map(p => p.Y);
- const left = Math.min(...xs) - strokeWidth / 2;
- const top = Math.min(...ys) - strokeWidth / 2;
- const right = Math.max(...xs) + strokeWidth / 2;
- const bottom = Math.max(...ys) + strokeWidth / 2;
- const width = Math.max(right - left);
+ const lineTop = Math.min(...ys);
+ const lineBot = Math.max(...ys);
+ const lineLft = Math.min(...xs);
+ const lineRgt = Math.max(...xs);
+ const left = lineLft - strokeWidth / 2;
+ const top = lineTop - strokeWidth / 2;
+ const right = lineRgt + strokeWidth / 2;
+ const bottom = lineBot + strokeWidth / 2;
+ const width = Math.max(1, right - left);
const height = Math.max(1, bottom - top);
const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth);
const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth);
const strokeColor = StrCast(this.layoutDoc.color, "");
const points = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+ StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
- StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
+ StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBot - lineTop > 1 && lineRgt - lineLft > 1, false);
const hpoints = InteractionUtils.CreatePolyline(data, left, top,
this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15),
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
- "none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true);
+ StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
+ "none", "none", undefined, scaleX, scaleY, "", "visiblepainted", false, true);
//points for adding
const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth,
- StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"),
+ StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker),
StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false);
@@ -161,36 +159,31 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
const dotsize = String(Math.max(width * scaleX, height * scaleY) / 40);
const addpoints = apoints.map((pts, i) =>
-
- <svg height="10" width="10">
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="invisible" stroke-width={String(Number(dotsize) / 2)} fill="invisible"
- onPointerDown={(e) => { FormatShapePane.Instance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll"
+ <svg height="10" width="10" key={`add${i}`}>
+ <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="invisible" strokeWidth={String(Number(dotsize) / 2)} fill="invisible"
+ onPointerDown={(e) => { formatInstance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll"
/>
</svg>);
const controls = controlPoints.map((pts, i) =>
-
- <svg height="10" width="10">
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" stroke-width={String(Number(dotsize) / 2)} fill="red"
+ <svg height="10" width="10" key={`ctrl${i}`}>
+ <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" strokeWidth={String(Number(dotsize) / 2)} fill="red"
onPointerDown={(e) => { this.changeCurrPoint(pts.I); this.onControlDown(e, pts.I); }} pointerEvents="all" cursor="all-scroll"
/>
</svg>);
const handles = handlePoints.map((pts, i) =>
-
- <svg height="10" width="10">
- <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" stroke-width={String(Number(dotsize) / 2)} fill="green"
- onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
+ <svg height="10" width="10" key={`hdl${i}`}>
+ <circle cx={(pts.X - left - strokeWidth / 2) * scaleX + strokeWidth / 2} cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={dotsize} stroke="black" strokeWidth={String(Number(dotsize) / 2)} fill="green"
+ onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
</svg>);
const handleLines = handleLine.map((pts, i) =>
-
- <svg height="100" width="100">
+ <svg height="100" width="100" key={`line${i}`}>
<line x1={(pts.X1 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
- x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" stroke-width={String(Number(dotsize) / 2)}
- display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
+ x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" strokeWidth={String(Number(dotsize) / 2)}
+ display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
<line x1={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y1={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2}
- x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" stroke-width={String(Number(dotsize) / 2)}
- display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} />
-
+ x2={(pts.X3 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y3 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} stroke="green" strokeWidth={String(Number(dotsize) / 2)}
+ display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} />
</svg>);
@@ -209,6 +202,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
if (cm) {
!Doc.UserDoc().noviceMode && cm.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
cm.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
+ cm.addItem({ description: "Edit Points", event: action(() => formatInstance._controlBtn = !formatInstance._controlBtn), icon: "paint-brush" });
//cm.addItem({ description: "Format Shape...", event: this.formatShape, icon: "paint-brush" });
}
}}
@@ -216,10 +210,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
</defs>
{hpoints}
{points}
- {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? addpoints : ""}
- {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? controls : ""}
- {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handles : ""}
- {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handleLines : ""}
+ {formatInstance._controlBtn && this.props.isSelected() ? addpoints : ""}
+ {formatInstance._controlBtn && this.props.isSelected() ? controls : ""}
+ {formatInstance._controlBtn && this.props.isSelected() ? handles : ""}
+ {formatInstance._controlBtn && this.props.isSelected() ? handleLines : ""}
</svg>
);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index a05a2b858..9ca8f348d 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -1,6 +1,7 @@
@import "globalCssVariables";
@import "nodeModuleOverrides";
+
.dash-tooltip {
font-size: 11px;
padding: 2px;
@@ -14,18 +15,23 @@
margin-top: 10px;
}
+.mainContent-div-flyout,
.mainContent-div {
position: relative;
width: 100%;
height: 100%;
}
+.mainContent-div-flyout {
+ left: calc(-1 * var(--flyoutHandleWidth));
+}
// add nodes menu. Note that the + button is actually an input label, not an actual button.
.mainView-docButtons {
position: absolute;
- bottom: 35px;
+ bottom: 10px;
left: calc(100% + 5px);
z-index: 1;
+ pointer-events: none;
}
.mainView-snapLines {
@@ -110,13 +116,16 @@
height: 55px;
width: 17px;
position: absolute;
- top: 55%;
+ top: 50%;
border: 1px black solid;
border-radius: 0;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border-right: unset;
- z-index: 2;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ padding: 4px;
.mainView-propertiesDragger-icon {
width: 10px;
@@ -143,26 +152,38 @@
cursor: auto;
}
-.mainView-flyoutContainer {
- display: flex;
- flex-direction: column;
+.mainView-innerContent {
+ display: contents;
+ flex-direction: row;
position: relative;
- height: 100%;
- background: dimgray;
+ .mainView-flyoutContainer {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ height: 100%;
+ background: dimgray;
- .documentView-node-topmost {
- background: lightgrey;
+ .documentView-node-topmost {
+ background: lightgrey;
+ }
+ }
+ .propertiesView {
+ right: 0;
+ position: absolute;
+ z-index: 2;
}
}
.mainView-menuPanel {
-
- width: 60px;
+ min-width: var(--menuPanelWidth);
background-color: #121721;
- height: calc(100% - $searchpanel-height);
- //overflow-y: scroll;
- //overflow-x: hidden;
+ .collectionStackingView {
+ scrollbar-width: none;
+ }
+ ::-webkit-scrollbar {
+ width:0;
+ }
.mainView-menuPanel-button {
padding: 7px;
@@ -265,14 +286,22 @@
transform: none !important;
}
+.mainView-libraryFlyout-out,
.mainView-libraryFlyout {
height: 100%;
width: 100%;
- position: absolute;
+ position: relative;
display: flex;
flex-direction: column;
z-index: 2;
+ .mainView-contentArea {
+ position: relative;
+ height: 100%;
+ width: 100%;
+ overflow: visible;
+ }
+
.mainView-libraryFlyout-close {
right: 6;
top: 5;
@@ -282,26 +311,27 @@
margin-bottom: 10;
}
}
-
-.mainView-expandFlyoutButton {
- position: absolute;
- top: 120px;
- right: 55px;
- cursor: pointer;
+.mainView-libraryFlyout-out {
+ transition: width .25s;
+ box-shadow: rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw;
}
+
.mainView-libraryHandle {
- width: 28px;
- left: calc(100% - 10px);
+ width: var(--flyoutHandleWidth);
height: 55px;
top: 50%;
+ left: -10px;
border: 1px solid black;
border-radius: 8px;
- position: absolute;
- z-index: 2;
+ position: relative;
+ z-index: 1;
touch-action: none;
background-color: lightgrey;
cursor: grab;
+ display: flex;
+ align-items: center;
+ padding: 12px;
.mainView-libraryHandle-icon {
width: 10px;
@@ -313,7 +343,7 @@
}
-.mainView-workspace {
+.mainView-dashboard {
height: 200px;
position: relative;
display: flex;
@@ -325,8 +355,25 @@
display: flex;
}
-.mainView-recentlyClosed {
+.mainView-inactiveDocs {
height: 25%;
position: relative;
display: flex;
+}
+
+.mainVew-webRef {
+ position: absolute;
+ left: -1000;
+ top: -1000;
+ display: block;
+ width: 200px;
+ height: 800px;
+}
+.mainVew-invisibleWebRef {
+ position: absolute;
+ left: 50;
+ top: 50;
+ display: block;
+ width: 500px;
+ height: 1000px;
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 059e1f566..f9b3b1da8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,48 +1,44 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faHireAHelper, faBuffer } from '@fortawesome/free-brands-svg-icons';
+import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
-import Measure from 'react-measure';
-import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
-import { Id } from '../../fields/FieldSymbols';
+import * as ReactDOM from 'react-dom';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { List } from '../../fields/List';
-import { listSpec } from '../../fields/Schema';
-import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
-import { TraceMobx } from '../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, simulateMouseClick } from '../../Utils';
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import { PrefetchProxy } from '../../fields/Proxy';
+import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
-import { Docs, DocumentOptions } from '../documents/Documents';
+import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { DocumentManager } from '../util/DocumentManager';
-import GroupManager from '../util/GroupManager';
+import { GroupManager } from '../util/GroupManager';
import { HistoryUtil } from '../util/History';
+import { Hypothesis } from '../util/HypothesisUtils';
import { Scripting } from '../util/Scripting';
-import { SelectionManager } from '../util/SelectionManager';
-import SettingsManager from '../util/SettingsManager';
-import SharingManager from '../util/SharingManager';
+import { SettingsManager } from '../util/SettingsManager';
+import { SharingManager } from '../util/SharingManager';
import { SnappingManager } from '../util/SnappingManager';
import { Transform } from '../util/Transform';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
-import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
-import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
-import { PropertiesView } from './collections/collectionFreeForm/PropertiesView';
+import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
import { CollectionLinearView } from './collections/CollectionLinearView';
-import CollectionMenu from './collections/CollectionMenu';
-import { CollectionView, CollectionViewType } from './collections/CollectionView';
+import { CollectionMenu } from './collections/CollectionMenu';
+import { CollectionViewType } from './collections/CollectionView';
import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
-import GestureOverlay from './GestureOverlay';
-import { ANTIMODEMENU_HEIGHT, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss';
-import KeyManager from './GlobalKeyHandler';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { GestureOverlay } from './GestureOverlay';
+import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss';
+import { KeyManager } from './GlobalKeyHandler';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
import { AudioBox } from './nodes/AudioBox';
@@ -53,70 +49,46 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview } from './nodes/LinkDocPreview';
import { RadialMenu } from './nodes/RadialMenu';
import { TaskCompletionBox } from './nodes/TaskCompletedBox';
+import { WebBox } from './nodes/WebBox';
import { OverlayView } from './OverlayView';
-import PDFMenu from './pdf/PDFMenu';
+import { PDFMenu } from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
-import { Hypothesis } from '../util/HypothesisUtils';
-import { WebBox } from './nodes/WebBox';
-import * as ReactDOM from 'react-dom';
+import { PropertiesView } from './PropertiesView';
import { SearchBox } from './search/SearchBox';
-import { SearchUtil } from '../util/SearchUtil';
-import { Networking } from '../Network';
-import * as rp from 'request-promise';
-import { LinkManager } from '../util/LinkManager';
-import RichTextMenu from './nodes/formattedText/RichTextMenu';
+import { TraceMobx } from '../../fields/util';
+import { SelectionManager } from '../util/SelectionManager';
+const _global = (window /* browser */ || global /* node */) as any;
@observer
export class MainView extends React.Component {
public static Instance: MainView;
- private _buttonBarHeight = 36;
- private _flyoutSizeOnDown = 0;
- private _urlState: HistoryUtil.DocUrl;
private _docBtnRef = React.createRef<HTMLDivElement>();
private _mainViewRef = React.createRef<HTMLDivElement>();
+ private _lastButton: Doc | undefined;
@observable private _panelWidth: number = 0;
@observable private _panelHeight: number = 0;
- @observable private _flyoutTranslate: boolean = false;
- @observable public flyoutWidth: number = 0;
- private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); }
+ @observable private _panelContent: string = "none";
+ @observable private _sidebarContent: any = this.userDoc?.sidebar;
+ @observable private _flyoutWidth: number = 0;
+ @computed private get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); }
@computed private get userDoc() { return Doc.UserDoc(); }
- @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
+ @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
+ @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
- @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
-
- @observable public sidebarContent: any = this.userDoc?.sidebar;
- @observable public panelContent: string = "none";
- @observable public showProperties: boolean = false;
- public isPointerDown = false;
- @computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else { return undefined; }
- }
-
- propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, CurrentUserUtils.propertiesWidth));
- @computed get propertiesIcon() {
- if (this.propertiesWidth() < 10) {
- return "chevron-left";
- } else {
- return "chevron-right";
- }
- }
- @observable propertiesDownX: number | undefined;
+ menuPanelWidth = () => Number(MENU_PANEL_WIDTH.replace("px", ""));
+ propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, CurrentUserUtils.propertiesWidth || 0));
componentDidMount() {
+ new InkStrokeProperties();
+ this._sidebarContent.proto = undefined;
DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
- const tag = document.createElement('script');
-
- const proto = DocServer.GetRefField("rtfProto").then(proto => {
- (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE),
- msg => msg && alert(msg));
- });
+ DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
+ const tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
@@ -125,11 +97,7 @@ export class MainView extends React.Component {
window.addEventListener("paste", KeyManager.Instance.paste as any);
document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on
const id = FormattedTextBox.GetDocFromUrl(e.detail);
- DocServer.GetRefField(id).then(doc => {
- if (doc instanceof Doc) {
- DocumentManager.Instance.jumpToDocument(doc, false, undefined);
- }
- });
+ DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
});
document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
}
@@ -137,7 +105,6 @@ export class MainView extends React.Component {
componentWillUnMount() {
window.removeEventListener("keydown", KeyManager.Instance.handle);
window.removeEventListener("pointerdown", this.globalPointerDown);
- window.removeEventListener("pointerup", this.globalPointerUp);
window.removeEventListener("paste", KeyManager.Instance.paste as any);
document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
}
@@ -145,212 +112,125 @@ export class MainView extends React.Component {
constructor(props: Readonly<{}>) {
super(props);
MainView.Instance = this;
- this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
- // causes errors to be generated when modifying an observable outside of an action
-
- CurrentUserUtils.propertiesWidth = 0;
+ CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
+ // causes errors to be generated when modifying an observable outside of an action
configure({ enforceActions: "observed" });
+
if (window.location.pathname !== "/home") {
const pathname = window.location.pathname.substr(1).split("/");
- if (pathname.length > 1) {
- const type = pathname[0];
- if (type === "doc") {
- CurrentUserUtils.MainDocId = pathname[1];
- if (!this.userDoc) {
- runInAction(() => this.closeFlyout());
- DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt<Field>) =>
- field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
- }
- }
+ if (pathname.length > 1 && pathname[0] === "doc") {
+ CurrentUserUtils.MainDocId = pathname[1];
+ !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
}
}
library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
- fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
- fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
- fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
+ fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+ fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
+ fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
- fa.faSearch, fa.faFileDownload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
- fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faClipboard,
+ fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
+ fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
- fa.faFileAudio, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+ fa.faFileAudio, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote,
fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
- fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
+ fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle,
fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
- fa.faWindowMinimize, fa.faWindowRestore);
+ fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
+ fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
+ fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload,
+ fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines);
this.initEventListeners();
this.initAuthenticationRouters();
}
globalPointerDown = action((e: PointerEvent) => {
- this.isPointerDown = true;
AudioBox.Enabled = true;
const targets = document.elementsFromPoint(e.x, e.y);
- if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) {
- ContextMenu.Instance.closeMenu();
- }
- if (targets && (targets.length && targets[0].className.toString() !== "timeline-menu-desc" && targets[0].className.toString() !== "timeline-menu-item" && targets[0].className.toString() !== "timeline-menu-input")) {
- TimelineMenu.Instance.closeMenu();
- }
- if (targets && targets.length && SearchBox.Instance._searchbarOpen) {
- let check = false;
- const icon = "icon";
- targets.forEach((thing) => {
- if (thing.className.toString() === "collectionSchemaView-searchContainer" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "collectionSchema-header-menuOptions" || thing.className.toString() === "altcollectionTimeView-treeView") {
- check = true;
- }
- });
- if (check === false) {
- SearchBox.Instance.closeSearch();
+ if (targets.length) {
+ const targClass = targets[0].className.toString();
+ if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
+ const check = targets.some((thing) =>
+ (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
+ thing.className === "collectionSchema-header-menuOptions"));
+ !check && SearchBox.Instance.resetSearch(true);
}
+ !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
+ !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
}
-
-
});
- globalPointerUp = () => this.isPointerDown = false;
-
initEventListeners = () => {
- window.addEventListener("drop", (e) => { e.preventDefault(); }, false); // drop event handler
- window.addEventListener("dragover", (e) => { e.preventDefault(); }, false); // drag event handler
- // click interactions for the context menu
+ window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
+ window.addEventListener("dragover", e => e.preventDefault(), false);
document.addEventListener("pointerdown", this.globalPointerDown);
- document.addEventListener("pointerup", this.globalPointerUp);
+ document.addEventListener("click", (e: MouseEvent) => {
+ if (!e.cancelBubble) {
+ const pathstr = (e as any)?.path.map((p: any) => p.classList?.toString()).join();
+ if (pathstr.includes("libraryFlyout")) {
+ SelectionManager.DeselectAll();
+ }
+ }
+ });
}
initAuthenticationRouters = async () => {
- // Load the user's active workspace, or create a new one if initial session after signup
+ // Load the user's active dashboard, or create a new one if initial session after signup
const received = CurrentUserUtils.MainDocId;
if (received && !this.userDoc) {
- reaction(
- () => CurrentUserUtils.GuestTarget,
- target => target && this.createNewWorkspace(),
- { fireImmediately: true }
- );
+ reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
} else {
- if (received && this._urlState.sharing) {
+ if (received && CurrentUserUtils._urlState.sharing) {
reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
- CollectionDockingView.AddRightSplit(docField);
+ CollectionDockingView.AddSplit(docField, "right");
}
}),
);
}
- const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc);
- if (doc) {
- this.openWorkspace(doc);
- } else {
- this.createNewWorkspace();
- }
- }
- }
-
- @action
- createNewWorkspace = async (id?: string) => {
- const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc;
- const workspaceCount = DocListCast(workspaces.data).length + 1;
- const freeformOptions: DocumentOptions = {
- x: 0,
- y: 400,
- _width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
- _height: this._panelHeight,
- title: "Untitled Collection",
- };
- const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row");
-
- const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
- const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
- const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`);
- workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, copyWorkspace!]);
- workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Workspace"]);
-
- Doc.AddDocToList(workspaces, "data", workspaceDoc);
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => this.openWorkspace(workspaceDoc), 0);
- }
-
- @action
- openWorkspace = (doc: Doc, fromHistory = false) => {
- CurrentUserUtils.MainDocId = doc[Id];
-
- if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace
- !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
- this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
- }
- const state = this._urlState;
- if (state.sharing === true && !this.userDoc) {
- DocServer.Control.makeReadOnly();
- } else {
- fromHistory || HistoryUtil.pushState({
- type: "doc",
- docId: doc[Id],
- readonly: state.readonly,
- nro: state.nro,
- sharing: false,
+ const activeDash = PromiseValue(this.userDoc.activeDashboard);
+ activeDash.then(dash => {
+ if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
+ else CurrentUserUtils.createNewDashboard(this.userDoc);
});
- if (state.readonly === true || state.readonly === null) {
- DocServer.Control.makeReadOnly();
- } else if (state.safe) {
- if (!state.nro) {
- DocServer.Control.makeReadOnly();
- }
- CollectionView.SetSafeMode(true);
- } else if (state.nro || state.nro === null || state.readonly === false) {
- } else if (doc.readOnly) {
- DocServer.Control.makeReadOnly();
- } else {
- DocServer.Control.makeEditable();
- }
}
-
- return true;
- }
-
- onDrop = (e: React.DragEvent<HTMLDivElement>) => {
- e.preventDefault();
- e.stopPropagation();
}
@action
- onResize = (r: any) => {
- this._panelWidth = r.offset.width;// - this.propertiesWidth();
- this._panelHeight = r.offset.height;
+ createNewPresentation = async () => {
+ if (!await this.userDoc.myPresentations) {
+ this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "PRESENTATION TRAILS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
+ }));
+ }
+ const pres = Docs.Create.PresDocument(new List<Doc>(),
+ { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
+ CollectionDockingView.AddSplit(pres, "right");
+ this.userDoc.activePresentation = pres;
+ Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
}
- @action
- getPWidth = () => this._panelWidth - this.propertiesWidth()
-
+ getPWidth = () => this._panelWidth - this.propertiesWidth();
getPHeight = () => this._panelHeight;
- getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
+ getContentsHeight = () => this._panelHeight - Number(SEARCH_PANEL_HEIGHT.replace("px", ""));
defaultBackgroundColors = (doc: Opt<Doc>, renderDepth: number) => {
- if (this.panelContent === doc?.title) return "lightgrey";
-
if (doc?.type === DocumentType.COL) {
- if (doc.title === "Basic Item Creators" || doc.title === "sidebar-tools"
- || doc.title === "sidebar-recentlyClosed" || doc.title === "sidebar-catalog"
- || doc.title === "Mobile Uploads" || doc.title === "COLLECTION_PROTO"
- || doc.title === "Advanced Item Prototypes" || doc.title === "all Creators") {
- return "lightgrey";
- }
- return StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
+ return Doc.IsSystem(doc) ? "lightgrey" : StrCast(renderDepth > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground);
}
if (this.darkScheme) {
switch (doc?.type) {
case DocumentType.FONTICON: return "white";
case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: return "#2d2d2d";
case DocumentType.LINK:
- case DocumentType.COL: {
- if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
- }
+ case DocumentType.COL: if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
default: return "black";
}
} else {
@@ -360,9 +240,7 @@ export class MainView extends React.Component {
case DocumentType.BUTTON:
case DocumentType.LABEL: return "lightgray";
case DocumentType.LINK:
- case DocumentType.COL: {
- if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray";
- }
+ case DocumentType.COL: if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray";
default: return "white";
}
}
@@ -391,6 +269,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
renderDepth={-1}
@@ -398,60 +277,41 @@ export class MainView extends React.Component {
}
@computed get dockingContent() {
- TraceMobx();
- const mainContainer = this.mainContainer;
- const width = this.flyoutWidth + this.propertiesWidth();
- return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)`, height: `calc(100% - ${SEARCH_PANEL_HEIGHT})` }}>
- {!mainContainer ? (null) : this.mainDocView}
+ return <div className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
+ style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)` }}>
+ {!this.mainContainer ? (null) : this.mainDocView}
</div>;
}
@action
onPropertiesPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
- CurrentUserUtils.propertiesWidth = this._panelWidth - e.clientX;
- return false;
- }), returnFalse, action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false);
+ setupMoveUpEvents(this, e,
+ action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._panelWidth - e.clientX)) ? false : false),
+ action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
+ action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false);
}
@action
onFlyoutPointerDown = (e: React.PointerEvent) => {
- if (this._flyoutTranslate) {
- setupMoveUpEvents(this, e, action((e: PointerEvent) => {
- this.flyoutWidth = Math.max(e.clientX, 0);
- if (this.flyoutWidth < 5) {
- this.panelContent = "none";
- this._lastButton && (this._lastButton.color = "white");
- this._lastButton && (this._lastButton._backgroundColor = "");
- }
- return false;
- }), emptyFunction, action(() => {
- if (this.flyoutWidth < 15) MainView.expandFlyout();
- else this.closeFlyout();
- }));
- }
+ setupMoveUpEvents(this, e,
+ action(e => (this._flyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+ () => this._flyoutWidth < 5 && this.closeFlyout(),
+ this.closeFlyout);
}
- flyoutWidthFunc = () => this.flyoutWidth;
- addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
- return where === "close" ? CollectionDockingView.CloseRightSplit(doc) :
- doc.dockingConfig ? this.openWorkspace(doc) :
- CollectionDockingView.AddRightSplit(doc, libraryPath);
- }
+ flyoutWidthFunc = () => this._flyoutWidth;
sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1);
mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0);
+ addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => {
+ return where === "close" ? CollectionDockingView.CloseSplit(doc) :
+ doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
+ }
@computed get flyout() {
- if (!this.sidebarContent) return null;
- return <div className="mainView-libraryFlyout">
- <div className="mainView-contentArea" style={{ position: "relative", height: `calc(100% - ${SEARCH_PANEL_HEIGHT})`, width: "100%", overflow: "visible" }}>
- {/* {this.flyoutWidth > 0 ? <div className="mainView-libraryFlyout-close"
- onPointerDown={this.closeFlyout}>
- <FontAwesomeIcon icon="times" color="black" size="lg" />
- </div> : null} */}
-
+ return <div className={`mainView-libraryFlyout${this._flyoutWidth ? "" : "-out"}`} style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
+ <div className="mainView-contentArea" >
<DocumentView
- Document={this.sidebarContent}
+ Document={this._sidebarContent}
DataDoc={undefined}
LibraryPath={emptyPath}
addDocument={undefined}
@@ -473,13 +333,15 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
relative={true}
forcedBackgroundColor={() => "lightgrey"}
/>
</div>
- {this.docButtons}</div>;
+ {this.docButtons}
+ </div>;
}
@computed get menuPanel() {
@@ -498,7 +360,7 @@ export class MainView extends React.Component {
onClick={undefined}
ScreenToLocalTransform={this.sidebarScreenToLocal}
ContentScaling={returnOne}
- PanelWidth={() => 60}
+ PanelWidth={this.menuPanelWidth}
PanelHeight={this.getContentsHeight}
renderDepth={0}
focus={emptyFunction}
@@ -507,6 +369,7 @@ export class MainView extends React.Component {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
relative={true}
@@ -515,159 +378,94 @@ export class MainView extends React.Component {
</div>;
}
-
- @action
- closeFlyout = () => {
- this._lastButton && (this._lastButton.color = "white");
- this._lastButton && (this._lastButton._backgroundColor = "");
- this.panelContent = "none";
- this.flyoutWidth = 0;
- }
-
- get groupManager() { return GroupManager.Instance; }
-
- _lastButton: Doc | undefined;
@action
selectMenu = (button: Doc) => {
const title = StrCast(Doc.GetProto(button).title);
- this._lastButton && (this._lastButton.color = "white");
- this._lastButton && (this._lastButton._backgroundColor = "");
- if (this.panelContent === title && this.flyoutWidth !== 0) {
- this.panelContent = "none";
- this.flyoutWidth = 0;
- } else {
- let panelDoc: Doc | undefined;
- switch (this.panelContent = title) {
- case "Tools": panelDoc = Doc.UserDoc()["sidebar-tools"] as Doc ?? undefined; break;
- case "Workspace": panelDoc = Doc.UserDoc()["sidebar-workspaces"] as Doc ?? undefined; break;
- case "Catalog": panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
- case "Archive": panelDoc = Doc.UserDoc()["sidebar-recentlyClosed"] as Doc ?? undefined; break;
- case "Settings": SettingsManager.Instance.open(); break;
- case "Import": panelDoc = Doc.UserDoc()["sidebar-import"] as Doc ?? undefined; break;
- case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break;
- case "User Doc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break;
+ const willOpen = !this._flyoutWidth || this._panelContent !== title;
+ this.closeFlyout();
+ if (willOpen) {
+ switch (this._panelContent = title) {
+ case "Settings":
+ SettingsManager.Instance.open();
+ break;
+ case "Catalog":
+ SearchBox.Instance._searchFullDB = "My Stuff";
+ SearchBox.Instance.enter(undefined);
+ break;
+ default:
+ this.expandFlyout(button);
}
- this.sidebarContent.proto = panelDoc;
- if (panelDoc) {
- MainView.expandFlyout();
- button._backgroundColor = "lightgrey";
- button.color = "black";
- this._lastButton = button;
- } else this.flyoutWidth = 0;
}
return true;
}
- @action
- closeProperties = () => {
- CurrentUserUtils.propertiesWidth = 0;
- }
-
- @computed get propertiesView() {
- TraceMobx();
- return <div className="mainView-propertiesView" style={{
- overflow: this.propertiesWidth() < 15 ? "hidden" : undefined
- }}>
- <PropertiesView
- width={this.propertiesWidth()}
- height={this._panelHeight}
- renderDepth={1}
- ScreenToLocalTransform={Transform.Identity}
- onDown={this.closeProperties}
- />
- </div>;
- }
-
@computed get mainInnerContent() {
- const rightFlyout = this.propertiesWidth() - 1;
return <>
{this.menuPanel}
- <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
- <div className="mainView-flyoutContainer" style={{ width: this.flyoutWidth }}>
- {this.flyoutWidth !== 0 ? <div className="mainView-libraryHandle"
- onPointerDown={this.onFlyoutPointerDown}
- //style={{ backgroundColor: '#8c8b8b' }}
- >
- <span title="library View Dragger" style={{
- width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
- //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
- position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
- top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
- }} />
- <div className="mainview-libraryHandle-icon">
- <FontAwesomeIcon icon="chevron-left" color="black" size="sm" />
- </div>
- </div> : null}
- <div className="mainView-libraryFlyout" style={{
- //transformOrigin: this._flyoutTranslate ? "" : "left center",
- transition: this._flyoutTranslate ? "" : "width .5s",
- //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
- boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw"
- }}>
- {this.flyout}
- {this.expandButton}
- </div>
+ <div className="mainView-innerContent" >
+ {this.flyout}
+ <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+ <FontAwesomeIcon icon="chevron-left" color="black" size="sm" />
</div>
+
{this.dockingContent}
- {this.showProperties ? (null) :
- <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown}
- style={{ right: rightFlyout, top: "50%" }}>
- <div className="mainView-propertiesDragger-icon">
- <FontAwesomeIcon icon={this.propertiesIcon} color="black" size="sm" /> </div>
- </div>
- }
- {this.propertiesWidth() < 10 ? (null) :
- <div style={{ width: this.propertiesWidth(), height: "calc(100% - 35px)" }}> {this.propertiesView} </div>}
+
+ <div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+ <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color="black" size="sm" />
+ </div>
+ {this.propertiesWidth() < 10 ? (null) : <PropertiesView width={this.propertiesWidth()} height={this.getContentsHeight()} />}
</div>
</>;
}
@computed get mainContent() {
- const n = (CollectionMenu.Instance?.Pinned ? 1 : 0);
- const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`;
- const pinned = FormatShapePane.Instance?.Pinned;
- const innerContent = this.mainInnerContent;
- return !this.userDoc ? (null) : (
- <Measure offset onResize={this.onResize}>
- {({ measureRef }) =>
- <div className="mainView-mainContent" ref={measureRef} style={{
- color: this.darkScheme ? "rgb(205,205,205)" : "black",
- //change to times 2 for both pinned
- height,
- width: pinned ? `calc(100% - 200px)` : "100%"
- }} >
- {innerContent}
- </div>
- }
- </Measure>);
+ return !this.userDoc ? (null) :
+ <div className="mainView-mainContent" ref={r => {
+ r && new _global.ResizeObserver(action(() => { this._panelWidth = r.getBoundingClientRect().width; this._panelHeight = r.getBoundingClientRect().height; })).observe(r);
+ }} style={{
+ color: this.darkScheme ? "rgb(205,205,205)" : "black",
+ height: `calc(100% - ${this.topOffset}px)`,
+ width: "100%",
+ }} >
+ {this.mainInnerContent}
+ </div>;
}
- public static expandFlyout = action(() => {
- MainView.Instance._flyoutTranslate = true;
- MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250);
-
+ expandFlyout = action((button: Doc) => {
+ this._flyoutWidth = (this._flyoutWidth || 250);
+ this._sidebarContent.proto = button.target as any;
+ button._backgroundColor = "lightgrey";
+ button.color = "black";
+ this._lastButton = button;
});
- @computed get expandButton() {
- return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}></div>) : (null);
- }
+ closeFlyout = action(() => {
+ this._lastButton && (this._lastButton.color = "white");
+ this._lastButton && (this._lastButton._backgroundColor = "");
+ this._panelContent = "none";
+ this._sidebarContent.proto = undefined;
+ this._flyoutWidth = 0;
+ });
- addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
+ addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => {
+ const ret = flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc);
+ ret && (doc._stayInCollection = undefined);
+ return ret;
+ }, true)
buttonBarXf = () => {
if (!this._docBtnRef.current) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
return new Transform(-translateX, -translateY, 1 / scale);
}
+
@computed get docButtons() {
- const dockedBtns = Doc.UserDoc()?.dockedBtns;
- if (dockedBtns instanceof Doc) {
- return <div className="mainView-docButtons" ref={this._docBtnRef}
- style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+ return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
+ <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
<CollectionLinearView
- Document={dockedBtns}
+ Document={this.userDoc.dockedBtns}
DataDoc={undefined}
LibraryPath={emptyPath}
fieldKey={"data"}
@@ -696,23 +494,13 @@ export class MainView extends React.Component {
focus={emptyFunction}
whenActiveChanged={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
- }
- return (null);
- }
-
- get mainViewElement() {
- return document.getElementById("mainView-container");
- }
-
- get mainViewRef() {
- return this._mainViewRef;
}
-
@computed get snapLines() {
- return !Doc.UserDoc().showSnapLines ? (null) : <div className="mainView-snapLines">
+ return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
<svg style={{ width: "100%", height: "100%" }}>
{SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
{SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
@@ -742,12 +530,18 @@ export class MainView extends React.Component {
</defs>
</svg>;
}
+ select = (ctrlPressed: boolean) => { };
@computed get search() {
+ TraceMobx();
return <div className="mainView-searchPanel">
- {/* <div style={{ float: "left", marginLeft: "10px" }}>{Doc.CurrentUserEmail}</div> */}
- <div><DocumentView Document={this.searchDoc}
- DataDoc={undefined}
+ <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
+ DataDoc={CurrentUserUtils.MySearchPanelDoc}
+ fieldKey="data"
+ dropAction="move"
+ isSelected={returnTrue}
+ active={returnTrue}
+ select={this.select}
LibraryPath={emptyPath}
addDocument={undefined}
addDocTab={this.addDocTabFunc}
@@ -764,19 +558,18 @@ export class MainView extends React.Component {
PanelHeight={this.getPHeight}
renderDepth={0}
focus={emptyFunction}
- parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- /></div>
+ ContainingCollectionDoc={undefined} />
</div>;
}
@computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
return !DocumentLinksButton.invisibleWebDoc ? null :
- <div style={{ position: 'absolute', left: 50, top: 50, display: 'block', width: '500px', height: '1000px' }} ref={DocumentLinksButton.invisibleWebRef}>
+ <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
<WebBox
fieldKey={"data"}
ContainingCollectionView={undefined}
@@ -801,13 +594,13 @@ export class MainView extends React.Component {
NativeWidth={() => 800}
ContentScaling={returnOne}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
/>
</div>;
}
render() {
- return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}>
-
+ return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} onScroll={() => document.getElementById("root")!.scrollTop = 0} ref={this._mainViewRef}>
{this.inkResources}
<DictationOverlay />
<SharingManager />
@@ -817,8 +610,6 @@ export class MainView extends React.Component {
<DocumentDecorations />
{this.search}
<CollectionMenu />
- <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
- <FormatShapePane />
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} backgroundColor={this.defaultBackgroundColors}
@@ -830,15 +621,13 @@ export class MainView extends React.Component {
<PreviewCursor />
<TaskCompletionBox />
<ContextMenu />
- <FormatShapePane />
<RadialMenu />
<PDFMenu />
<MarqueeOptionsMenu />
-
<OverlayView />
<TimelineMenu />
{this.snapLines}
- <div ref={this.makeWebRef} style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} />
+ <div className="mainView-webRef" ref={this.makeWebRef} />
</div >);
}
@@ -847,7 +636,7 @@ export class MainView extends React.Component {
invisibleDoc => {
ReactDOM.unmountComponentAtNode(ele);
invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
- <div style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} ref={DocumentLinksButton.invisibleWebRef}>
+ <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
<WebBox
fieldKey={"data"}
ContainingCollectionView={undefined}
@@ -872,112 +661,26 @@ export class MainView extends React.Component {
NativeWidth={() => 800}
ContentScaling={returnOne}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
/>
</div>;
</span>, ele);
- var success = false;
+ let success = false;
const onSuccess = () => {
success = true;
clearTimeout(interval);
document.removeEventListener("editSuccess", onSuccess);
};
- // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
+ // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
// so we keep simulating clicks until annotations have loaded and editing is successful
- const interval = setInterval(() => {
- !success && simulateMouseClick(ele, 50, 50, 50, 50);
- }, 500);
-
+ const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
document.addEventListener("editSuccess", onSuccess);
});
}
-
- importDocument = () => {
- const sidebar = Cast(Doc.UserDoc()["sidebar-import-documents"], Doc, null);
- const sidebarDocView = DocumentManager.Instance.getDocumentView(sidebar);
- const input = document.createElement("input");
- input.type = "file";
- input.multiple = true;
- input.accept = ".zip, application/pdf, video/*, image/*, audio/*";
- input.onchange = async _e => {
- const upload = Utils.prepend("/uploadDoc");
- const formData = new FormData();
- const file = input.files && input.files[0];
- if (file && file.type === 'application/zip') {
- formData.append('file', file);
- formData.append('remap', "true");
- const response = await fetch(upload, { method: "POST", body: formData });
- const json = await response.json();
- if (json !== "error") {
- const doc = await DocServer.GetRefField(json);
- if (doc instanceof Doc && sidebarDocView) {
- sidebarDocView.props.addDocument?.(doc);
- setTimeout(() => {
- SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => {
- docs.docs.forEach(d => LinkManager.Instance.addLink(d));
- });
- }, 2000); // need to give solr some time to update so that this query will find any link docs we've added.
-
- }
- }
- } else if (input.files && input.files.length !== 0) {
- const files = input.files || [];
- Array.from(files).forEach(async file => {
- const res = await Networking.UploadFilesToServer(file);
- res.map(async ({ result }) => {
- const name = file.name;
- if (result instanceof Error) {
- return;
- }
- const path = Utils.prepend(result.accessPaths.agnostic.client);
- let doc: Doc;
- // Case 1: File is a video
- if (file.type.includes("video")) {
- doc = Docs.Create.VideoDocument(path, { _height: 100, title: name });
- // Case 2: File is a PDF document
- } else if (file.type === "application/pdf") {
- doc = Docs.Create.PdfDocument(path, { _height: 100, _fitWidth: true, title: name });
- // Case 3: File is an image
- } else if (file.type.includes("image")) {
- doc = Docs.Create.ImageDocument(path, { _height: 100, title: name });
- // Case 4: File is an audio document
- } else {
- doc = Docs.Create.AudioDocument(path, { title: name });
- }
- const res = await rp.get(Utils.prepend("/getUserDocumentId"));
- if (!res) {
- throw new Error("No user id returned");
- }
- const field = await DocServer.GetRefField(res);
- let pending: Opt<Doc>;
- if (field instanceof Doc) {
- pending = sidebar;
- }
- if (pending) {
- const data = await Cast(pending.data, listSpec(Doc));
- if (data) data.push(doc);
- else pending.data = new List([doc]);
- }
- });
- });
- } else {
- console.log("No file selected");
- }
- };
- input.click();
- }
}
+
Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
-Scripting.addGlobal(function freezeSidebar() { MainView.expandFlyout(); });
-Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
-Scripting.addGlobal(function copyWorkspace() {
- const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true);
- const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null);
- Doc.AddDocToList(workspaces, "data", copiedWorkspace);
- // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
- setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0);
-});
-Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); },
- "imports files from device directly into the import sidebar");
+Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); \ No newline at end of file
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 19387f619..34e0ff126 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -14,7 +14,7 @@ export interface MainViewOverlayProps {
}
@observer
-export default class MainViewModal extends React.Component<MainViewOverlayProps> {
+export class MainViewModal extends React.Component<MainViewOverlayProps> {
render() {
const p = this.props;
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 49580cde4..cc0dd0443 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,10 +1,11 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
+import ReactLoading from 'react-loading';
import { Doc, DocListCast, Opt } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { NumCast, Cast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
import { DocumentView } from "./nodes/DocumentView";
@@ -145,7 +146,7 @@ export class OverlayView extends React.Component {
@computed get overlayDocs() {
- const userDocOverlays = Doc.UserDoc().myOverlayDocuments;
+ const userDocOverlays = Doc.UserDoc().myOverlayDocs;
if (!userDocOverlays) {
return null;
}
@@ -165,7 +166,7 @@ export class OverlayView extends React.Component {
dragData.dropAction = "move";
dragData.removeDocument = (doc: Doc | Doc[]) => {
const docs = (doc instanceof Doc) ? [doc] : doc;
- docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null), "data", d));
+ docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d));
return true;
};
dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
@@ -205,12 +206,18 @@ export class OverlayView extends React.Component {
addDocTab={returnFalse}
pinToPres={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
});
}
+ public static ShowSpinner() {
+ return OverlayView.Instance.addElement(<ReactLoading type="spinningBubbles" color="green" height={250} width={250} />, { x: 300, y: 200 });
+ }
+
+
render() {
return (
<div className="overlayView" id="overlayView">
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 0a4334302..92c3f09b4 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { Doc } from "../../fields/Doc";
import { NumCast } from "../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter } from "../../Utils";
+import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { DocumentView } from "./nodes/DocumentView";
import "./Palette.scss";
@@ -60,6 +60,7 @@ export default class Palette extends React.Component<PaletteProps> {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
<div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index d7034fcfb..1cadba18a 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -8,7 +8,7 @@ import { Doc } from '../../fields/Doc';
import { Transform } from "../util/Transform";
import { DocServer } from '../DocServer';
import { undoBatch, UndoManager } from '../util/UndoManager';
-import { NumCast } from '../../fields/Types';
+import { NumCast, Cast } from '../../fields/Types';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import * as rp from 'request-promise';
import { Utils } from '../../Utils';
@@ -44,8 +44,7 @@ export class PreviewCursor extends React.Component<{}> {
if (plain.indexOf("www.youtube.com/watch") !== -1) {
const url = plain.replace("youtube.com/watch?v=", "youtube.com/embed/");
undoBatch(() => PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
- title: url, _width: 400, _height: 315,
- _nativeWidth: 600, _nativeHeight: 472.5,
+ title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5,
x: newPoint[0], y: newPoint[1]
})))();
}
@@ -53,45 +52,41 @@ export class PreviewCursor extends React.Component<{}> {
else if (re.test(plain)) {
const url = plain;
undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
- title: url, _width: 500, _height: 300, UseCors: true,
- // nativeWidth: 300, nativeHeight: 472.5,
- x: newPoint[0], y: newPoint[1]
+ title: url, _width: 500, _height: 300, useCors: true, x: newPoint[0], y: newPoint[1]
})))();
}
-
else if (plain.startsWith("__DashDocId(") || plain.startsWith("__DashCloneId(")) {
const clone = plain.startsWith("__DashCloneId(");
const docids = plain.split(":");
const strs = docids[0].split(",");
const ptx = Number(strs[0].substring((clone ? "__DashCloneId(" : "__DashDocId(").length));
const pty = Number(strs[1].substring(0, strs[1].length - 1));
- let count = 1;
- const list: Doc[] = [];
- let first: Doc | undefined;
- docids.map((did, i) => i && DocServer.GetRefField(did).then(async doc => {
- count++;
- if (doc instanceof Doc) {
- i === 1 && (first = doc);
- const alias = clone ? (await Doc.MakeClone(doc)).clone : doc;
- const deltaX = NumCast(doc.x) - NumCast(first!.x) - ptx;
- const deltaY = NumCast(doc.y) - NumCast(first!.y) - pty;
- alias.x = newPoint[0] + deltaX;
- alias.y = newPoint[1] + deltaY;
- list.push(alias);
- }
- if (count === docids.length) {
- undoBatch(() => PreviewCursor._addDocument(list))();
- }
- }));
+ const batch = UndoManager.StartBatch("cloning");
+ {
+ const docs = await Promise.all(docids.filter((did, i) => i).map(async (did) => {
+ const doc = Cast(await DocServer.GetRefField(did), Doc, null);
+ return clone ? (await Doc.MakeClone(doc)).clone : doc;
+ }));
+ const firstx = docs.length ? NumCast(docs[0].x) + ptx - newPoint[0] : 0;
+ const firsty = docs.length ? NumCast(docs[0].y) + pty - newPoint[1] : 0;
+ docs.map(doc => {
+ doc.x = NumCast(doc.x) - firstx;
+ doc.y = NumCast(doc.y) - firsty;
+ });
+ PreviewCursor._addDocument(docs);
+ }
+ batch.end();
e.stopPropagation();
- } else {
+ }
+ else {
// creates text document
FormattedTextBox.PasteOnLoad = e;
undoBatch(() => PreviewCursor._addLiveTextDoc(Docs.Create.TextDocument("", {
_width: 500,
limitHeight: 400,
_autoHeight: true,
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
x: newPoint[0],
y: newPoint[1],
title: "-pasted text-"
@@ -132,6 +127,7 @@ export class PreviewCursor extends React.Component<{}> {
e.key !== "Insert" && e.key !== "Home" && e.key !== "End" && e.key !== "PageUp" && e.key !== "PageDown" &&
e.key !== "NumLock" && e.key !== " " &&
(e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
+ (e.keyCode < 173 || e.keyCode > 183 || e.key === "-") && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
!e.key.startsWith("Arrow") &&
!e.defaultPrevented) {
if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {// /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 8d9d56c9e..d63eb874c 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -43,6 +43,13 @@ $linkGap : 3px;
cursor: pointer;
}
}
+.propertiesButtons-linkButton-empty.toggle-on {
+ background-color: white;
+ color: black;
+}
+.propertiesButtons-linkButton-empty.toggle-off {
+ color: white;
+}
.propertiesButtons {
margin-top: 3px;
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 35d4f7f6e..d66cba710 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,51 +1,29 @@
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowAltCircleDown, faArrowAltCircleRight, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faPhotoVideo, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc } from "../../fields/Doc";
+import { InkField } from '../../fields/InkField';
import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast, BoolCast } from "../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, Utils } from "../../Utils";
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
+import { Cast, NumCast } from "../../fields/Types";
+import { ImageField } from '../../fields/URLField';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
+import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from '../documents/Documents';
-import { DragManager } from '../util/DragManager';
-import { CollectionDockingView, DockedFrameRenderer } from './collections/CollectionDockingView';
-import { ParentDocSelector } from './collections/ParentDocumentSelector';
-import './collections/ParentDocumentSelector.scss';
-import './PropertiesButtons.scss';
-import { MetadataEntryMenu } from './MetadataEntryMenu';
-import { DocumentView } from './nodes/DocumentView';
+import { DocumentType } from '../documents/DocumentTypes';
+import { SelectionManager } from '../util/SelectionManager';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
-import { TemplateMenu } from "./TemplateMenu";
-import { Template, Templates } from "./Templates";
+import './PropertiesButtons.scss';
import React = require("react");
-import { Tooltip } from '@material-ui/core';
-import { SelectionManager } from '../util/SelectionManager';
-import SharingManager from '../util/SharingManager';
-import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils';
-import { ImageField } from '../../fields/URLField';
-import { undoBatch, UndoManager } from '../util/UndoManager';
-import { DocumentType } from '../documents/DocumentTypes';
-import { InkField } from '../../fields/InkField';
-import { PresBox } from './nodes/PresBox';
+import { CollectionViewType } from './collections/CollectionView';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faLink);
-library.add(faTag);
-library.add(faTimes);
-library.add(faArrowAltCircleDown);
-library.add(faArrowAltCircleUp);
-library.add(faArrowAltCircleRight);
-library.add(faStopCircle);
-library.add(faCheckCircle);
-library.add(faCloudUploadAlt);
-library.add(faSyncAlt);
-library.add(faShare);
-library.add(faPhotoVideo);
const cloud: IconProp = "cloud-upload-alt";
const fetch: IconProp = "sync-alt";
@@ -58,7 +36,6 @@ enum UtilityButtonState {
@observer
export class PropertiesButtons extends React.Component<{}, {}> {
- private _dragRef = React.createRef<HTMLDivElement>();
private _pullAnimating = false;
private _pushAnimating = false;
private _pullColorAnimating = false;
@@ -76,13 +53,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
public static hasPulledHack = false;
+ @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
if (SelectionManager.SelectedDocuments().length) {
return SelectionManager.SelectedDocuments()[0];
- } else { return undefined; }
+ } else return undefined;
}
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
- @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; }
@computed get onClick() { return this.selectedDoc?.onClickBehavior ? this.selectedDoc?.onClickBehavior : "nothing"; }
@@ -167,11 +143,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-linker"
style={{ backgroundColor: this.pullColor }}
onPointerEnter={action(e => {
- if (e.altKey) {
- this.openHover = UtilityButtonState.OpenExternally;
- } else if (e.shiftKey) {
- this.openHover = UtilityButtonState.OpenRight;
- }
+ e.altKey && (this.openHover = UtilityButtonState.OpenExternally);
+ e.shiftKey && (this.openHover = UtilityButtonState.OpenRight);
})}
onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)}
onClick={async e => {
@@ -180,11 +153,11 @@ export class PropertiesButtons extends React.Component<{}, {}> {
e.preventDefault();
let googleDoc = await Cast(dataDoc.googleDoc, Doc);
if (!googleDoc) {
- const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false };
+ const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, useCors: false };
googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
dataDoc.googleDoc = googleDoc;
}
- CollectionDockingView.AddRightSplit(googleDoc);
+ CollectionDockingView.AddSplit(googleDoc, "right");
} else if (e.altKey) {
e.preventDefault();
window.open(googleDocUrl);
@@ -195,8 +168,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true);
}
}}>
- <FontAwesomeIcon className="documentdecorations-icon" size="lg"
- color="black"
+ <FontAwesomeIcon className="documentdecorations-icon" size="lg" color="black"
style={{ WebkitAnimation: animation, MozAnimation: animation }}
icon={(() => {
switch (this.openHover) {
@@ -212,248 +184,116 @@ export class PropertiesButtons extends React.Component<{}, {}> {
</div>
</Tooltip>;
}
+
+ @action @undoBatch
+ onLock = () => {
+ SelectionManager.SelectedDocuments().forEach(dv => dv.toggleLockPosition());
+ }
+
@computed
- get pinButton() {
+ get lockButton() {
const targetDoc = this.selectedDoc;
- const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
- return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
+ return !targetDoc ? (null) : <Tooltip
+ title={<div className="dash-tooltip">{`${this.selectedDoc?.lockedPosition ? "Unlock" : "Lock"} " Position"`}</div>} placement="top">
<div>
- <div className="propertiesButtons-linker"
- style={{ backgroundColor: isPinned ? "white" : "", color: isPinned ? "black" : "white" }}
- onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
- <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc.lockedPosition ? "on" : "off"}`} onPointerDown={this.onLock} >
+ <FontAwesomeIcon className="documentdecorations-icon" size="lg"
+ color={this.selectedDoc?.lockedPosition ? "black" : "white"}
+ icon={this.selectedDoc?.lockedPosition ? "unlock" : "lock"} />
</div>
-
<div className="propertiesButtons-title"
- // style={{
- // backgroundColor: Doc.isDocPinned(targetDoc) ? "white" : "black",
- // color: Doc.isDocPinned(targetDoc) ? "black" : "white"
- // }}
- >{Doc.isDocPinned(targetDoc) ? "Unpin" : "Pin"}</div>
+ >Position </div>
</div>
</Tooltip>;
}
@computed
- get pinWithViewButton() {
+ get downloadButton() {
const targetDoc = this.selectedDoc;
- if (targetDoc) {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- }
- return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with this view"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip
+ title={<div className="dash-tooltip">{"Download Document"}</div>} placement="top">
<div>
- <div className="propertiesButtons-linker"
- onClick={e => {
- if (targetDoc) {
- DockedFrameRenderer.PinDoc(targetDoc, false);
- const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeDoc.presPinView = true;
- activeDoc.presPinViewX = x;
- activeDoc.presPinViewY = y;
- activeDoc.presPinViewScale = scale;
- }
- }}>
- <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
- <div style={{ position: 'relative', fontSize: 25, fontWeight: 700, transform: 'translate(0, -28px)', color: 'rgba(250,250,250,0.55)' }}>V</div>
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={() => this.selectedDoc && Doc.Zip(this.selectedDoc)}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="download" size="lg" />
</div>
-
- <div className="propertiesButtons-title">{"View"}</div>
+ <div className="propertiesButtons-title"> downld </div>
</div>
</Tooltip>;
}
-
- @computed
- get metadataButton() {
- //const view0 = this.view0;
- if (this.selectedDoc) {
- return <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>} placement="top">
- <div className="propertiesButtons-linkFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP}
- content={<MetadataEntryMenu docs={[this.selectedDoc]} suggestWithFunction /> /* tfs: @bcz This might need to be the data document? */}>
- <div>
- <div className={"propertiesButtons-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
- {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" size="lg" />}
- </div>
- <div className="propertiesButtons-title">Metadata</div>
- </div>
- </Flyout>
- </div></Tooltip>;
- } else {
- return null;
- }
-
- }
-
- @observable _aliasDown = false;
- onAliasButtonDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
- }
@undoBatch
- onAliasButtonMoved = () => {
- if (this._dragRef.current) {
- const dragDocView = this.selectedDocumentView!;
- const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
- const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- dragData.dropAction = "alias";
- DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
- offsetX: dragData.offset[0],
- offsetY: dragData.offset[1],
- hideSource: false
- });
- return true;
- }
- return false;
- }
-
- @computed
- get templateButton() {
- const docView = this.selectedDocumentView;
- const templates: Map<Template, boolean> = new Map();
- const views = [this.selectedDocumentView];
- Array.from(Object.values(Templates.TemplateList)).map(template =>
- templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
- return !docView ? (null) :
- <Tooltip title={<><div className="dash-tooltip">Customize layout</div></>} placement="top">
- <div className="propertiesButtons-linkFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} //onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
- content={<TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
- <div>
- <div className={"propertiesButtons-linkButton-empty"} >
- {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="lg" />}
- </div>
- <div className="propertiesButtons-title">Layout</div>
- </div>
- </Flyout>
- </div></Tooltip>;
- }
-
- @undoBatch
- onCopy = () => {
- if (this.selectedDoc && this.selectedDocumentView) {
- // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
- // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
- // copy.y = NumCast(this.selectedDoc.y) + 30;
- // this.selectedDocumentView.props.addDocument?.(copy);
- const alias = Doc.MakeAlias(this.selectedDoc);
- alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
- alias.y = NumCast(this.selectedDoc.y) + 30;
- this.selectedDocumentView.props.addDocument?.(alias);
- }
+ @action
+ setDictation = () => {
+ SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._showAudio = dv.rootDoc._showAudio === !dv.rootDoc._showAudio);
}
@computed
- get copyButton() {
+ get dictationButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Tap or Drag to create an alias"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Dictation Controls"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- ref={this._dragRef}
- onPointerDown={this.onAliasButtonDown}
- onClick={this.onCopy}>
- {<FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showAudio ? "on" : "off"}`} onPointerDown={this.setDictation}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="microphone" size="lg" />
</div>
- <div className="propertiesButtons-title">Alias</div>
+ <div className="propertiesButtons-title"> Dictate </div>
</div>
</Tooltip>;
}
- @action @undoBatch
- onLock = () => {
- this.selectedDocumentView?.toggleLockPosition();
+
+ @undoBatch
+ @action
+ setTitle = () => {
+ SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._showTitle = dv.rootDoc._showTitle === undefined ? "title" : undefined);
}
@computed
- get lockButton() {
+ get titleButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{this.selectedDoc?.lockedPosition ?
- "Unlock Position" : "Lock Position"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Title Header"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "" }}
- onPointerDown={this.onLock} >
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"}
- icon={BoolCast(this.selectedDoc?.lockedPosition) ? "unlock" : "lock"} size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showTitle ? "on" : "off"}`} onPointerDown={this.setTitle}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="text-width" size="lg" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: BoolCast(this.selectedDoc?.lockedPosition) ? "white" : "black",
- // color: BoolCast(this.selectedDoc?.lockedPosition) ? "black" : "white"
- // }}
- >Position </div>
+ <div className="propertiesButtons-title"> Title </div>
</div>
</Tooltip>;
}
- @computed
- get downloadButton() {
- const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{"Download Document"}</div></>} placement="top">
- <div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={async () => {
- if (this.selectedDocumentView?.props.Document) {
- Doc.Zip(this.selectedDocumentView?.props.Document);
- }
- }}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="download" size="lg" />}
- </div>
- <div className="propertiesButtons-title"> downld </div>
- </div>
- </Tooltip>;
+ @undoBatch
+ @action
+ setCaption = () => {
+ SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._showCaption = dv.rootDoc._showCaption === undefined ? "caption" : undefined);
}
@computed
- get deleteButton() {
+ get captionButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">Close Document</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Caption Footer"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={this.deleteDocument}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="times" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showCaption ? "on" : "off"}`} onPointerDown={this.setCaption}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="closed-captioning" size="lg" />
</div>
- <div className="propertiesButtons-title"> close </div>
+ <div className="propertiesButtons-title"> Caption </div>
</div>
</Tooltip>;
}
@undoBatch
@action
- deleteDocument = () => {
- const selected = SelectionManager.SelectedDocuments().slice();
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
- this.selectedDoc && (this.selectedDoc.deleted = true);
- this.selectedDocumentView?.props.ContainingCollectionView?.removeDocument(this.selectedDocumentView?.props.Document);
- SelectionManager.DeselectAll();
+ setChrome = () => {
+ SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._chromeStatus = dv.rootDoc._chromeStatus === "disabled" ? "enabled" : "disabled");
}
@computed
- get sharingButton() {
+ get chromeButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{"Share Document"}</div></>} placement="top">
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Editing UI"}</div>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={() => {
- if (this.selectedDocumentView) {
- SharingManager.Instance.open(this.selectedDocumentView);
- }
- }}>
- {<FontAwesomeIcon className="propertiesButtons-icon"
- icon="users" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._chromeStatus === "enabled" ? "on" : "off"}`} onPointerDown={this.setChrome}>
+ <FontAwesomeIcon className="propertiesButtons-icon" icon="edit" size="lg" />
</div>
- <div className="propertiesButtons-title"> share </div>
+ <div className="propertiesButtons-title"> Controls </div>
</div>
</Tooltip>;
}
@@ -484,27 +324,31 @@ export class PropertiesButtons extends React.Component<{}, {}> {
handleOptionChange = (e: any) => {
const value = e.target.value;
this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value);
- if (value === "nothing") {
- this.selectedDocumentView?.noOnClick();
- } else if (value === "enterPortal") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.makeIntoPortal();
- } else if (value === "toggleDetail") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleDetail();
- } else if (value === "linkInPlace") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleFollowLink("inPlace", true, false);
- } else if (value === "linkOnRight") {
- this.selectedDocumentView?.noOnClick();
- this.selectedDocumentView?.toggleFollowLink("onRight", false, false);
- }
+
+ SelectionManager.SelectedDocuments().forEach(dv => {
+ if (value === "nothing") {
+ dv.noOnClick();
+ } else if (value === "enterPortal") {
+ dv.noOnClick();
+ dv.makeIntoPortal();
+ } else if (value === "toggleDetail") {
+ dv.noOnClick();
+ dv.toggleDetail();
+ } else if (value === "linkInPlace") {
+ dv.noOnClick();
+ dv.toggleFollowLink("inPlace", true, false);
+ } else if (value === "linkOnRight") {
+ dv.noOnClick();
+ dv.toggleFollowLink("add:right", false, false);
+ }
+ });
}
@undoBatch @action
editOnClickScript = () => {
if (this.selectedDoc) {
- DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick");
+ if (SelectionManager.SelectedDocuments().length) SelectionManager.SelectedDocuments().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, "onClick"));
+ else DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick");
}
}
@@ -563,13 +407,8 @@ export class PropertiesButtons extends React.Component<{}, {}> {
title={<><div className="dash-tooltip">{"Export to Google Photos"}</div></>} placement="top">
<div>
<div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={() => {
- if (this.selectedDocumentView) {
- GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDocumentView.Document }).then(console.log);
- }
- }}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- icon="cloud-upload-alt" size="lg" />}
+ onPointerDown={() => this.selectedDoc && GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDoc }).then(console.log)}>
+ {<FontAwesomeIcon className="documentdecorations-icon" icon="cloud-upload-alt" size="lg" />}
</div>
<div className="propertiesButtons-title"> google </div>
</div>
@@ -580,33 +419,30 @@ export class PropertiesButtons extends React.Component<{}, {}> {
get clustersButton() {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip
- title={<><div className="dash-tooltip">{this.selectedDoc?.useClusters ? "Stop Showing Clusters" : "Show Clusters"}</div></>} placement="top">
+ title={<><div className="dash-tooltip">{this.selectedDoc?._useClusters ? "Stop Showing Clusters" : "Show Clusters"}</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: this.selectedDoc?.useClusters ? "white" : "" }}
- onPointerDown={this.changeClusters}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={this.selectedDoc?.useClusters ? "black" : "white"}
- icon="braille" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._useClusters ? "on" : "off"}`} onPointerDown={this.changeClusters}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="braille" size="lg" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: this.selectedDoc?.useClusters ? "white" : "black",
- // color: this.selectedDoc?.useClusters ? "black" : "white"
- // }}
- > clusters </div>
+ <div className="propertiesButtons-title" > clusters </div>
</div>
</Tooltip>;
}
@action @undoBatch
changeFitToBox = () => {
- this.selectedDoc && (this.selectedDoc._fitToBox = !this.selectedDoc._fitToBox);
+ if (this.selectedDoc) {
+ if (SelectionManager.SelectedDocuments().length) SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._fitToBox = !dv.rootDoc._fitToBox);
+ else this.selectedDoc._fitToBox = !this.selectedDoc._fitToBox;
+ }
}
@action @undoBatch
changeClusters = () => {
- this.selectedDoc && (this.selectedDoc.useClusters = !this.selectedDoc.useClusters);
+ if (this.selectedDoc) {
+ if (SelectionManager.SelectedDocuments().length) SelectionManager.SelectedDocuments().forEach(dv => dv.rootDoc._useClusters = !dv.rootDoc._useClusters);
+ else this.selectedDoc._useClusters = !this.selectedDoc._useClusters;
+ }
}
@computed
@@ -615,19 +451,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? (null) : <Tooltip
title={<><div className="dash-tooltip">{this.selectedDoc?._fitToBox ? "Stop Fitting Content" : "Fit Content"}</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- style={{ backgroundColor: this.selectedDoc?._fitToBox ? "white" : "" }}
- onPointerDown={this.changeFitToBox}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color={this.selectedDoc?._fitToBox ? "black" : "white"}
- icon="expand" size="lg" />}
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._fitToBox ? "on" : "off"}`} onPointerDown={this.changeFitToBox}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="expand" size="lg" />
</div>
- <div className="propertiesButtons-title"
- // style={{
- // backgroundColor: this.selectedDoc?._fitToBox ? "white" : "black",
- // color: this.selectedDoc?._fitToBox ? "black" : "white"
- // }}
- > {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
+ <div className="propertiesButtons-title"> {this.selectedDoc?._fitToBox ? "unfit" : "fit"} </div>
</div>
</Tooltip>;
}
@@ -650,101 +477,45 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? (null) : <Tooltip
title={<><div className="dash-tooltip">Make Mask</div></>} placement="top">
<div>
- <div className={"propertiesButtons-linkButton-empty"}
- onPointerDown={this.makeMask}>
- {<FontAwesomeIcon className="documentdecorations-icon"
- color="white" icon="paint-brush" size="lg" />}
+ <div className={"propertiesButtons-linkButton-empty"} onPointerDown={this.makeMask}>
+ <FontAwesomeIcon className="documentdecorations-icon" color="white" icon="paint-brush" size="lg" />
</div>
<div className="propertiesButtons-title"> mask </div>
</div>
</Tooltip>;
}
- @computed
- get contextButton() {
- if (this.selectedDoc) {
- return <Tooltip title={<><div className="dash-tooltip">Show Context</div></>} placement="top">
- <div>
- <div className={"propertiesButtons-linkButton-empty"}>
- <ParentDocSelector Document={this.selectedDoc} addDocTab={(doc, where) => {
- where === "onRight" ? CollectionDockingView.AddRightSplit(doc) :
- this.selectedDocumentView?.props.addDocTab(doc, "onRight");
- return true;
- }} />
- </div>
- <div className="propertiesButtons-title"> context </div>
- </div>
- </Tooltip>;
- } else {
- return false;
- }
-
- }
-
- // @computed
- // get importButton() {
- // const targetDoc = this.selectedDoc;
- // return !targetDoc ? (null) : <Tooltip
- // title={<><div className="dash-tooltip">{"Import a Document"}</div></>}>
- // <div className={"propertiesButtons-linkButton-empty"}
- // onPointerDown={() => {
- // if (this.selectedDocumentView) {
- // CollectionFreeFormView.importDocument(100, 100);
- // }
- // }}>
- // {<FontAwesomeIcon className="documentdecorations-icon"
- // icon="upload" size="sm" />}
- // </div>
- // </Tooltip>;
- // }
-
-
render() {
if (!this.selectedDoc) return (null);
- const isText = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ const layoutField = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)];
+ const isText = layoutField instanceof RichTextField;
+ const isImage = layoutField instanceof ImageField;
+ const isInk = layoutField instanceof InkField;
+ const isCollection = this.selectedDoc.type === DocumentType.COL;
+ const isFreeForm = this.selectedDoc._viewType === CollectionViewType.Freeform;
const considerPull = isText && this.considerGoogleDocsPull;
const considerPush = isText && this.considerGoogleDocsPush;
- const isImage = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof ImageField;
- const isInk = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof InkField;
- const isCollection = this.selectedDoc.type === DocumentType.COL ? true : false;
- const isFreeForm = this.selectedDoc._viewType === "freeform" ? true : false;
- const hasContext = this.selectedDoc.context ? true : false;
- return <div><div className="propertiesButtons" style={{ paddingBottom: "5.5px" }}>
+ return <div className="propertiesButtons" style={{ paddingBottom: "5.5px" }}>
<div className="propertiesButtons-button">
- {this.templateButton}
+ {this.titleButton}
</div>
- {/* <div className="propertiesButtons-button">
- {this.metadataButton}
- </div> */}
<div className="propertiesButtons-button">
- {this.pinButton}
+ {this.captionButton}
</div>
- <div className="propertiesButtons-button">
- {this.pinWithViewButton}
- </div>
- <div className="propertiesButtons-button" style={{ display: hasContext ? "" : "none" }}>
- {this.copyButton}
+ <div className="propertiesButtons-button" style={{ display: isCollection ? "" : "none" }}>
+ {this.chromeButton}
</div>
<div className="propertiesButtons-button">
{this.lockButton}
</div>
- <div className="propertiesButtons-button">
- {this.downloadButton}
- </div>
- <div className="propertiesButtons-button">
- {this.deleteButton}
+ <div className="propertiesButtons-button" style={{ display: isText || isImage ? "" : "none" }}>
+ {this.dictationButton}
</div>
<div className="propertiesButtons-button">
{this.onClickButton}
</div>
- <div className="propertiesButtons-button">
- {this.sharingButton}
- </div>
- <div className="propertiesButtons-button">
- {this.contextButton}
- </div>
<div className="propertiesButtons-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
</div>
@@ -754,22 +525,16 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button" style={{ display: !isImage ? "none" : "" }}>
{this.googlePhotosButton}
</div>
- {/* <div className="propertiesButtons-button" style={{ display: !isCollection ? "none" : "" }}>
- {this.importButton}
- </div> */}
<div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
{this.clustersButton}
</div>
-
<div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
{this.fitContentButton}
</div>
-
<div className="propertiesButtons-button" style={{ display: !isInk ? "none" : "" }}>
{this.maskButton}
</div>
- </div>
</div>;
}
}
diff --git a/src/client/views/PropertiesDocContextSelector.scss b/src/client/views/PropertiesDocContextSelector.scss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/client/views/PropertiesDocContextSelector.scss
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
new file mode 100644
index 000000000..427748fe7
--- /dev/null
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -0,0 +1,58 @@
+import { IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Doc } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { NumCast, StrCast } from "../../fields/Types";
+import { CollectionViewType } from "./collections/CollectionView";
+import { CollectionDockingView } from "./collections/CollectionDockingView";
+import './PropertiesDocContextSelector.scss';
+import { SearchUtil } from "../util/SearchUtil";
+
+type PropertiesDocContextSelectorProps = {
+ Document: Doc,
+ Stack?: any,
+ hideTitle?: boolean,
+ addDocTab(doc: Doc, location: string): void
+};
+
+@observer
+export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
+ @observable private _docs: { col: Doc, target: Doc }[] = [];
+ @observable private _otherDocs: { col: Doc, target: Doc }[] = [];
+ _reaction: IReactionDisposer | undefined;
+
+ componentDidMount() { this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true }); }
+ componentWillUnmount() { this._reaction?.(); }
+ async fetchDocuments() {
+ const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document);
+ const containerProtoSets = await Promise.all(aliases.map(async alias => ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs)));
+ const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
+ const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => SearchUtil.GetAliasesOfDocument(container)));
+ const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
+ const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => SearchUtil.GetAliasesOfDocument(dp)));
+ const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>()).keys());
+ runInAction(() => {
+ this._docs = doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).filter(doc => !Doc.IsSystem(doc)).map(doc => ({ col: doc, target: this.props.Document }));
+ this._otherDocs = [];
+ });
+ }
+
+ getOnClick = (col: Doc, target: Doc) => {
+ col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
+ if (col._viewType === CollectionViewType.Freeform) {
+ col._panX = NumCast(target.x) + NumCast(target._width) / 2;
+ col._panY = NumCast(target.y) + NumCast(target._height) / 2;
+ }
+ this.props.addDocTab(col, "add:right");
+ }
+
+ render() {
+ return <div>
+ {this.props.hideTitle ? (null) : <p key="contexts">Contexts:</p>}
+ {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a></p>)}
+ {this._otherDocs.length ? <hr key="hr" /> : null}
+ {this._otherDocs.map(doc => <p key={"p" + doc.col[Id] + doc.target[Id]}><a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a></p>)}
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/PropertiesView.scss
index aee28366a..e5f9e0417 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -6,7 +6,7 @@
cursor: auto;
overflow-x: hidden;
- overflow-y: scroll;
+ overflow-y: auto;
.propertiesView-title {
background-color: rgb(159, 159, 159);
@@ -47,7 +47,7 @@
}
.propertiesView-settings {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
font-size: 12.5px;
font-weight: bold;
@@ -87,7 +87,7 @@
}
.propertiesView-sharing {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
.propertiesView-sharing-title {
@@ -121,6 +121,19 @@
padding: 10px;
margin-left: 5px;
+ .propertiesView-acls-checkbox {
+ float: right;
+ height: 20px;
+ margin-top: -20px;
+ margin-right: -15;
+
+ .propertiesView-acls-checkbox-text {
+ font-size: 7px;
+ margin-top: -10px;
+ margin-left: 6px;
+ }
+ }
+
.change-buttons {
display: flex;
@@ -137,7 +150,7 @@
}
.propertiesView-appearance {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
.propertiesView-appearance-title {
@@ -174,7 +187,7 @@
}
.propertiesView-transform {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
.propertiesView-transform-title {
@@ -258,7 +271,8 @@
// display: inline-table;
background-color: #ececec;
max-height: 130px;
- overflow-y: scroll;
+ overflow-y: auto;
+ width: 92%;
.propertiesView-sharingTable-item {
@@ -267,7 +281,6 @@
padding: 3px;
align-items: center;
border-bottom: 0.5px solid grey;
- cursor: pointer;
&:hover .propertiesView-sharingTable-item-name {
overflow-x: unset;
@@ -290,7 +303,6 @@
margin-left: auto;
.permissions-select {
- z-index: 1;
border: none;
background-color: inherit;
width: 75px;
@@ -310,7 +322,7 @@
}
.propertiesView-fields {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
.propertiesView-fields-title {
@@ -383,6 +395,41 @@
}
}
+ .propertiesView-contexts {
+
+ .propertiesView-contexts-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ &:hover {
+ cursor: pointer;
+ }
+
+ .propertiesView-contexts-title-icon {
+ float: right;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-contexts-content {
+ overflow: hidden;
+ padding: 10px;
+ }
+
+ }
+
.propertiesView-layout {
.propertiesView-layout-title {
@@ -419,7 +466,7 @@
}
.propertiesView-presTrails {
- border-bottom: 1px solid black;
+ //border-bottom: 1px solid black;
//padding: 8.5px;
.propertiesView-presTrails-title {
@@ -718,7 +765,9 @@
border: none;
padding: 6px;
padding-bottom: 2px;
-
+ background: #eeeeee;
+ border-top: 1px solid;
+ border-left: 1px solid;
&:hover {
border: 0.75px solid rgb(122, 28, 28);
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 57e968aa7..46f38795c 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,46 +1,40 @@
import React = require("react");
-import { observer } from "mobx-react";
-import "./PropertiesView.scss";
-import { observable, action, computed, runInAction } from "mobx";
-import { Doc, Field, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin, Opt, DocCastAsync } from "../../../../fields/Doc";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { EditableView } from "../../EditableView";
-import { KeyValueBox } from "../../nodes/KeyValueBox";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView";
-import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils";
-import { Id } from "../../../../fields/FieldSymbols";
-import { Transform } from "../../../util/Transform";
-import { PropertiesButtons } from "../../PropertiesButtons";
-import { SelectionManager } from "../../../util/SelectionManager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip, Checkbox } from "@material-ui/core";
-import SharingManager from "../../../util/SharingManager";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util";
-import { InkField } from "../../../../fields/InkField";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { Checkbox, Tooltip } from "@material-ui/core";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
-import "./FormatShapePane.scss";
-import { PresBox } from "../../nodes/PresBox";
-import { DocumentManager } from "../../../util/DocumentManager";
-import FormatShapePane from "./FormatShapePane";
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { InkField } from "../../fields/InkField";
+import { ComputedField } from "../../fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
+import { GetEffectiveAcl, SharingPermissions } from "../../fields/util";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../Utils";
+import { DocumentType } from "../documents/DocumentTypes";
+import { DocumentManager } from "../util/DocumentManager";
+import { SelectionManager } from "../util/SelectionManager";
+import { SharingManager } from "../util/SharingManager";
+import { Transform } from "../util/Transform";
+import { undoBatch, UndoManager } from "../util/UndoManager";
+import { CollectionDockingView } from "./collections/CollectionDockingView";
+import { EditableView } from "./EditableView";
+import { InkStrokeProperties } from "./InkStrokeProperties";
+import { ContentFittingDocumentView } from "./nodes/ContentFittingDocumentView";
+import { KeyValueBox } from "./nodes/KeyValueBox";
+import { PresBox } from "./nodes/PresBox";
+import { PropertiesButtons } from "./PropertiesButtons";
+import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
+import "./PropertiesView.scss";
+import { intersection } from "lodash";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
const _global = (window /* browser */ || global /* node */) as any;
-// import * as fa from '@fortawesome/free-solid-svg-icons';
-// import { library } from "@fortawesome/fontawesome-svg-core";
-
-// library.add(fa.faPlus, fa.faMinus, fa.faCog);
-
interface PropertiesViewProps {
width: number;
height: number;
- renderDepth: number;
- ScreenToLocalTransform: () => Transform;
- onDown: (event: any) => void;
}
@observer
@@ -49,30 +43,30 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get MAX_EMBED_HEIGHT() { return 200; }
+ @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else if (PresBox.Instance && PresBox.Instance._selectedArray.length) {
- return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
- } else { return undefined; }
+ if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
+ if (PresBox.Instance && PresBox.Instance._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
+ return undefined;
}
@computed get isPres(): boolean {
if (this.selectedDoc?.type === DocumentType.PRES) return true;
return false;
}
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
- @computed get dataDoc() { return this.selectedDocumentView?.dataDoc; }
+ @computed get dataDoc() { return this.selectedDoc?.[DataSym]; }
@observable layoutFields: boolean = false;
- @observable openActions: boolean = true;
+ @observable openOptions: boolean = true;
@observable openSharing: boolean = true;
@observable openFields: boolean = true;
@observable openLayout: boolean = true;
+ @observable openContexts: boolean = true;
@observable openAppearance: boolean = true;
@observable openTransform: boolean = true;
// @observable selectedUser: string = "";
// @observable addButtonPressed: boolean = false;
+ @observable layoutDocAcls: boolean = false;
//Pres Trails booleans:
@observable openPresTransitions: boolean = false;
@@ -80,7 +74,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@observable openAddSlide: boolean = false;
@observable openSlideOptions: boolean = false;
- @observable inActions: boolean = false;
+ @observable inOptions: boolean = false;
@observable _controlBtn: boolean = false;
@observable _lock: boolean = false;
@@ -135,24 +129,26 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get expandedField() {
if (this.dataDoc && this.selectedDoc) {
const ids: { [key: string]: string } = {};
- const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc;
- doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+ const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] :
+ SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? Doc.Layout(dv.layoutDoc) : dv.dataDoc);
+ docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
const rows: JSX.Element[] = [];
for (const key of Object.keys(ids).slice().sort()) {
- const contents = doc[key];
+ const docvals = new Set<any>();
+ docs.forEach(doc => docvals.add(doc[key]));
+ const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
if (key[0] === "#") {
rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "2px" }} key={key}>
<span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
&nbsp;
</div>);
} else {
- let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- contentElement = <EditableView key="editableView"
+ const contentElement = <EditableView key="editableView"
contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
height={13}
fontSize={10}
- GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+ GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
+ SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
/>;
rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
<span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
@@ -175,45 +171,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get noviceFields() {
- if (this.dataDoc && this.selectedDoc) {
+ if (this.dataDoc) {
const ids: { [key: string]: string } = {};
- const doc = this.dataDoc;
- doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+ const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => dv.dataDoc);
+ docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
const rows: JSX.Element[] = [];
- for (const key of Object.keys(ids).slice().sort()) {
- if ((key[0] === key[0].toUpperCase() && key.substring(0, 3) !== "ACL" && key !== "UseCors")
- || key[0] === "#" || key === "author" ||
- key === "creationDate" || key.indexOf("lastModified") !== -1) {
-
- const contents = doc[key];
- if (key[0] === "#") {
+ const noviceReqFields = ["author", "creationDate", "tags"];
+ const noviceLayoutFields = ["_curPage"];
+ const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL"))),
+ ...noviceReqFields, ...noviceLayoutFields];
+ for (const key of noviceKeys.sort()) {
+ const docvals = new Set<any>();
+ docs.forEach(doc => docvals.add(doc[key]));
+ const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
+ if (key[0] === "#") {
+ rows.push(<div className="uneditable-field" key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
+ &nbsp;
+ </div>);
+ } else if (contents !== undefined) {
+ const value = Field.toString(contents as Field);
+ if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) {
rows.push(<div className="uneditable-field" key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
- &nbsp;
- </div>);
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
+ <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
+ </div>);
} else {
- const value = Field.toString(contents as Field);
- if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) {
- rows.push(<div className="uneditable-field" key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
- <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
- </div>);
- } else {
- let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- contentElement = <EditableView key="editableView"
- contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
- height={13}
- fontSize={10}
- GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
- />;
-
- rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
- <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
- &nbsp;
- {contentElement}
- </div>);
- }
+ const contentElement = <EditableView key="editableView"
+ contents={value}
+ height={13}
+ fontSize={10}
+ GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
+ SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
+ />;
+
+ rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
+ <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
+ &nbsp;
+ {contentElement}
+ </div>);
}
}
}
@@ -232,18 +228,27 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@undoBatch
setKeyValue = (value: string) => {
- if (this.selectedDoc && this.dataDoc) {
- const doc = this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc;
+ const docs = SelectionManager.SelectedDocuments().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+ docs.forEach(doc => {
if (value.indexOf(":") !== -1) {
const newVal = value[0].toUpperCase() + value.substring(1, value.length);
- KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
+ const splits = newVal.split(":");
+ KeyValueBox.SetField(doc, splits[0], splits[1], true);
+ const tags = StrCast(doc.tags, ":");
+ if (tags.includes(`${splits[0]}:`) && splits[1] === "undefined") {
+ KeyValueBox.SetField(doc, "tags", `"${tags.replace(splits[0] + ":", "")}"`, true);
+ }
return true;
} else if (value[0] === "#") {
const newVal = value + `:'${value}'`;
- KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true);
+ doc[DataSym][value] = value;
+ const tags = StrCast(doc.tags, ":");
+ if (!tags.includes(`${value}:`)) {
+ doc[DataSym].tags = `${tags + value + ':'}`;
+ }
return true;
}
- }
+ });
return false;
}
@@ -257,8 +262,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
ref && observer.observe(ref);
}
+ @computed get contexts() {
+ return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
+ }
+
previewBackground = () => "lightgrey";
@computed get layoutPreview() {
+ if (SelectionManager.SelectedDocuments().length > 1) {
+ return "-- multiple selected --";
+ }
if (this.selectedDoc) {
const layoutDoc = Doc.Layout(this.selectedDoc);
const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
@@ -268,7 +280,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
Document={layoutDoc}
DataDoc={this.dataDoc}
LibraryPath={emptyPath}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={1}
rootSelected={returnFalse}
treeViewDoc={undefined}
backgroundColor={this.previewBackground}
@@ -283,6 +295,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
focus={returnFalse}
ScreenToLocalTransform={this.getTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
addDocument={returnFalse}
@@ -308,19 +321,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@undoBatch
changePermissions = (e: any, user: string) => {
- SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!);
+ const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.selectedDoc!] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
}
/**
* @returns the options for the permissions dropdown.
*/
getPermissionsSelect(user: string, permission: string) {
+ const dropdownValues: string[] = Object.values(SharingPermissions);
+ if (permission === "-multiple-") dropdownValues.unshift(permission);
return <select className="permissions-select"
value={permission}
onChange={e => this.changePermissions(e, user)}>
- {Object.values(SharingPermissions).map(permission => {
+ {dropdownValues.map(permission => {
return (
- <option key={permission} value={permission} selected={this.selectedDoc![`ACL-${user.replace(".", "_")}`] === permission}>
+ <option key={permission} value={permission}>
{permission}
</option>);
})}
@@ -344,8 +360,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get expansionIcon() {
return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
<div className="expansion-button" onPointerDown={() => {
- if (this.selectedDocumentView) {
- SharingManager.Instance.open(this.selectedDocumentView);
+ if (this.selectedDocumentView || this.selectedDoc) {
+ SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc);
}
}}>
<FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
@@ -356,7 +372,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
/**
* @returns a row of the permissions panel
*/
- sharingItem(name: string, effectiveAcl: symbol, permission: string) {
+ sharingItem(name: string, admin: boolean, permission: string) {
return <div className="propertiesView-sharingTable-item" key={name + permission}
// style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
// onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
@@ -364,7 +380,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "85px" : "80px" }}> {name} </div>
{/* {name !== "Me" ? this.notifyIcon : null} */}
<div className="propertiesView-sharingTable-item-permission">
- {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission}
+ {admin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission}
{permission === "Owner" ? this.expansionIcon : null}
</div>
</div>;
@@ -382,15 +398,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
[AclAdmin, SharingPermissions.Admin]
]);
- const effectiveAcl = GetEffectiveAcl(this.selectedDoc!);
+ // all selected docs
+ const docs = SelectionManager.SelectedDocuments().length < 2 ?
+ [this.layoutDocAcls ? this.selectedDoc! : this.selectedDoc![DataSym]]
+ : SelectionManager.SelectedDocuments().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
+
+ const target = docs[0];
+
+ // tslint:disable-next-line: no-unnecessary-callback-wrapper
+ const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
+ const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+
+ // users in common between all docs
+ const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym])));
+
const tableEntries = [];
// DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
- if (this.selectedDoc![AclSym]) {
- for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) {
+ if (commonKeys.length) {
+ for (const key of commonKeys) {
const name = key.substring(4).replace("_", ".");
- if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author/* && sidebarUsersDisplayed![name] !== false*/) {
- tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value)!));
+ const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]);
+ if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) {
+ tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-"));
}
}
}
@@ -402,9 +432,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
// }
// })
- // shifts the current user and the owner to the top of the doc.
- tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`])));
- if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), effectiveAcl, "Owner"));
+ // shifts the current user, owner, public to the top of the doc.
+ tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.every(doc => doc["ACL-Public"] === docs[0]["ACL-Public"]) ? (AclMap.get(target[AclSym]?.["ACL-Public"]) || SharingPermissions.None) : "-multiple-"));
+ tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-"));
+ if (Doc.CurrentUserEmail !== target.author && docs.every(doc => doc.author === docs[0].author)) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner"));
return <div className="propertiesView-sharingTable">
{tableEntries}
@@ -425,21 +456,27 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
@computed get editableTitle() {
+ const titles = new Set<string>();
+ SelectionManager.SelectedDocuments().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
+ const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title);
return <div className="editable-title"><EditableView
key="editableView"
- contents={StrCast(this.selectedDoc?.title)}
+ contents={title}
height={25}
fontSize={14}
- GetValue={() => StrCast(this.selectedDoc?.title)}
+ GetValue={() => title}
SetValue={this.setTitle} /> </div>;
}
@undoBatch
@action
setTitle = (value: string) => {
- if (this.dataDoc) {
- this.selectedDoc && (this.selectedDoc.title = value);
- KeyValueBox.SetField(this.dataDoc, "title", value, true);
+ if (SelectionManager.SelectedDocuments().length > 1) {
+ SelectionManager.SelectedDocuments().map(dv => Doc.SetInPlace(dv.rootDoc, "title", value, true));
+ return true;
+ } else if (this.dataDoc) {
+ if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, "title", value, true);
+ else KeyValueBox.SetField(this.dataDoc, "title", value, true);
return true;
}
return false;
@@ -496,15 +533,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed
get controlPointsButton() {
- return <div className="inking-button">
+ const formatInstance = InkStrokeProperties.Instance;
+ return !formatInstance ? (null) : <div className="inking-button">
<Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}>
- <div className="inking-button-points" onPointerDown={action(() => FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn)} style={{ backgroundColor: FormatShapePane.Instance._controlBtn ? "black" : "" }}>
+ <div className="inking-button-points" onPointerDown={action(() => formatInstance._controlBtn = !formatInstance._controlBtn)} style={{ backgroundColor: formatInstance._controlBtn ? "black" : "" }}>
<FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
</div>
</Tooltip>
- <Tooltip title={<div className="dash-tooltip">{FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}</div>}>
- <div className="inking-button-lock" onPointerDown={action(() => FormatShapePane.Instance._lock = !FormatShapePane.Instance._lock)} >
- <FontAwesomeIcon icon={FormatShapePane.Instance._lock ? "lock" : "unlock"} color="white" size="lg" />
+ <Tooltip title={<div className="dash-tooltip">{formatInstance._lock ? "Unlock ratio" : "Lock ratio"}</div>}>
+ <div className="inking-button-lock" onPointerDown={action(() => formatInstance._lock = !formatInstance._lock)} >
+ <FontAwesomeIcon icon={formatInstance._lock ? "lock" : "unlock"} color="white" size="lg" />
</div>
</Tooltip>
<Tooltip title={<div className="dash-tooltip">{"Rotate 90Ëš"}</div>}>
@@ -524,7 +562,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="inputBox-title"> {title} </div>
<input className="inputBox-input"
type="text" value={value}
- onChange={e => setter(e.target.value)} />
+ onChange={e => {
+ setter(e.target.value);
+ }}
+ onKeyPress={e => {
+ e.stopPropagation();
+ }} />
<div className="inputBox-button">
<div className="inputBox-button-up" key="up2"
onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
@@ -559,7 +602,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const oldX = NumCast(this.selectedDoc?.x);
const oldY = NumCast(this.selectedDoc?.y);
this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10));
- FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height)));
+ InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height)));
const doc = this.selectedDoc;
if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
const ink = Cast(doc.data, InkField)?.inkData;
@@ -581,7 +624,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const oX = NumCast(this.selectedDoc?.x);
const oY = NumCast(this.selectedDoc?.y);
this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10));
- FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width)));
+ InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width)));
const docu = this.selectedDoc;
if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) {
const ink = Cast(docu.data, InkField)?.inkData;
@@ -619,12 +662,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
set shapeWid(value) {
const oldWidth = NumCast(this.selectedDoc?._width);
this.selectedDoc && (this.selectedDoc._width = Number(value));
- FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth);
+ InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth);
}
set shapeHgt(value) {
const oldHeight = NumCast(this.selectedDoc?._height);
this.selectedDoc && (this.selectedDoc._height = Number(value));
- FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight);
+ InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight);
}
@computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); }
@@ -829,35 +872,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
if (this.selectedDoc && !this.isPres) {
return <div className="propertiesView" style={{
width: this.props.width,
+ minWidth: this.props.width
//overflowY: this.scrolling ? "scroll" : "visible"
}} >
<div className="propertiesView-title" style={{ width: this.props.width }}>
Properties
- {/* <div className="propertiesView-title-icon" onPointerDown={this.props.onDown}>
- <FontAwesomeIcon icon="times" color="black" size="sm" />
- </div> */}
</div>
<div className="propertiesView-name">
{this.editableTitle}
</div>
- <div className="propertiesView-settings" onPointerEnter={() => runInAction(() => { this.inActions = true; })}
- onPointerLeave={action(() => this.inActions = false)}>
+ <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
+ onPointerLeave={action(() => this.inOptions = false)}>
<div className="propertiesView-settings-title"
- onPointerDown={() => runInAction(() => { this.openActions = !this.openActions; })}
- style={{ backgroundColor: this.openActions ? "black" : "" }}>
- Actions
+ onPointerDown={action(() => this.openOptions = !this.openOptions)}
+ style={{ backgroundColor: this.openOptions ? "black" : "" }}>
+ Options
<div className="propertiesView-settings-title-icon">
- <FontAwesomeIcon icon={this.openActions ? "caret-down" : "caret-right"} size="lg" color="white" />
+ <FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
- {!this.openActions ? (null) :
+ {!this.openOptions ? (null) :
<div className="propertiesView-settings-content">
<PropertiesButtons />
</div>}
</div>
<div className="propertiesView-sharing">
<div className="propertiesView-sharing-title"
- onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
+ onPointerDown={action(() => this.openSharing = !this.openSharing)}
style={{ backgroundColor: this.openSharing ? "black" : "" }}>
Sharing {"&"} Permissions
<div className="propertiesView-sharing-title-icon">
@@ -903,7 +944,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{!this.isInk ? (null) :
<div className="propertiesView-appearance">
<div className="propertiesView-appearance-title"
- onPointerDown={() => runInAction(() => { this.openAppearance = !this.openAppearance; })}
+ onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
Appearance
<div className="propertiesView-appearance-title-icon">
@@ -918,7 +959,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{this.isInk ? <div className="propertiesView-transform">
<div className="propertiesView-transform-title"
- onPointerDown={() => runInAction(() => { this.openTransform = !this.openTransform; })}
+ onPointerDown={action(() => this.openTransform = !this.openTransform)}
style={{ backgroundColor: this.openTransform ? "black" : "" }}>
Transform
<div className="propertiesView-transform-title-icon">
@@ -932,7 +973,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-fields">
<div className="propertiesView-fields-title"
- onPointerDown={() => runInAction(() => { this.openFields = !this.openFields; })}
+ onPointerDown={action(() => this.openFields = !this.openFields)}
style={{ backgroundColor: this.openFields ? "black" : "" }}>
Fields {"&"} Tags
<div className="propertiesView-fields-title-icon">
@@ -948,12 +989,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{novice ? this.noviceFields : this.expandedField}
</div>}
</div>
+ <div className="propertiesView-contexts">
+ <div className="propertiesView-contexts-title"
+ onPointerDown={action(() => this.openContexts = !this.openContexts)}
+ style={{ backgroundColor: this.openContexts ? "black" : "" }}>
+ Contexts
+ <div className="propertiesView-contexts-title-icon">
+ <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openContexts ? <div className="propertiesView-contexts-content" >{this.contexts}</div> : null}
+ </div>
<div className="propertiesView-layout">
<div className="propertiesView-layout-title"
- onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}
+ onPointerDown={action(() => this.openLayout = !this.openLayout)}
style={{ backgroundColor: this.openLayout ? "black" : "" }}>
Layout
- <div className="propertiesView-layout-title-icon" onPointerDown={() => runInAction(() => { this.openLayout = !this.openLayout; })}>
+ <div className="propertiesView-layout-title-icon">
<FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
@@ -991,7 +1043,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>}
{!selectedItem ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
- onPointerDown={() => runInAction(() => { this.openPresProgressivize = !this.openPresProgressivize; })}
+ onPointerDown={action(() => { this.openPresProgressivize = !this.openPresProgressivize; })}
style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
&nbsp; <FontAwesomeIcon icon={"tasks"} /> &nbsp; Progressivize
<div className="propertiesView-presTrails-title-icon">
@@ -1004,7 +1056,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>}
{!selectedItem ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
- onPointerDown={() => runInAction(() => { this.openSlideOptions = !this.openSlideOptions; })}
+ onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
&nbsp; <FontAwesomeIcon icon={"cog"} /> &nbsp; {PresBox.Instance.stringType} options
<div className="propertiesView-presTrails-title-icon">
@@ -1017,7 +1069,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>}
<div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
- onPointerDown={() => runInAction(() => { this.openAddSlide = !this.openAddSlide; })}
+ onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })}
style={{ backgroundColor: this.openAddSlide ? "black" : "" }}>
&nbsp; <FontAwesomeIcon icon={"plus"} /> &nbsp; Add new slide
<div className="propertiesView-presTrails-title-icon">
@@ -1030,7 +1082,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>
{/* <div className="propertiesView-sharing">
<div className="propertiesView-sharing-title"
- onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })}
+ onPointerDown={acition(() => { this.openSharing = !this.openSharing; })}
style={{ backgroundColor: this.openSharing ? "black" : "" }}>
Sharing {"&"} Permissions
<div className="propertiesView-sharing-title-icon">
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index db087fb23..c598b2861 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -1,17 +1,12 @@
-import * as React from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
-import './ScriptingRepl.scss';
-import { Scripting, CompileScript, ts, Transformer } from '../util/Scripting';
+import * as React from 'react';
import { DocumentManager } from '../util/DocumentManager';
-import { OverlayView } from './OverlayView';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons';
+import { CompileScript, Scripting, Transformer, ts } from '../util/Scripting';
import { DocumentIconContainer } from './nodes/DocumentIcon';
-
-library.add(faCaretDown);
-library.add(faCaretRight);
+import { OverlayView } from './OverlayView';
+import './ScriptingRepl.scss';
@observer
export class ScriptingObjectDisplay extends React.Component<{ scrollToBottom: () => void, value: { [key: string]: any }, name?: string }> {
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index eb20fc257..870af03aa 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../fields/Doc";
import { Docs, DocUtils, } from "../documents/Documents";
import { StrCast, Cast } from "../../fields/Types";
import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils";
+import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../Utils";
import { Transform } from "../util/Transform";
import { ScriptField, ComputedField } from "../../fields/ScriptField";
import { Scripting } from "../util/Scripting";
@@ -116,9 +116,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
const addedTypes = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
const layout = Doc.Layout(firstDoc);
const templateMenu: Array<JSX.Element> = [];
- this.props.templates.forEach((checked, template) =>
- templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
- templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
+ //this.props.templates.forEach((checked, template) =>
+ // templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />));
+ //templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />);
templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
@@ -133,6 +133,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
rootSelected={returnFalse}
onCheckedClick={this.scriptField}
onChildClick={this.scriptField}
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 1b81c544a..e84022366 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -331,7 +331,7 @@ export class Keyframe extends React.Component<IProps> {
}),
TimelineMenu.Instance.addItem("button", "Show Data", action(() => {
const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 });
- CollectionDockingView.AddRightSplit(kvp, emptyPath);
+ CollectionDockingView.AddSplit(kvp, "right");
})),
TimelineMenu.Instance.addItem("button", "Delete", action(() => {
(this.regiondata.keyframes as List<Doc>).splice(this.keyframes.indexOf(kf), 1);
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index df828897e..1131ec874 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -83,14 +83,11 @@ export class Timeline extends React.Component<FieldViewProps> {
}
/////////lifecycle functions////////////
- componentWillMount() {
+ componentDidMount() {
const relativeHeight = window.innerHeight / 20; //sets height to arbitrary size, relative to innerHeight
this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max
this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset
this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset
- }
-
- componentDidMount() {
runInAction(() => {
if (!this.props.Document.AnimationLength) { //if animation length did not exist
this.props.Document.AnimationLength = this._time; //set it to default time
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 6ebd5103b..96f5afcd9 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,24 +1,5 @@
@import "../../views/globalCssVariables.scss";
-.miniMap {
- position: absolute;
- overflow: hidden;
- right: 10;
- bottom: 10;
- border: solid 1px;
- box-shadow: black 0.4vw 0.4vw 0.8vw;
-
- .miniOverlay {
- width: 100%;
- height: 100%;
- position: absolute;
-
- .miniThumb {
- background: #25252525;
- position: absolute;
- }
- }
-}
.lm_title {
margin-top: 3px;
@@ -69,19 +50,7 @@
}
.lm_popout {
- display: none;
-}
-
-.messageCounter {
- width: 18px;
- height: 20px;
- text-align: center;
- border-radius: 20px;
- margin-left: 5px;
- transform: translate(0px, -8px);
- display: inline-block;
- background: transparent;
- border: 1px #999999 solid;
+ display: inline;
}
.collectiondockingview-container {
@@ -101,7 +70,7 @@
margin: auto;
}
- .collectionDockingView-dragAsDocument {
+ .collectionDockingView-drag {
touch-action: none;
position: absolute;
padding-left: 5px;
@@ -119,6 +88,10 @@
transform: scale(1.2);
}
+ .lm_controls .lm_popout {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAUCAAAAABHICnvAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfkCBsXMgbrEyzaAAAAT0lEQVQY02NgIAcIu8tgEW3/u4IDQ5B14/8LQlhFhckVFfCJjIyIOfP/QWpEZGSQJFS05s9fIPj3/z+YmseCTxS7CZS7DI+PsYcOjpAkDAA6H0KZxzDzlgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMC0wOC0yN1QyMzo1MDowNi0wNDowMDvgVpQAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjAtMDgtMjdUMjM6NTA6MDYtMDQ6MDBKve4oAAAAAElFTkSuQmCC)
+ }
+
.lm_maximised .lm_controls .lm_maximise {
opacity: 1;
transform: scale(0.8);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 43da0d3cf..c891d2035 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,46 +1,32 @@
import 'golden-layout/src/css/goldenlayout-base.css';
import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, computed, Lambda, observable, reaction, runInAction, trace, IReactionDisposer } from "mobx";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as ReactDOM from 'react-dom';
import * as GoldenLayout from "../../../client/goldenLayout";
-import { DateField } from '../../../fields/DateField';
-import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
-import { FieldId } from "../../../fields/RefField";
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter, setupMoveUpEvents, returnFalse, emptyPath, aggregateBounds } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
-import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { DragManager } from "../../util/DragManager";
+import { InteractionUtils } from '../../util/InteractionUtils';
import { Scripting } from '../../util/Scripting';
-import { SelectionManager } from '../../util/SelectionManager';
-import { Transform } from '../../util/Transform';
-import { undoBatch } from "../../util/UndoManager";
-import { MainView } from '../MainView';
-import { DocumentView } from "../nodes/DocumentView";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import "./CollectionDockingView.scss";
-import { SubCollectionViewProps } from "./CollectionSubView";
-import { DockingViewButtonSelector } from './ParentDocumentSelector';
-import React = require("react");
+import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
import { CollectionViewType } from './CollectionView';
-import { SnappingManager } from '../../util/SnappingManager';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { listSpec } from '../../../fields/Schema';
-import { clamp } from 'lodash';
-import { PresBox } from '../nodes/PresBox';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { InteractionUtils } from '../../util/InteractionUtils';
-import { InkTool } from '../../../fields/InkField';
+import { TabDocView } from './TabDocView';
+import React = require("react");
const _global = (window /* browser */ || global /* node */) as any;
@observer
-export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
- @observable public static Instances: CollectionDockingView[] = [];
- @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
- public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
+export class CollectionDockingView extends CollectionSubView(doc => doc) {
+ @observable public static Instance: CollectionDockingView;
+ public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
return {
type: 'react-component',
component: 'DocumentFrameRenderer',
@@ -48,341 +34,212 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
width: width,
props: {
documentId: document[Id],
- libraryPath: libraryPath?.map(d => d[Id])
+ panelName // name of tab that can be used to close or replace its contents
}
};
}
- @computed public get initialized() {
- return this._goldenLayout !== null;
- }
-
- @observable private _goldenLayout: any = null;
+ private _reactionDisposer?: IReactionDisposer;
private _containerRef = React.createRef<HTMLDivElement>();
- private _flush: boolean = false;
+ private _flush: UndoManager.Batch | undefined;
private _ignoreStateChange = "";
- private _isPointerDown = false;
- private _maximizedSrc: Opt<DocumentView>;
+ public tabMap: Set<any> = new Set();
+ public get initialized() { return this._goldenLayout !== null; }
+ public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
+ @observable private _goldenLayout: any = null;
constructor(props: SubCollectionViewProps) {
super(props);
- runInAction(() => !CollectionDockingView.Instances ? CollectionDockingView.Instances = [this] : CollectionDockingView.Instances.push(this));
+ runInAction(() => CollectionDockingView.Instance = this);
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
DragManager.StartWindowDrag = this.StartOtherDrag;
}
+
public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
- let config: any;
- if (dragDocs.length === 1) {
- config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
- } else {
- config = {
- type: 'row',
- content: dragDocs.map((doc, i) => {
- CollectionDockingView.makeDocumentConfig(doc);
- })
- };
- }
- const div = document.createElement("div");
- const dragSource = this._goldenLayout.createDragSource(div, config);
- dragSource._dragListener.on("dragStop", () => {
- dragSource.destroy();
- });
+ !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag"));
+ const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
+ { type: 'row', content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) };
+ const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
+ //dragSource._dragListener.on("dragStop", dragSource.destroy);
dragSource._dragListener.onMouseDown(e);
}
@undoBatch
- @action
- public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
- if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
- return MainView.Instance.openWorkspace(docView.props.Document);
- }
- const document = Doc.MakeAlias(docView.props.Document);
- const newItemStackConfig = {
- type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
- };
- const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
- this._goldenLayout.root.contentItems[0].addChild(docconfig);
- docconfig.callDownwards('_$init');
- this._goldenLayout._$maximiseItem(docconfig);
- this._maximizedSrc = docView;
- this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
+ public CloseFullScreen = () => {
+ this._goldenLayout._maximisedItem?.toggleMaximise();
this.stateChanged();
- SelectionManager.DeselectAll();
}
- public CloseFullScreen = () => {
- const target = this._goldenLayout._maximisedItem;
- if (target !== null && this._maximizedSrc) {
- this._goldenLayout._maximisedItem.remove();
- SelectionManager.SelectDoc(this._maximizedSrc, false);
- this._maximizedSrc = undefined;
- this.stateChanged();
+ @undoBatch
+ public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
+ const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
+ if (tab) {
+ const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
+ if (j !== -1) {
+ tab.header.parent.contentItems[j].remove();
+ return CollectionDockingView.Instance.layoutChanged();
+ }
}
- }
- public HasFullScreen = () => {
- return this._goldenLayout._maximisedItem !== null;
+ return false;
}
@undoBatch
- @action
- public static CloseRightSplit(document: Opt<Doc>): boolean {
+ public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) {
const instance = CollectionDockingView.Instance;
- const tryClose = (childItem: any) => {
- if (childItem.config?.component === "DocumentFrameRenderer") {
- const docView = DocumentManager.Instance.getDocumentViewById(childItem.config.props.documentId);
- if (docView && ((!document && docView.Document.isDisplayPanel) || (document && Doc.AreProtosEqual(docView.props.Document, document)))) {
- childItem.remove();
- instance.layoutChanged(document);
- return true;
- }
- }
- return false;
+ if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
+ return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+ }
+ const newItemStackConfig = {
+ type: 'stack',
+ content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))]
};
- const retVal = !instance?._goldenLayout.root.contentItems[0].isRow ? false :
- Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
-
- retVal && instance.stateChanged();
- return retVal;
+ const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+ instance._goldenLayout.root.contentItems[0].addChild(docconfig);
+ docconfig.callDownwards('_$init');
+ instance._goldenLayout._$maximiseItem(docconfig);
+ instance._goldenLayout.emit('stateChanged');
+ instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
+ instance.stateChanged();
+ return true;
}
- @action
- layoutChanged(removed?: Doc) {
- this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
- this._goldenLayout.emit('stateChanged');
- this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
- if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
- this.stateChanged();
- }
@undoBatch
- @action
- public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
- if (!CollectionDockingView.Instance) return false;
+ public static ReplaceTab(document: Doc, panelName: string, stack: any, addToSplit?: boolean): boolean {
const instance = CollectionDockingView.Instance;
- let retVal = false;
- if (instance._goldenLayout.root.contentItems[0].isRow) {
- retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
- if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
- DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
- const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
- child.addChild(newItemStackConfig, undefined);
- !addToSplit && child.contentItems[0].remove();
- instance.layoutChanged(document);
- return true;
- }
- return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
- if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
- const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
- child.addChild(newItemStackConfig, undefined);
- !addToSplit && child.contentItems[j].remove();
- instance.layoutChanged(document);
- return true;
- }
- return false;
- });
- });
+ if (!instance) return false;
+ const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
+ if (!panelName && stack) {
+ const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config);
+ const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout);
+ stack.addChild(newContentItem.contentItems[0], undefined);
+ stack.contentItems[activeContentItemIndex].remove();
+ return CollectionDockingView.Instance.layoutChanged();
}
- if (retVal) {
- instance.stateChanged();
+ const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName);
+ if (tab) {
+ tab.header.parent.addChild(newConfig, undefined);
+ const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
+ !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove();
+ return CollectionDockingView.Instance.layoutChanged();
}
- return retVal;
+ return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
}
- //
- // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split
- //
- @undoBatch
- @action
- public static AddRightSplit(document: Doc, libraryPath?: Doc[]) {
- if (!CollectionDockingView.Instance) return false;
- const instance = CollectionDockingView.Instance;
- const newItemStackConfig = {
- type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
- };
-
- const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-
- if (instance._goldenLayout.root.contentItems.length === 0) {
- instance._goldenLayout.root.addChild(newContentItem);
- } else if (instance._goldenLayout.root.contentItems[0].isRow) {
- instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
- } else {
- const collayout = instance._goldenLayout.root.contentItems[0];
- const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
- collayout.parent.replaceChild(collayout, newRow);
- newRow.addChild(newContentItem, undefined, true);
- newRow.addChild(collayout, 0, true);
-
- collayout.config.width = 50;
- newContentItem.config.width = 50;
- }
- newContentItem.callDownwards('_$init');
- instance.layoutChanged();
- return true;
+ @undoBatch
+ public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) {
+ return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
+ CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
}
-
//
// Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
//
@undoBatch
- @action
- public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) {
- if (!CollectionDockingView.Instance) return false;
+ public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
+ if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
const instance = CollectionDockingView.Instance;
- const newItemStackConfig = {
- type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
- };
+ if (!instance) return false;
+ const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
- const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-
- if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
- instance._goldenLayout.root.addChild(newContentItem);
- } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
- if (pullSide === "left") {
- instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
- } else if (pullSide === "right") {
- instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
- } else if (pullSide === "top" || pullSide === "bottom") {
- // if not going in a row layout, must add already existing content into column
- const rowlayout = instance._goldenLayout.root.contentItems[0];
- const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
- rowlayout.parent.replaceChild(rowlayout, newColumn);
- if (pullSide === "top") {
- newColumn.addChild(rowlayout, undefined, true);
- newColumn.addChild(newContentItem, 0, true);
- } else if (pullSide === "bottom") {
- newColumn.addChild(newContentItem, undefined, true);
- newColumn.addChild(rowlayout, 0, true);
- }
+ if (!pullSide && stack) {
+ stack.addChild(docContentConfig, undefined);
+ } else {
+ const newItemStackConfig = { type: 'stack', content: [docContentConfig] };
+ const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+ if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
+ instance._goldenLayout.root.addChild(newContentItem);
+ } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
+ switch (pullSide) {
+ default:
+ case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+ case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+ case "top":
+ case "bottom":
+ // if not going in a row layout, must add already existing content into column
+ const rowlayout = instance._goldenLayout.root.contentItems[0];
+ const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
+ rowlayout.parent.replaceChild(rowlayout, newColumn);
+ if (pullSide === "top") {
+ newColumn.addChild(rowlayout, undefined, true);
+ newColumn.addChild(newContentItem, 0, true);
+ } else if (pullSide === "bottom") {
+ newColumn.addChild(newContentItem, undefined, true);
+ newColumn.addChild(rowlayout, 0, true);
+ }
- rowlayout.config.height = 50;
- newContentItem.config.height = 50;
- }
- } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
- if (pullSide === "top") {
- instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
- } else if (pullSide === "bottom") {
- instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
- } else if (pullSide === "left" || pullSide === "right") {
- // if not going in a row layout, must add already existing content into column
- const collayout = instance._goldenLayout.root.contentItems[0];
- const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
- collayout.parent.replaceChild(collayout, newRow);
-
- if (pullSide === "left") {
- newRow.addChild(collayout, undefined, true);
- newRow.addChild(newContentItem, 0, true);
- } else if (pullSide === "right") {
- newRow.addChild(newContentItem, undefined, true);
- newRow.addChild(collayout, 0, true);
+ rowlayout.config.height = 50;
+ newContentItem.config.height = 50;
}
+ } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
+ switch (pullSide) {
+ case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+ case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+ case "left":
+ case "right":
+ default:
+ // if not going in a row layout, must add already existing content into column
+ const collayout = instance._goldenLayout.root.contentItems[0];
+ const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+ collayout.parent.replaceChild(collayout, newRow);
+
+ if (pullSide === "left") {
+ newRow.addChild(collayout, undefined, true);
+ newRow.addChild(newContentItem, 0, true);
+ } else {
+ newRow.addChild(newContentItem, undefined, true);
+ newRow.addChild(collayout, 0, true);
+ }
- collayout.config.width = 50;
- newContentItem.config.width = 50;
- }
- }
-
- newContentItem.callDownwards('_$init');
- instance.layoutChanged();
- return true;
- }
-
-
- //
- // Creates a vertical split on the right side of the docking view, and then adds the Document to that split
- //
- @undoBatch
- @action
- public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
- document.isDisplayPanel = true;
- if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
- CollectionDockingView.AddRightSplit(document, libraryPath);
- }
- }
-
- @undoBatch
- @action
- public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
- Doc.GetProto(document).lastOpened = new DateField;
- const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
- if (stack === undefined) {
- let stack: any = this._goldenLayout.root;
- while (!stack.isStack) {
- if (stack.contentItems.length) {
- stack = stack.contentItems[0];
- } else {
- stack.addChild({ type: 'stack', content: [docContentConfig] });
- stack = undefined;
- break;
+ collayout.config.width = 50;
+ newContentItem.config.width = 50;
}
}
- if (stack) {
- stack.addChild(docContentConfig);
- }
- } else {
- stack.addChild(docContentConfig, undefined);
+ instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
+ newContentItem.callDownwards('_$init');
}
- this.layoutChanged();
- return true;
+
+ return instance.layoutChanged();
}
@undoBatch
@action
- public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
- Doc.GetProto(document).lastOpened = new DateField;
- const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
- if (stack === undefined) {
- let stack: any = this._goldenLayout.root;
- while (!stack.isStack) {
- if (stack.contentItems.length) {
- stack = stack.contentItems[0];
- } else {
- stack.addChild({ type: 'stack', content: [docContentConfig] });
- stack = undefined;
- break;
- }
- }
- if (stack) {
- stack.addChild(docContentConfig);
- }
- } else {
- stack.addChild(docContentConfig, undefined);
- }
- this.layoutChanged();
+ layoutChanged() {
+ this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
+ this._goldenLayout.emit('stateChanged');
+ this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
+ this.stateChanged();
return true;
}
- setupGoldenLayout() {
+ async setupGoldenLayout() {
const config = StrCast(this.props.Document.dockingConfig);
if (config) {
- if (!this._goldenLayout) {
- runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
- }
- else {
+ const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? [];
+ await Promise.all(docids.map(id => DocServer.GetRefField(id)));
+
+ if (this._goldenLayout) {
if (config === JSON.stringify(this._goldenLayout.toConfig())) {
return;
+ } else {
+ try {
+ this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+ this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ } catch (e) { }
}
- try {
- this._goldenLayout.unbind('itemDropped', this.itemDropped);
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
- this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- this._goldenLayout.unbind('stackCreated', this.stackCreated);
- } catch (e) { }
- this._goldenLayout.destroy();
- runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
}
- this._goldenLayout.on('itemDropped', this.itemDropped);
+ this.tabMap.clear();
+ this._goldenLayout?.destroy();
+ runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
this._goldenLayout.on('tabCreated', this.tabCreated);
this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
this._goldenLayout.on('stackCreated', this.stackCreated);
- this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer);
+ this._goldenLayout.registerComponent('DocumentFrameRenderer', TabDocView);
this._goldenLayout.container = this._containerRef.current;
if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
try {
@@ -394,582 +251,152 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._goldenLayout.init();
}
}
- reactionDisposer?: Lambda;
+
componentDidMount: () => void = () => {
if (this._containerRef.current) {
- const observer = new _global.ResizeObserver(action((entries: any) => {
- for (const entry of entries) {
- this.onResize(null as any);
- }
- }));
- observer.observe(this._containerRef.current);
- this.reactionDisposer = reaction(
- () => this.props.Document.dockingConfig,
- () => {
- if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) {
- // Because this is in a set timeout, if this component unmounts right after mounting,
- // we will leak a GoldenLayout, because we try to destroy it before we ever create it
- setTimeout(() => this.setupGoldenLayout(), 1);
- DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false);
- this.props.Document.workspaceBrush = true;
+ new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
+ this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig),
+ config => {
+ if (!this._goldenLayout || this._ignoreStateChange !== config) { // bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one.
+ this.setupGoldenLayout();
}
this._ignoreStateChange = "";
- }, { fireImmediately: true });
-
- window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
+ });
+ setTimeout(() => this.setupGoldenLayout(), 0);
+ //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
}
}
+
componentWillUnmount: () => void = () => {
try {
- this.props.Document.workspaceBrush = false;
- this._goldenLayout.unbind('itemDropped', this.itemDropped);
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- } catch (e) {
-
- }
- this._goldenLayout && this._goldenLayout.destroy();
- runInAction(() => {
- CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1);
- this._goldenLayout = null;
- });
+ } catch (e) { }
+ this._goldenLayout?.destroy();
window.removeEventListener('resize', this.onResize);
- this.reactionDisposer && this.reactionDisposer();
+ this._reactionDisposer?.();
}
+
@action
onResize = (event: any) => {
const cur = this._containerRef.current;
-
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
- this._goldenLayout?.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height);
+ cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
}
@action
- onPointerUp = (e: React.PointerEvent): void => {
+ onPointerUp = (e: MouseEvent): void => {
+ window.removeEventListener("pointerup", this.onPointerUp);
if (this._flush) {
- this._flush = false;
setTimeout(() => {
CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
this.stateChanged();
+ this._flush!.end();
+ this._flush = undefined;
}, 10);
}
}
+
@action
onPointerDown = (e: React.PointerEvent): void => {
- this._isPointerDown = true;
- const onPointerUp = action(() => {
- window.removeEventListener("pointerup", onPointerUp);
- this._isPointerDown = false;
- });
- window.addEventListener("pointerup", onPointerUp);
- const className = (e.target as any).className;
- if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") {
- this._flush = true;
+ window.addEventListener("mouseup", this.onPointerUp);
+ if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) {
+ this._flush = UndoManager.StartBatch("golden layout edit");
}
- if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
- return;
- } else {
+ if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
+ Doc.GetSelectedTool() !== InkTool.Highlighter && Doc.GetSelectedTool() !== InkTool.Pen) {
e.stopPropagation();
}
}
- updateDataField = async (json: string) => {
+ public static Copy(doc: Doc) {
+ let json = StrCast(doc.dockingConfig);
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
- const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+ const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || [];
+ const docs = docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
+ const newtabs = docs.map(doc => {
+ const copy = Doc.MakeAlias(doc);
+ json = json.replace(doc[Id], copy[Id]);
+ return copy;
+ });
+ const copy = Docs.Create.DockDocument(newtabs, json, { title: "Snapshot: " + doc.title });
+ const docsublists = DocListCast(doc.data);
+ const copysublists = DocListCast(copy.data);
+ const docother = Cast(docsublists[1], Doc, null);
+ const copyother = Cast(copysublists[1], Doc, null);
+ const newother = DocListCast(docother.data).map(doc => Doc.MakeAlias(doc));
+ Doc.GetProto(copyother).data = new List<Doc>(newother);
- if (docids) {
- const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
- docs.map(doc => Doc.AddDocToList(Doc.GetProto(this.props.Document), this.props.fieldKey, doc));
- // Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
- }
+ return copy;
}
- @undoBatch
+ @action
stateChanged = () => {
const json = JSON.stringify(this._goldenLayout.toConfig());
- this.props.Document.dockingConfig = json;
- this.updateDataField(json);
- }
-
- itemDropped = () => {
- CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
- this.stateChanged();
- }
-
- htmlToElement(html: string) {
- const template = document.createElement('template');
- html = html.trim(); // Never return a text node of whitespace as the result
- template.innerHTML = html;
- return template.content.firstChild;
- }
-
- tabCreated = async (tab: any) => {
- tab.titleElement[0].Tab = tab;
- if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
- if (tab.contentItem.config.fixed) {
- tab.contentItem.parent.config.fixed = true;
- }
+ const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
+ const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
- const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc;
- if (doc instanceof Doc) {
- tab.titleElement[0].onclick = (e: any) => {
- if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
- tab.titleElement[0].lastClick = Date.now();
- tab.titleElement[0].focus();
- };
- tab.titleElement[0].onchange = (e: any) => {
- tab.titleElement[0].size = e.currentTarget.value.length + 1;
- Doc.GetProto(doc).title = e.currentTarget.value, true;
- };
- tab.titleElement[0].size = StrCast(doc.title).length + 1;
- tab.titleElement[0].value = doc.title;
- tab.titleElement[0].style["max-width"] = "100px";
- const gearSpan = document.createElement("span");
- gearSpan.className = "collectionDockingView-gear";
- gearSpan.style.position = "relative";
- gearSpan.style.paddingLeft = "0px";
- gearSpan.style.paddingRight = "12px";
- const stack = tab.contentItem.parent;
- tab.element[0].onpointerdown = (e: any) => {
- const view = DocumentManager.Instance.getDocumentView(doc);
- view && SelectionManager.SelectDoc(view, false);
- };
- // shifts the focus to this tab when another tab is dragged over it
- tab.element[0].onmouseenter = (e: any) => {
- if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
- const activeContentItem = tab.header.parent.getActiveContentItem();
- if (tab.contentItem !== activeContentItem) {
- tab.header.parent.setActiveContentItem(tab.contentItem);
- }
- tab.setActive(true);
- };
- const onDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e) => {
- if (!(e as any).defaultPrevented) {
- const dragData = new DragManager.DocumentDragData([doc]);
- dragData.dropAction = doc.dropAction as dropActionType;
- DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
- return true;
- }
- return false;
- }, returnFalse, emptyFunction);
- };
-
- tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)),
- (views) => {
- if (views.length) {
- ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} >
- <DockingViewButtonSelector views={() => views} Stack={stack} />
- </span>,
- gearSpan);
- tab.buttonDisposer?.();
- }
- }, { fireImmediately: true });
-
- tab.reactComponents = [gearSpan];
- tab.element.append(gearSpan);
- tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
- tab.titleElement[0].textContent = title, { fireImmediately: true };
- tab.titleElement[0].style.padding = degree ? 0 : 2;
- tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
- });
- //TODO why can't this just be doc instead of the id?
- tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
- }
- }
- tab.closeElement.off('click') //unbind the current click handler
- .click(async function () {
- tab.reactionDisposer?.();
- tab.buttonDisposer?.();
- const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId);
- if (doc instanceof Doc) {
- const theDoc = doc;
- CollectionDockingView.Instance._removedDocs.push(theDoc);
-
- const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc);
- if (recent) {
- Doc.AddDocToList(recent, "data", doc, undefined, true, true);
- }
- SelectionManager.DeselectAll();
- }
- CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
- tab.contentItem.remove();
- CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
- });
+ this.props.Document.dockingConfig = json;
+ const sublists = DocListCast(this.props.Document[this.props.fieldKey]);
+ const tabs = Cast(sublists[0], Doc, null);
+ const other = Cast(sublists[1], Doc, null);
+ const tabdocs = DocListCast(tabs.data);
+ const otherdocs = DocListCast(other.data);
+ Doc.GetProto(tabs).data = new List<Doc>(docs);
+ const otherSet = new Set<Doc>();
+ otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+ Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
}
tabDestroyed = (tab: any) => {
- if (tab.reactComponents) {
- for (const ele of tab.reactComponents) {
- ReactDOM.unmountComponentAtNode(ele);
- }
- }
+ this.tabMap.delete(tab);
+ Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+ tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
+ }
+ tabCreated = (tab: any) => {
+ tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content)
}
- _removedDocs: Doc[] = [];
stackCreated = (stack: any) => {
- //stack.header.controlsContainer.find('.lm_popout').hide();
- stack.header.element.on('mousedown', (e: any) => {
- if (e.target === stack.header.element[0] && e.button === 1) {
- this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }));
+ stack.header?.element.on('mousedown', (e: any) => {
+ if (e.target === stack.header?.element[0] && e.button === 2) {
+ const emptyPane = CurrentUserUtils.EmptyPane;
+ emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], {
+ _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+ }), "", stack);
}
});
- // starter code for bezel to add new pane
- // stack.element.on("touchstart", (e: TouchEvent) => {
- // if (e.targetTouches.length === 2) {
- // let pt1 = e.targetTouches.item(0);
- // let pt2 = e.targetTouches.item(1);
- // let threshold = 40 * window.devicePixelRatio;
- // if (pt1 && pt2 && InteractionUtils.TwoPointEuclidist(pt1, pt2) < threshold) {
- // let edgeThreshold = 30 * window.devicePixelRatio;
- // let center = InteractionUtils.CenterPoint([pt1, pt2]);
- // let stackRect: DOMRect = stack.element.getBoundingClientRect();
- // let nearLeft = center.X - stackRect.x < edgeThreshold;
- // let nearTop = center.Y - stackRect.y < edgeThreshold;
- // let nearRight = stackRect.right - center.X < edgeThreshold;
- // let nearBottom = stackRect.bottom - center.Y < edgeThreshold;
- // let ns = [nearLeft, nearTop, nearRight, nearBottom].filter(n => n);
- // if (ns.length === 1) {
-
- // }
- // }
- // }
- // });
- stack.header.controlsContainer.find('.lm_close') //get the close icon
+ stack.header?.controlsContainer.find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
- .click(action(async function () {
+ .click(action(() => {
//if (confirm('really close this?')) {
-
stack.remove();
- stack.contentItems.forEach(async (contentItem: any) => {
- const doc = await DocServer.GetRefField(contentItem.config.props.documentId);
- if (doc instanceof Doc) {
- let recent: Doc | undefined;
- if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) {
- Doc.AddDocToList(recent, "data", doc, undefined, true, true);
- }
- const theDoc = doc;
- CollectionDockingView.Instance._removedDocs.push(theDoc);
- }
- });
- //}
+ stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true));
}));
- stack.header.controlsContainer.find('.lm_popout') //get the close icon
+ stack.header?.controlsContainer.find('.lm_popout') //get the close icon
.off('click') //unbind the current click handler
- .click(action(function () {
- stack.config.fixed = !stack.config.fixed;
- // const url = Utils.prepend("/doc/" + stack.contentItems[0].tab.contentItem.config.props.documentId);
- // let win = window.open(url, stack.contentItems[0].tab.title, "width=300,height=400");
+ .click(action(() => {
+ // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size
+ const emptyPane = CurrentUserUtils.EmptyPane;
+ emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], {
+ _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+ }), "", stack);
}));
}
render() {
- if (this.props.renderDepth > 0) {
- return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
- }
- return <div className="collectiondockingview-container" id="menuContainer"
- onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />;
- }
-
-}
-
-interface DockedFrameProps {
- documentId: FieldId;
- glContainer: any;
- libraryPath: (FieldId[]);
- //collectionDockingView: CollectionDockingView
-}
-@observer
-export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
- _mainCont: HTMLDivElement | null = null;
- @observable private _libraryPath: Doc[] = [];
- @observable private _panelWidth = 0;
- @observable private _panelHeight = 0;
- @observable private _document: Opt<Doc>;
- @observable private _isActive: boolean = false;
- _tabReaction: IReactionDisposer | undefined;
-
- get _stack(): any {
- return (this.props as any).glContainer.parent.parent;
- }
- get _tab(): any {
- const tab = (this.props as any).glContainer.tab?.element[0] as HTMLElement;
- return tab?.getElementsByClassName("lm_title")?.[0];
- }
- constructor(props: any) {
- super(props);
- DocServer.GetRefField(this.props.documentId).then(action((f: Opt<Field>) => this._document = f as Doc));
- this.props.libraryPath && this.setupLibraryPath();
- }
-
- async setupLibraryPath() {
- Promise.all(this.props.libraryPath.map(async docid => {
- const d = await DocServer.GetRefField(docid);
- return d instanceof Doc ? d : undefined;
- })).then(action((list: (Doc | undefined)[]) => this._libraryPath = list.filter(d => d).map(d => d as Doc)));
- }
-
- /**
- * Adds a document to the presentation view
- **/
- @undoBatch
- @action
- public static PinDoc(doc: Doc, unpin = false) {
- if (unpin) DockedFrameRenderer.UnpinDoc(doc);
- else {
- //add this new doc to props.Document
- const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
- if (curPres) {
- const pinDoc = Doc.MakeAlias(doc);
- pinDoc.presentationTargetDoc = doc;
- pinDoc.presZoomButton = true;
- pinDoc.context = curPres;
- Doc.AddDocToList(curPres, "data", pinDoc);
- if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
- if (!DocumentManager.Instance.getDocumentView(curPres)) {
- CollectionDockingView.AddRightSplit(curPres);
- }
- DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
- }
- }
- }
- /**
- * Adds a document to the presentation view
- **/
- @undoBatch
- @action
- public static UnpinDoc(doc: Doc) {
- //add this new doc to props.Document
- const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
- if (curPres) {
- const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
- ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
- }
- }
-
- componentDidMount() {
- const observer = new _global.ResizeObserver(action((entries: any) => {
- for (const entry of entries) {
- this._panelWidth = entry.contentRect.width;
- this._panelHeight = entry.contentRect.height;
- }
- }));
- observer.observe(this.props.glContainer._element[0]);
- this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
- this.props.glContainer.on("tab", this.onActiveContentItemChanged);
- this.onActiveContentItemChanged();
- this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white") }),
- (data) => {
- const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document));
- this._tab && (this._tab.style.backgroundColor = selected ? data.color : "");
- }
- );
- }
-
- componentWillUnmount() {
- this._tabReaction?.();
- this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
- this.props.glContainer.off("tab", this.onActiveContentItemChanged);
- }
-
- @action.bound
- private onActiveContentItemChanged() {
- if (this.props.glContainer.tab) {
- this._isActive = this.props.glContainer.tab.isActive;
- setTimeout(() => {
- const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document);
- dv && SelectionManager.SelectDoc(dv, false);
- });
- !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
- }
- }
-
- get layoutDoc() { return this._document && Doc.Layout(this._document); }
- nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
- panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
- (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
- panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
-
- nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
- nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
-
- contentScaling = () => {
- const nativeH = this.nativeHeight();
- const nativeW = this.nativeWidth();
- let scaling = 1;
- if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) {
- scaling = 1;
- } else if (NumCast(this.layoutDoc!._nativeWidth) && ((this.layoutDoc?._fitWidth) ||
- this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth))) {
- scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
- } else if (nativeW && nativeH) {
- // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) {
- // if ((this.layoutDoc?._fitWidth) ||
- // this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
- // return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
- // } else {
- // return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
- // }
- // }
- const wscale = this.panelWidth() / nativeW;
- scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
- } else scaling = 1;
- return scaling;
- }
-
- ScreenToLocalTransform = () => {
- if (this._mainCont && this._mainCont.children) {
- const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement);
- const scale = Utils.GetScreenTransform(this._mainCont).scale;
- return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
- }
- return Transform.Identity();
- }
- get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}%` : undefined; }
-
- addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
- SelectionManager.DeselectAll();
- if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
- return MainView.Instance.openWorkspace(doc);
- } else if (location === "onRight") {
- return CollectionDockingView.AddRightSplit(doc, libraryPath);
- } else if (location === "close") {
- return CollectionDockingView.CloseRightSplit(doc);
- } else if (location === "replace") {
- CollectionDockingView.UseRightSplit(doc);
- return true;
- } else {// if (location === "inPlace") {
- return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
- }
- }
-
- @computed get renderContentBounds() {
- const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
- const xbounds = bounds[2] - bounds[0];
- const ybounds = bounds[3] - bounds[1];
- const dim = Math.max(xbounds, ybounds);
- return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
- }
- @computed get miniLeft() { return 50 + (NumCast(this._document?._panX) - this.renderContentBounds.cx) / this.renderContentBounds.dim * 100 - this.miniWidth / 2; }
- @computed get miniTop() { return 50 + (NumCast(this._document?._panY) - this.renderContentBounds.cy) / this.renderContentBounds.dim * 100 - this.miniHeight / 2; }
- @computed get miniWidth() { return this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
- @computed get miniHeight() { return this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
- childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null);
- returnMiniSize = () => NumCast(this._document?._miniMapSize, 150);
- miniDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
- this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim);
- this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim);
- return false;
- }), emptyFunction, emptyFunction);
- }
- getCurrentFrame = (): number => {
- const presTargetDoc = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null);
- const currentFrame = Cast(presTargetDoc.currentFrame, "number", null);
- return currentFrame;
- }
-
- renderMiniMap() {
- return <div className="miniMap" style={{
- width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor,
- StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))),
- }}>
- <CollectionFreeFormView
- Document={this._document!}
- LibraryPath={emptyPath}
- CollectionView={undefined}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid havin to set stuff like this.
- noOverlay={true} // don't render overlay Docs since they won't scale
- active={returnTrue}
- select={emptyFunction}
- dropAction={undefined}
- isSelected={returnFalse}
- dontRegisterView={true}
- annotationsKey={""}
- fieldKey={Doc.LayoutFieldKey(this._document!)}
- bringToFront={emptyFunction}
- rootSelected={returnTrue}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- ContentScaling={returnOne}
- PanelWidth={this.returnMiniSize}
- PanelHeight={this.returnMiniSize}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- ScreenToLocalTransform={this.ScreenToLocalTransform}
- renderDepth={0}
- whenActiveChanged={emptyFunction}
- focus={emptyFunction}
- backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
- addDocTab={this.addDocTab}
- pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
- fitToBox={true}
- />
- <div className="miniOverlay" onPointerDown={this.miniDown} >
- <div className="miniThumb" style={{
- width: `${this.miniWidth}%`,
- height: `${this.miniHeight}%`,
- left: `${this.miniLeft}%`,
- top: `${this.miniTop}%`,
- }}
- />
- </div>
+ return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef}>
+ {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)}
</div>;
}
- @computed get docView() {
- TraceMobx();
- if (!this._document) return (null);
- const document = this._document;
- const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;
- return <>
- <DocumentView key={document[Id]}
- LibraryPath={this._libraryPath}
- Document={document}
- DataDoc={resolvedDataDoc}
- bringToFront={emptyFunction}
- rootSelected={returnTrue}
- addDocument={undefined}
- removeDocument={undefined}
- ContentScaling={this.contentScaling}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- NativeHeight={this.nativeHeight}
- NativeWidth={this.nativeWidth}
- ScreenToLocalTransform={this.ScreenToLocalTransform}
- renderDepth={0}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- focus={emptyFunction}
- backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
- addDocTab={this.addDocTab}
- pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
- </>;
- }
-
- render() {
- return (!this._isActive || !this.layoutDoc) ? (null) :
- (<div className="collectionDockingView-content" ref={ref => this._mainCont = ref}
- style={{
- transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
- height: this.layoutDoc && this.layoutDoc._fitWidth ? undefined : "100%",
- width: this.widthpercent
- }}>
- {this.docView}
- </div >);
- }
}
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); },
+
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddSplit(doc, "right"); },
"opens up the inputted document on the right side of the screen", "(doc: any)");
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
+Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/CollectionDockingViewMenu.scss
index bc9cf4848..4157f0f7e 100644
--- a/src/client/views/collections/ParentDocumentSelector.scss
+++ b/src/client/views/collections/CollectionDockingViewMenu.scss
@@ -1,15 +1,14 @@
-.parentDocumentSelector-linkFlyout {
+
+.dockingViewButtonSelector {
div {
overflow: visible !important;
}
- .metadataEntry-outerDiv {
- overflow: hidden !important;
- pointer-events: all;
- }
+ display: inline-block;
+ width:100%;
+ height:100%;
}
-
-.parentDocumentSelector-flyout {
+.dockingViewButtonSelector-flyout {
position: relative;
z-index: 9999;
background-color: #eeeeee;
@@ -32,32 +31,4 @@
border-right: 0px;
border-left: 0px;
}
-}
-
-.parentDocumentSelector-button {
- pointer-events: all;
- position: relative;
- display: inline-block;
-
- svg {
- // width:20px !important;
- //height:20px;
- }
-}
-
-.parentDocumentSelector-metadata {
- pointer-events: auto;
- padding-right: 5px;
- width: 25px;
- display: inline-block;
-}
-
-.buttonSelector {
- div {
- overflow: visible !important;
- }
-
- display: inline-block;
- width:100%;
- height:100%;
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingViewMenu.tsx b/src/client/views/collections/CollectionDockingViewMenu.tsx
new file mode 100644
index 000000000..1cab293a8
--- /dev/null
+++ b/src/client/views/collections/CollectionDockingViewMenu.tsx
@@ -0,0 +1,48 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { DocumentButtonBar } from "../DocumentButtonBar";
+import { DocumentView } from "../nodes/DocumentView";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+export class CollectionDockingViewMenu extends React.Component<{ views: () => DocumentView[], Stack: any }> {
+ customStylesheet(styles: any) {
+ return {
+ ...styles,
+ panel: {
+ ...styles.panel,
+ minWidth: "100px"
+ },
+ };
+ }
+ _ref = React.createRef<HTMLDivElement>();
+
+ @computed get flyout() {
+ return (
+ <div className="dockingViewButtonSelector-flyout" title=" " ref={this._ref}>
+ <DocumentButtonBar views={this.props.views} stack={this.props.Stack} />
+ </div>
+ );
+ }
+
+ @observable _tooltipOpen: boolean = false;
+ render() {
+ return <Tooltip open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
+ <span className="dockingViewButtonSelector"
+ onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}
+ onPointerDown={action(e => {
+ this.props.views()[0]?.select(false);
+ this._tooltipOpen = false;
+ })} >
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
+ <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
+ </Flyout>
+ </span>
+ </Tooltip >;
+ }
+}
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss
index f5c4299a9..ca72b98a5 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/CollectionLinearView.scss
@@ -4,10 +4,12 @@
.collectionLinearView-outer {
overflow: visible;
height: 100%;
+ pointer-events: none;
.collectionLinearView {
display: flex;
height: 100%;
+ align-items: center;
>span {
background: $dark-color;
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index e1b07077e..9eaa02bf8 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -17,6 +17,7 @@ import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup';
import { Tooltip } from '@material-ui/core';
+import { all } from 'bluebird';
type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -89,6 +90,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}
}
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
}
@action
@@ -112,12 +114,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
const backgroundColor = StrCast(this.props.Document.backgroundColor, "black");
const color = StrCast(this.props.Document.color, "white");
- const menuOpener = <label htmlFor={`${guid}`} style={{
- background: backgroundColor === color ? "black" : backgroundColor,
- // width: "18px", height: "18px", fontSize: "12.5px",
- // transition: this.props.Document.linearViewIsExpanded ? "transform 0.2s" : "transform 0.5s",
- // transform: this.props.Document.linearViewIsExpanded ? "" : "rotate(45deg)"
- }}
+ const menuOpener = <label htmlFor={`${guid}`} style={{ pointerEvents: "all", cursor: "default", background: backgroundColor === color ? "black" : backgroundColor, }}
onPointerDown={e => e.stopPropagation()} >
<p>{BoolCast(this.props.Document.linearViewIsExpanded) ? "–" : "+"}</p>
</label>;
@@ -139,6 +136,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
const scalable = pair.layout.onClick || pair.layout.onDragStart;
return <div className={`collectionLinearView-docBtn` + (scalable ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
style={{
+ pointerEvents: "all",
width: scalable ? (nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize) : undefined,
height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
}} >
@@ -166,6 +164,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
</div>;
@@ -192,9 +191,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
</span>
</Tooltip>
- {/* <FontAwesomeIcon icon="times-circle" size="lg" style={{ color: "red" }}
- onClick={this.exitLongLinks} /> */}
-
</span> : null}
</div>
</div>;
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index cfec3a6bc..1af1a05aa 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -69,7 +69,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
if (!this._initialLookupPending.get(id)) {
this._initialLookupPending.set(id, true);
setTimeout(() => {
- titleLoc && Doc.SetInPlace(doc, "title", titleLoc, true);
+ titleLoc && Doc.SetInPlace(doc, `${fieldKey}-address`, titleLoc, true);
this.respondToAddressChange(doc, fieldKey, address).then(() => this._initialLookupPending.delete(id));
});
}
@@ -114,12 +114,12 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
}
}
- private renderMarker = (layout: Doc) => {
- const location = this.getLocation(layout, Doc.LayoutFieldKey(layout));
+ private renderMarker = (layout: Doc, fieldKey?: string) => {
+ const location = this.getLocation(layout, fieldKey || Doc.LayoutFieldKey(layout));
return !location ? (null) :
<Marker
key={layout[Id]}
- label={StrCast(layout.title)}
+ label={StrCast(layout[`${this.props.fieldKey}-address`])}
position={location}
onClick={() => this.markerClick(layout, location)}
icon={this.renderMarkerIcon(layout)}
@@ -250,7 +250,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
}}
>
{this.reactiveContents}
- {mapLoc ? this.renderMarker(this.rootDoc) : undefined}
+ {mapLoc && StrCast(this.rootDoc[`${fieldKey}-mapCenter-address`]) ? this.renderMarker(this.rootDoc, `${fieldKey}-mapCenter`) : undefined}
</GeoMap>
</div>
</div>;
@@ -260,9 +260,10 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
export default GoogleApiWrapper({
apiKey: process.env.GOOGLE_MAPS!,
- LoadingContainer: () => (
- <div className={"loadingWrapper"}>
+ LoadingContainer: () => {
+ console.log(process.env.GOOGLE_MAPS);
+ return <div className={"loadingWrapper"}>
<img className={"loadingGif"} src={"/assets/loading.gif"} />
- </div>
- )
+ </div>;
+ }
})(CollectionMapView) as any; \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index c772dcfe7..1c96f69bf 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -140,7 +140,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
addDocument = (value: string, shiftDown?: boolean) => {
this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document._pivotField);
- const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value });
+ const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value });
newDoc[key] = this.getValue(this.props.heading);
const docs = this.props.parent.childDocList;
return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument(newDoc);
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 8658212b7..e36e5caa7 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -229,14 +229,18 @@
.flexLabel {
margin-bottom: 0;
}
- }
- .collectionGridViewChrome-entryBox {
- width: 50%;
+ .collectionGridViewChrome-entryBox {
+ width: 50%;
+ color: black;
+ }
+
+ .collectionGridViewChrome-columnButton {
+ color: black;
+ }
}
}
-
.collectionStackingViewChrome-sort,
.collectionTreeViewChrome-sort {
display: flex;
@@ -311,6 +315,59 @@
}
}
+.webBox-urlEditor {
+ position: relative;
+ opacity: 0.9;
+ z-index: 9001;
+ transition: top .5s;
+
+ .urlEditor {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ padding-bottom: 10px;
+ overflow: hidden;
+ margin-top: 5px;
+ height: 35px;
+
+ .editorBase {
+ display: flex;
+
+ .editor-collapse {
+ transition: all .5s, opacity 0.3s;
+ position: absolute;
+ width: 40px;
+ transform-origin: top left;
+ }
+
+ .switchToText {
+ color: $main-accent;
+ }
+
+ .switchToText:hover {
+ color: $dark-color;
+ }
+ }
+
+ button:hover {
+ transform: scale(1);
+ }
+ }
+}
+
+.webpage-urlInput {
+ padding: 12px 10px 11px 10px;
+ border: 0px;
+ color: black;
+ font-size: 10px;
+ letter-spacing: 2px;
+ outline-color: black;
+ background: rgb(238, 238, 238);
+ width: 100%;
+ min-width: 350px;
+ margin-right: 10px;
+ height: 100%;
+}
+
.collectionFreeFormMenu-cont {
display: inline-flex;
position: relative;
@@ -320,6 +377,7 @@
.antimodeMenu-button {
text-align: center;
display: block;
+ position: relative;
}
.color-previewI {
@@ -327,12 +385,15 @@
height: 20%;
bottom: 0;
position: absolute;
+ margin-left: 2px;
}
.color-previewII {
width: 80%;
height: 80%;
margin-left: 10%;
+ position: absolute;
+ bottom: 5;
}
.btn-group {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5119ff6c9..b04c9c2eb 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -5,45 +5,48 @@ import { Tooltip } from "@material-ui/core";
import { action, computed, Lambda, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { ColorState } from "react-color";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Document } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
-import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { WebField } from "../../../fields/URLField";
import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DragManager } from "../../util/DragManager";
+import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { undoBatch } from "../../util/UndoManager";
-import AntimodeMenu from "../AntimodeMenu";
+import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { EditableView } from "../EditableView";
-import GestureOverlay from "../GestureOverlay";
+import { GestureOverlay } from "../GestureOverlay";
import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
-import RichTextMenu from "../nodes/formattedText/RichTextMenu";
+import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
+import { PresBox } from "../nodes/PresBox";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
+import { TabDocView } from "./TabDocView";
@observer
-export default class CollectionMenu extends AntimodeMenu {
- static Instance: CollectionMenu;
+export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
+ @observable static Instance: CollectionMenu;
@observable SelectedCollection: DocumentView | undefined;
@observable FieldKey: string;
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
this.FieldKey = "";
- CollectionMenu.Instance = this;
+ runInAction(() => CollectionMenu.Instance = this);
this._canFade = false; // don't let the inking menu fade away
- this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true);
+ runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true));
this.jumpTo(300, 300);
}
@@ -130,7 +133,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
_contentCommand = {
params: ["target", "source"], title: "set content",
script: "getProto(self.target).data = copyField(self.source);",
- immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)), // Doc.aliasDocs(source),
+ immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
initialize: emptyFunction,
};
_onClickCommand = {
@@ -161,9 +164,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
};
_viewCommand = {
params: ["target"], title: "bookmark view",
- script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale'];",
- immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; }),
- initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; },
+ script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
+ immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = 0; }),
+ initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; },
};
_clusterCommand = {
params: ["target"], title: "fit content",
@@ -173,18 +176,22 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
};
_fitContentCommand = {
params: ["target"], title: "toggle clusters",
- script: "self.target.useClusters = !self.target.useClusters;",
- immediate: undoBatch((source: Doc[]) => this.target.useClusters = !this.target.useClusters),
+ script: "self.target._useClusters = !self.target._useClusters;",
+ immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters),
initialize: emptyFunction
};
_saveFilterCommand = {
params: ["target"], title: "save filter",
- script: "self.target._docFilters = copyField(self['target-docFilters']);",
- immediate: undoBatch((source: Doc[]) => this.target._docFilters = undefined),
- initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; },
+ script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']);
+ self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
+ immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
+ initialize: (button: Doc) => {
+ button['target-docFilters'] = Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters as any as ObjectField) : undefined;
+ button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
+ },
};
- @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+ @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
@computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
@computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
@computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
@@ -366,6 +373,92 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
}
else return false;
}
+ @computed
+ get pinButton() {
+ const targetDoc = this.selectedDoc;
+ const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
+ return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
+ <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderRight: "1px solid gray" }}
+ onClick={e => TabDocView.PinDoc(targetDoc, isPinned)}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
+ </button>
+ </Tooltip>;
+ }
+
+ @undoBatch
+ onAlias = () => {
+ if (this.selectedDoc && this.selectedDocumentView) {
+ // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
+ // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
+ // copy.y = NumCast(this.selectedDoc.y) + 30;
+ // this.selectedDocumentView.props.addDocument?.(copy);
+ const alias = Doc.MakeAlias(this.selectedDoc);
+ alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
+ alias.y = NumCast(this.selectedDoc.y) + 30;
+ this.selectedDocumentView.props.addDocument?.(alias);
+ }
+ }
+ private _dragRef = React.createRef<HTMLButtonElement>();
+
+ @observable _aliasDown = false;
+ onAliasButtonDown = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
+ }
+
+ @undoBatch
+ onAliasButtonMoved = (e: PointerEvent) => {
+ if (this._dragRef.current && this.selectedDoc) {
+ const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
+ const [left, top] = [e.clientX, e.clientY];
+ dragData.dropAction = "alias";
+ DragManager.StartDocumentDrag([this._dragRef.current], dragData, left, top, {
+ offsetX: dragData.offset[0],
+ offsetY: dragData.offset[1],
+ hideSource: false
+ });
+ return true;
+ }
+ return false;
+ }
+
+ @computed
+ get aliasButton() {
+ const targetDoc = this.selectedDoc;
+ return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
+ <button className="antimodeMenu-button" ref={this._dragRef} onPointerDown={this.onAliasButtonDown} onClick={this.onAlias}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />
+ </button>
+ </Tooltip>;
+ }
+
+ @computed
+ get pinWithViewButton() {
+ const targetDoc = this.selectedDoc;
+ if (targetDoc) {
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ }
+ return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin to presentation with current view"}</div></>} placement="top">
+ <button className="antidmodeMenu-button" style={{ borderRight: "1px solid gray" }}
+ onClick={e => {
+ if (targetDoc) {
+ TabDocView.PinDoc(targetDoc, false);
+ const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeDoc.presPinView = true;
+ activeDoc.presPinViewX = x;
+ activeDoc.presPinViewY = y;
+ activeDoc.presPinViewScale = scale;
+ }
+ }}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-marker" />
+ </button>
+ </Tooltip>;
+ }
+
render() {
return (
@@ -391,6 +484,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
<FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
</button>
</Tooltip>}
+ {this.aliasButton}
+ {this.pinButton}
+ {this.pinWithViewButton}
</div>
{this.subChrome}
</div>
@@ -429,7 +525,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get isText() {
if (this.selectedDoc) {
- return this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)] instanceof RichTextField;
+ const layoutField = Doc.LayoutField(this.selectedDoc);
+ return StrCast(layoutField).includes("FormattedText") ||
+ (layoutField instanceof Doc && StrCast(layoutField.layout).includes("FormattedText"));
}
else return false;
}
@@ -437,25 +535,25 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@undoBatch
@action
nextKeyframe = (): void => {
- const currentFrame = Cast(this.document.currentFrame, "number", null);
+ const currentFrame = Cast(this.document._currentFrame, "number", null);
if (currentFrame === undefined) {
- this.document.currentFrame = 0;
+ this.document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
- this.document.currentFrame = Math.max(0, (currentFrame || 0) + 1);
- this.document.lastFrame = Math.max(NumCast(this.document.currentFrame), NumCast(this.document.lastFrame));
+ this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
+ this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
}
@undoBatch
@action
prevKeyframe = (): void => {
- const currentFrame = Cast(this.document.currentFrame, "number", null);
+ const currentFrame = Cast(this.document._currentFrame, "number", null);
if (currentFrame === undefined) {
- this.document.currentFrame = 0;
+ this.document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
- this.document.currentFrame = Math.max(0, (currentFrame || 0) - 1);
+ this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
}
@undoBatch
@action
@@ -605,6 +703,141 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>;
}
+ onUrlDrop = (e: React.DragEvent) => {
+ const { dataTransfer } = e;
+ const html = dataTransfer.getData("text/html");
+ const uri = dataTransfer.getData("text/uri-list");
+ const url = uri || html || this._url;
+ const newurl = url.startsWith(window.location.origin) ?
+ url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+ this.submitURL(newurl);
+ e.stopPropagation();
+ }
+ onUrlDragover = (e: React.DragEvent) => {
+ e.preventDefault();
+ }
+
+ @computed get _url() {
+ return this.selectedDoc?.data instanceof WebField ? Cast(this.selectedDoc.data, WebField, null)?.url.toString() : Field.toString(this.selectedDoc?.data as Field);
+ }
+
+ set _url(value) {
+ if (this.selectedDoc) {
+ Doc.GetProto(this.selectedDoc).data = new WebField(value);
+ Doc.SetInPlace(this.selectedDoc, "title", value, true);
+ const annots = Doc.GetProto(this.selectedDoc)["data-annotations-" + this.urlHash(value)];
+ Doc.GetProto(this.selectedDoc)["data-annotations"] = annots instanceof ObjectField ? ObjectField.MakeCopy(annots) : new List<Doc>([]);
+ }
+ }
+
+ @action
+ submitURL = (url: string) => {
+ if (!url.startsWith("http")) url = "http://" + url;
+ try {
+ const selectedDoc = this.selectedDoc;
+ if (selectedDoc) {
+ const URLy = new URL(url);
+ const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
+ const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
+ const annos = DocListCast(selectedDoc["data-annotations"]);
+ if (Field.toString(selectedDoc.data as Field) === Field.toString(new WebField(URLy))) {
+ Doc.GetProto(selectedDoc).data = undefined;
+ setTimeout(action(() => Doc.GetProto(selectedDoc).data = new WebField(URLy)), 0);
+ } else {
+ if (url) {
+ Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(annos);
+ if (history === undefined) {
+ selectedDoc["data-history"] = new List<string>([this._url]);
+ } else {
+ history.push(this._url);
+ }
+ future && (future.length = 0);
+ }
+ this._url = url;
+ }
+ }
+ } catch (e) {
+ console.log("WebBox URL error:" + url);
+ }
+ }
+
+ urlHash(s: string) {
+ return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
+ }
+
+ onValueKeyDown = async (e: React.KeyboardEvent) => {
+ e.key === "Enter" && this.submitURL(this._keyInput.current!.value);
+ e.stopPropagation();
+ }
+
+ @action
+ forward = () => {
+ const selectedDoc = this.selectedDoc;
+ if (selectedDoc) {
+ const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
+ const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
+ if (future?.length) {
+ history?.push(this._url);
+ Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(selectedDoc["data-annotations"]));
+ const newurl = future.pop()!;
+ Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl));
+ Doc.GetProto(selectedDoc)["data-annotations"] = new List<Doc>(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)]));
+ }
+ }
+ }
+
+ @action
+ back = () => {
+ const selectedDoc = this.selectedDoc;
+ if (selectedDoc) {
+ const future = Cast(selectedDoc["data-future"], listSpec("string"), null);
+ const history = Cast(selectedDoc["data-history"], listSpec("string"), null);
+ if (history?.length) {
+ if (future === undefined) selectedDoc["data-future"] = new List<string>([this._url]);
+ else future.push(this._url);
+ Doc.GetProto(selectedDoc)["data-annotations-" + this.urlHash(this._url)] = new List<Doc>(DocListCast(selectedDoc["data-annotations"]));
+ const newurl = history.pop()!;
+ Doc.GetProto(selectedDoc).data = new WebField(new URL(this._url = newurl));
+ Doc.GetProto(selectedDoc)["data-annotations"] = new List<Doc>(DocListCast(selectedDoc["data-annotations-" + this.urlHash(newurl)]));
+ }
+ }
+ }
+
+ private _keyInput = React.createRef<HTMLInputElement>();
+
+ @computed get urlEditor() {
+ return (
+ <div className="webBox-buttons"
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover} style={{ display: "flex" }}>
+ <input className="webpage-urlInput" key={this._url}
+ placeholder="ENTER URL"
+ defaultValue={this._url}
+ onDrop={this.onUrlDrop}
+ onDragOver={this.onUrlDragover}
+ onKeyDown={this.onValueKeyDown}
+ onClick={(e) => {
+ this._keyInput.current!.select();
+ e.stopPropagation();
+ }}
+ ref={this._keyInput}
+ />
+ <div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between", maxWidth: "250px", }}>
+ <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
+ GO
+ </button>
+ <button className="submitUrl" onClick={this.back}>
+ <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
+ </button>
+ <button className="submitUrl" onClick={this.forward}>
+ <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
+ </button>
+ </div>
+ </div>
+ );
+ }
+
+
@observable viewType = this.selectedDoc?._viewType;
render() {
@@ -625,7 +858,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
{!this.isText && !this.props.isDoc ? <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
<div className="numKeyframe" style={{ color: this.document.editing ? "white" : "black", backgroundColor: this.document.editing ? "#5B9FDD" : "#AEDDF8" }}
onClick={action(() => this.document.editing = !this.document.editing)} >
- {NumCast(this.document.currentFrame)}
+ {NumCast(this.document._currentFrame)}
</div>
</Tooltip> : null}
{!this.isText && !this.props.isDoc ? <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
@@ -635,16 +868,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</Tooltip> : null}
{!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText || this.props.isDoc ? (null) :
- <Tooltip key="hypothesis" title={<div className="dash-tooltip">Use Hypothesis</div>} placement="bottom">
- <button className={"antimodeMenu-button"} key="hypothesis"
- style={{
- backgroundColor: !this.props.docView.layoutDoc.isAnnotating ? "121212" : undefined,
- borderRight: "1px solid gray"
- }}
- onClick={() => this.props.docView.layoutDoc.isAnnotating = !this.props.docView.layoutDoc.isAnnotating}>
- <FontAwesomeIcon icon={["fab", "hire-a-helper"]} size={"lg"} />
- </button>
- </Tooltip>
+ this.urlEditor
}
{!this.isText ?
<>
@@ -655,7 +879,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</> :
(null)
}
- {this.isText ? <RichTextMenu key="rich" /> : null}
+ {this.isText ? <RichTextMenu /> : null}
</div>;
}
}
@@ -677,15 +901,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
if (docs instanceof Doc) {
const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
+ (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 ||
- key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 ||
- key.indexOf("lastModified") >= 0 || (key[0].toUpperCase() === key[0] &&
- key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_"));
+ const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
+ key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
+ (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key[0] !== "_"));
return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
@@ -933,16 +1156,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
get numCols() { return NumCast(this.document.gridNumCols, 10); }
/**
- * Sets the value of `numCols` on the grid's Document to the value entered.
- */
- @undoBatch
- onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.key === "Enter" || e.key === "Tab") {
- if (e.currentTarget.valueAsNumber > 0) {
- this.document.gridNumCols = e.currentTarget.valueAsNumber;
- }
-
- }
+ * Sets the value of `numCols` on the grid's Document to the value entered.
+ */
+ onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
}
/**
@@ -980,9 +1197,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
*/
onDecrementButtonClick = () => {
this.clicked = true;
- if (!this.decrementLimitReached) {
+ if (this.numCols > 1 && !this.decrementLimitReached) {
this.entered && (this.document.gridNumCols as number)++;
undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
+ if (this.numCols === 1) this.decrementLimitReached = true;
}
this.entered = false;
}
@@ -1005,7 +1223,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
decrementValue = () => {
this.entered = true;
if (!this.clicked) {
- if (this.numCols !== 1) {
+ if (this.numCols > 1) {
this.document.gridNumCols = this.numCols - 1;
}
else {
@@ -1038,9 +1256,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
<span className="grid-icon">
<FontAwesomeIcon icon="columns" size="1x" />
</span>
- <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.numCols.toString()} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
- <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
- <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
+ <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+ <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
+ <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
</span>
{/* <span className="grid-control">
<span className="grid-icon">
@@ -1081,4 +1299,15 @@ export class CollectionGridViewChrome extends React.Component<CollectionMenuProp
</div>
);
}
-} \ No newline at end of file
+}
+Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
+ const dataField = doc[Doc.LayoutFieldKey(doc)];
+ const childDocs = DocListCast(dataField);
+ const currentFrame = Cast(doc._currentFrame, "number", null);
+ if (currentFrame === undefined) {
+ doc._currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
+ }
+ CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
+ doc._currentFrame = Math.max(0, newFrame);
+}); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 2e4055256..522f93b88 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -28,10 +28,8 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
}
this._originalChrome = StrCast(this.layoutDoc._chromeStatus);
this.layoutDoc._chromeStatus = "disabled";
- this.layoutDoc.hideFilterView = true;
}
componentWillUnmount() {
- this.layoutDoc.hideFilterView = false;
this.layoutDoc._chromeStatus = this._originalChrome;
}
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 20ce6b76d..18ae260e8 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -1,44 +1,38 @@
import React = require("react");
-import { action, observable, trace, computed, runInAction } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
+import DatePicker from "react-datepicker";
+import "react-datepicker/dist/react-datepicker.css";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath } from "../../../Utils";
+import { DateField } from "../../../fields/DateField";
import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
-import { KeyCodes } from "../../util/KeyCodes";
-import { SetupDrag, DragManager } from "../../util/DragManager";
-import { CompileScript } from "../../util/Scripting";
-import { Transform } from "../../util/Transform";
-import { MAX_ROW_HEIGHT, COLLECTION_BORDER_WIDTH } from '../globalCssVariables.scss';
-import '../DocumentDecorations.scss';
-import { EditableView } from "../EditableView";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import "./CollectionSchemaView.scss";
-import { CollectionView, Flyout } from "./CollectionView";
-import { NumCast, StrCast, BoolCast, FieldValue, Cast, DateCast } from "../../../fields/Types";
-import { Docs } from "../../documents/Documents";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faExpand } from '@fortawesome/free-solid-svg-icons';
+import { List } from "../../../fields/List";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { undoBatch } from "../../util/UndoManager";
-import { SnappingManager } from "../../util/SnappingManager";
import { ComputedField } from "../../../fields/ScriptField";
+import { BoolCast, Cast, DateCast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
import { ImageField } from "../../../fields/URLField";
-import { List } from "../../../fields/List";
-import { OverlayView } from "../OverlayView";
-import { DocumentIconContainer } from "../nodes/DocumentIcon";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import DatePicker from "react-datepicker";
-import "react-datepicker/dist/react-datepicker.css";
-import { DateField } from "../../../fields/DateField";
-import { RichTextField } from "../../../fields/RichTextField";
+import { Utils } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager } from "../../util/DragManager";
+import { KeyCodes } from "../../util/KeyCodes";
+import { CompileScript } from "../../util/Scripting";
import { SearchUtil } from "../../util/SearchUtil";
+import { SnappingManager } from "../../util/SnappingManager";
+import { undoBatch } from "../../util/UndoManager";
+import '../DocumentDecorations.scss';
+import { EditableView } from "../EditableView";
+import { MAX_ROW_HEIGHT } from '../globalCssVariables.scss';
+import { DocumentIconContainer } from "../nodes/DocumentIcon";
+import { OverlayView } from "../OverlayView";
+import "./CollectionSchemaView.scss";
+import { CollectionView } from "./CollectionView";
const path = require('path');
-library.add(faExpand);
-
export interface CellProps {
row: number;
col: number;
@@ -64,37 +58,24 @@ export interface CellProps {
@observer
export class CollectionSchemaCell extends React.Component<CellProps> {
+ public static resolvedFieldKey(column: string, rowDoc: Doc) {
+ const fieldKey = column;
+ if (fieldKey.startsWith("*")) {
+ const rootKey = fieldKey.substring(1);
+ const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
+ const matchedKeys = allKeys.filter(key => key.includes(rootKey));
+ if (matchedKeys.length) return matchedKeys[0];
+ }
+ return fieldKey;
+ }
@observable protected _isEditing: boolean = false;
protected _focusRef = React.createRef<HTMLDivElement>();
- protected _document = this.props.rowProps.original;
+ protected _rowDoc = this.props.rowProps.original;
protected _dropDisposer?: DragManager.DragDropDisposer;
-
- async componentWillMount() {
-
- }
-
- async componentDidMount() {
- document.addEventListener("keydown", this.onKeyDown);
- console.log("mounted");
- console.log(this.type);
- if (this.type === "context") {
- console.log("mounted2");
- const doc = Doc.GetProto(this.props.rowProps.original);
- const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
- if (aliasdoc.length > 0) {
- const targetContext = Cast(aliasdoc[0].context, Doc) as Doc;
- console.log(StrCast(targetContext.title));
- runInAction(() => this.contents = StrCast(targetContext.title));
- }
- }
-
- }
-
@observable contents: string = "";
- componentWillUnmount() {
- document.removeEventListener("keydown", this.onKeyDown);
- }
+ componentDidMount() { document.addEventListener("keydown", this.onKeyDown); }
+ componentWillUnmount() { document.removeEventListener("keydown", this.onKeyDown); }
@action
onKeyDown = (e: KeyboardEvent): void => {
@@ -116,7 +97,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
@action
onPointerDown = async (e: React.PointerEvent): Promise<void> => {
-
+ this.onItemDown(e);
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
this.props.setPreviewDoc(this.props.rowProps.original);
@@ -130,61 +111,39 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
} catch { }
}
- // this._isEditing = true;
- // this.props.setIsEditing(true);
-
- const field = this.props.rowProps.original[this.props.rowProps.column.id!];
- const doc = FieldValue(Cast(field, Doc));
- if (typeof field === "object" && doc) this.props.setPreviewDoc(doc);
+ const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
+ doc && this.props.setPreviewDoc(doc);
}
@undoBatch
applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) });
if (!res.success) return false;
- // doc[this.props.fieldKey] = res.result;
- // return true;
- doc[this.props.rowProps.column.id as string] = res.result;
+ doc[this.renderFieldKey] = res.result;
return true;
}
private drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData) {
- const fieldKey = this.props.rowProps.column.id as string;
if (de.complete.docDragData.draggedDocuments.length === 1) {
- this._document[fieldKey] = de.complete.docDragData.draggedDocuments[0];
+ this._rowDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
}
else {
const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {});
- this._document[fieldKey] = coll;
+ this._rowDoc[this.renderFieldKey] = coll;
}
e.stopPropagation();
}
}
protected dropRef = (ele: HTMLElement | null) => {
- this._dropDisposer && this._dropDisposer();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
- }
+ this._dropDisposer?.();
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
}
- // expandDoc = (e: React.PointerEvent) => {
- // let field = this.props.rowProps.original[this.props.rowProps.column.id as string];
- // let doc = FieldValue(Cast(field, Doc));
-
- // this.props.setPreviewDoc(doc!);
-
- // // this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
-
- // e.stopPropagation();
- // }
-
- returnHighlights(bing: (() => string), positions?: number[]) {
- const results = [];
- const contents = bing();
-
- if (positions !== undefined) {
+ returnHighlights(contents: string, positions?: number[]) {
+ if (positions) {
+ const results = [];
StrCast(this.props.Document._searchString);
const length = StrCast(this.props.Document._searchString).length;
const color = contents ? "black" : "grey";
@@ -199,71 +158,24 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
);
return results;
}
- else {
- return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>;
- }
+ return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>;
}
- type: string = "";
+ @computed get renderFieldKey() { return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); }
+ onItemDown = async (e: React.PointerEvent) => {
+ if (this.props.Document._searchDoc) {
+ const doc = Doc.GetProto(this._rowDoc);
+ const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
+ const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
+ DocumentManager.Instance.jumpToDocument(this._rowDoc, false, () => undefined, targetContext);
+ }
+ }
renderCellWithType(type: string | undefined) {
const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
- const props: FieldViewProps = {
- Document: this.props.rowProps.original,
- DataDoc: this.props.rowProps.original,
- LibraryPath: [],
- dropAction: "alias",
- bringToFront: emptyFunction,
- rootSelected: returnFalse,
- fieldKey: this.props.rowProps.column.id as string,
- docFilters: returnEmptyFilter,
- ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
- isSelected: returnFalse,
- select: emptyFunction,
- renderDepth: this.props.renderDepth + 1,
- ScreenToLocalTransform: Transform.Identity,
- focus: emptyFunction,
- active: returnFalse,
- whenActiveChanged: emptyFunction,
- PanelHeight: returnZero,
- PanelWidth: returnZero,
- NativeHeight: returnZero,
- NativeWidth: returnZero,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- ContentScaling: returnOne
- };
-
- let matchedKeys = [props.fieldKey];
- if (props.fieldKey.startsWith("*")) {
- const allKeys = Array.from(Object.keys(props.Document));
- allKeys.push(...Array.from(Object.keys(Doc.GetProto(props.Document))));
- matchedKeys = allKeys.filter(key => key.includes(props.fieldKey.substring(1)));
- }
- const fieldKey = matchedKeys.length ? matchedKeys[0] : props.fieldKey;
- const field = props.Document[fieldKey];
- const doc = FieldValue(Cast(field, Doc));
- const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
+ const fieldKey = this.renderFieldKey;
+ const field = this._rowDoc[fieldKey];
- const onItemDown = async (e: React.PointerEvent) => {
- if (this.props.Document._searchDoc !== undefined) {
- const doc = Doc.GetProto(this.props.rowProps.original);
- const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
- let targetContext = undefined;
- if (aliasdoc.length > 0) {
- targetContext = Cast(aliasdoc[0].context, Doc) as Doc;
- }
- DocumentManager.Instance.jumpToDocument(this.props.rowProps.original, false, undefined, targetContext);
- }
- else {
- fieldIsDoc &&
- SetupDrag(this._focusRef,
- () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
- this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument,
- this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
- }
- };
const onPointerEnter = (e: React.PointerEvent): void => {
if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
@@ -273,52 +185,24 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
dragRef.current!.className = "collectionSchemaView-cellContainer";
};
- let contents: any = "incorrect type";
- if (type === undefined) contents = <FieldView {...props} fieldKey={fieldKey} />;
- if (type === "number") contents = typeof field === "number" ? NumCast(field) : "--" + typeof field + "--";
- if (type === "string") contents = typeof field === "string" ? (StrCast(field) === "" ? "--" : StrCast(field)) : "--" + typeof field + "--";
- if (type === "boolean") contents = typeof field === "boolean" ? (BoolCast(field) ? "true" : "false") : "--" + typeof field + "--";
- if (type === "document") {
- const doc = FieldValue(Cast(field, Doc));
- contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
- }
- if (type === "image") {
- const image = FieldValue(Cast(field, ImageField));
- const doc = FieldValue(Cast(field, Doc));
- contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
- }
- if (type === "list") {
- contents = typeof field === "object" ? doc ? StrCast(field) === "" ? "--" : StrCast(field) : `--${typeof field}--` : `--${typeof field}--`;
- }
- if (type === "date") {
- contents = typeof field === "object" ? doc ? StrCast(field) === "" ? "--" : StrCast(field) : `--${typeof field}--` : `--${typeof field}--`;
- }
-
+ let contents = Field.toString(field as Field);
+ contents = contents === "" ? "--" : contents;
let className = "collectionSchemaView-cellWrapper";
if (this._isEditing) className += " editing";
if (this.props.isFocused && this.props.isEditable) className += " focused";
if (this.props.isFocused && !this.props.isEditable) className += " inactive";
-
- // let docExpander = (
- // <div className="collectionSchemaView-cellContents-docExpander" onPointerDown={this.expandDoc} >
- // <FontAwesomeIcon icon="expand" size="sm" />
- // </div>
- // );
const positions = [];
- let cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- this.type = props.fieldKey;
if (StrCast(this.props.Document._searchString).toLowerCase() !== "") {
- let term = Field.toString(cfield as Field);
- term = term.toLowerCase();
+ let term = (field instanceof Promise) ? "...promise pending..." : contents.toLowerCase();
const search = StrCast(this.props.Document._searchString).toLowerCase();
let start = term.indexOf(search);
let tally = 0;
if (start !== -1) {
positions.push(start);
}
- while (start < contents.length && start !== -1) {
+ while (start < contents?.length && start !== -1) {
term = term.slice(start + search.length + 1);
tally += start + search.length + 1;
start = term.indexOf(search);
@@ -328,179 +212,94 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
positions.pop();
}
}
- let search = false;
- if (this.props.Document._searchDoc !== undefined) {
- search = true;
- }
-
+ const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined";
return (
- <div className="collectionSchemaView-cellContainer" style={{ cursor: fieldIsDoc ? "grab" : "auto" }}
+ <div className="collectionSchemaView-cellContainer" style={{ cursor: field instanceof Doc ? "grab" : "auto" }}
ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
- <div className={className} ref={this._focusRef} onPointerDown={onItemDown} tabIndex={-1}>
- <div className="collectionSchemaView-cellContents"
- ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}>
- {!search ?
+ <div className={className} ref={this._focusRef} tabIndex={-1}>
+ <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null}>
+ {!this.props.Document._searchDoc ?
<EditableView
- positions={positions.length > 0 ? positions : undefined}
- search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined}
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
display={"inline"}
- contents={contents ? contents : type === "number" ? "0" : "undefined"}
- highlight={positions.length > 0 ? true : undefined}
- //contents={StrCast(contents)}
+ contents={contents}
height={"auto"}
maxHeight={Number(MAX_ROW_HEIGHT)}
- placeholder={"undefined"}
- bing={() => {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- if (cfield !== undefined) {
- // if (typeof(cfield)===RichTextField)
- const a = cfield as RichTextField;
- const b = cfield as DateField;
- console.log(b);
- if (a.Text !== undefined) {
- return (a.Text);
- }
- else if (b.toString() !== undefined) {
- return b.toString();
- }
- else if (StrCast(cfield)) {
- return StrCast(cfield);
- }
- else {
- return String(NumCast(cfield));
- }
- }
- }}
+ placeholder={placeholder}
GetValue={() => {
- if (type === "number" && (contents === 0 || contents === "0")) {
- return "0";
- } else {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- if (type === "number") {
- return StrCast(cfield);
- }
- const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
- const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1];
- const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
- Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
- return val;
-
- }
-
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
+ const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
+ const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1];
+ return cscript ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
+ Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
}}
SetValue={action((value: string) => {
let retVal = false;
-
- if (value.startsWith(":=")) {
- retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col);
+ if (value.startsWith(":=") || value.startsWith("=:=")) {
+ const script = value.substring(value.startsWith("=:=") ? 3 : 2);
+ retVal = this.props.setComputed(script, value.startsWith(":=") ? Doc.GetProto(this.props.Document) : this.props.Document, this.renderFieldKey, this.props.row, this.props.col);
} else {
const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- if (script.compiled) {
- retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run);
- }
-
+ script.compiled && (retVal = this.applyToDoc(this._rowDoc, this.props.row, this.props.col, script.run));
}
if (retVal) {
this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing'
this.props.setIsEditing(false);
}
return retVal;
-
- //return true;
})}
OnFillDown={async (value: string) => {
const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- if (script.compiled) {
- DocListCast(this.props.Document[this.props.fieldKey]).
- forEach((doc, i) => value.startsWith(":=") ?
- this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) :
- this.applyToDoc(doc, i, this.props.col, script.run));
- }
+ script.compiled && DocListCast(field).
+ forEach((doc, i) => value.startsWith(":=") ?
+ this.props.setComputed(value.substring(2), doc, this.renderFieldKey, i, this.props.col) :
+ this.applyToDoc(doc, i, this.props.col, script.run));
}}
/>
:
- this.returnHighlights(() => {
- console.log(props.fieldKey);
- const dateCheck: Date | undefined = this.props.rowProps.original[this.props.rowProps.column.id as string] instanceof DateField ? DateCast(this.props.rowProps.original[this.props.rowProps.column.id as string]).date : undefined;
- if (dateCheck !== undefined) {
- cfield = dateCheck.toLocaleString();
- }
- if (props.fieldKey === "context") {
- cfield = this.contents;
- console.log("this should work");
- }
- if (props.fieldKey === "*lastModified") {
- if (FieldValue(props.Document["data-lastModified"]) !== undefined) {
- const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["data-lastModified"])) as DateField;
- cfield = d.date.toLocaleString();
- }
-
- else if (FieldValue(props.Document["text-lastModified"]) !== undefined) {
- const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["text-lastModified"])) as DateField;
- cfield = d.date.toLocaleString();
- }
- }
- return Field.toString(cfield as Field);
- }, positions)
+ this.returnHighlights(contents, positions)
}
</div >
- {/* {fieldIsDoc ? docExpander : null} */}
</div>
</div>
);
}
- render() {
- return this.renderCellWithType(undefined);
- }
+ render() { return this.renderCellWithType(undefined); }
}
@observer
-export class CollectionSchemaNumberCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("number");
- }
-}
+export class CollectionSchemaNumberCell extends CollectionSchemaCell { render() { return this.renderCellWithType("number"); } }
@observer
-export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("boolean");
- }
-}
+export class CollectionSchemaBooleanCell extends CollectionSchemaCell { render() { return this.renderCellWithType("boolean"); } }
@observer
-export class CollectionSchemaStringCell extends CollectionSchemaCell {
- render() {
- return this.renderCellWithType("string");
- }
-}
+export class CollectionSchemaStringCell extends CollectionSchemaCell { render() { return this.renderCellWithType("string"); } }
@observer
export class CollectionSchemaDateCell extends CollectionSchemaCell {
- @observable private _date: Date = this.props.rowProps.original[this.props.rowProps.column.id as string] instanceof DateField ? DateCast(this.props.rowProps.original[this.props.rowProps.column.id as string]).date :
- this.props.rowProps.original[this.props.rowProps.column.id as string] instanceof Date ? this.props.rowProps.original[this.props.rowProps.column.id as string] : new Date();
+ @computed get _date(): Opt<DateField> { return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined; }
@action
handleChange = (date: any) => {
- this._date = date;
// const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
// if (script.compiled) {
// this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
// } else {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
- this._document[this.props.rowProps.column.id as string] = date as Date;
+ this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
//}
}
render() {
- return <DatePicker
- selected={this._date}
- onSelect={date => this.handleChange(date)}
- onChange={date => this.handleChange(date)}
- />;
+ return !this.props.isFocused ? <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : "--"}</span> :
+ <DatePicker
+ selected={this._date?.date || new Date}
+ onSelect={date => this.handleChange(date)}
+ onChange={date => this.handleChange(date)}
+ />;
}
}
@@ -509,44 +308,11 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
_overlayDisposer?: () => void;
- private prop: FieldViewProps = {
- Document: this.props.rowProps.original,
- DataDoc: this.props.rowProps.original,
- LibraryPath: [],
- dropAction: "alias",
- bringToFront: emptyFunction,
- rootSelected: returnFalse,
- fieldKey: this.props.rowProps.column.id as string,
- ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
- isSelected: returnFalse,
- select: emptyFunction,
- renderDepth: this.props.renderDepth + 1,
- ScreenToLocalTransform: Transform.Identity,
- focus: emptyFunction,
- active: returnFalse,
- whenActiveChanged: emptyFunction,
- PanelHeight: returnZero,
- PanelWidth: returnZero,
- NativeHeight: returnZero,
- NativeWidth: returnZero,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- ContentScaling: returnOne,
- docFilters: returnEmptyFilter
- };
- @observable private _field = this.prop.Document[this.prop.fieldKey];
- @observable private _doc = FieldValue(Cast(this._field, Doc));
- @observable private _docTitle = this._doc?.title;
- @observable private _preview = false;
- @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
- @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
- @computed get tableWidth() { return this.prop.PanelWidth() - 2 * this.borderWidth - 4 - this.previewWidth(); }
+ @computed get _doc() { return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc)); }
@action
onSetValue = (value: string) => {
- this._docTitle = value;
- //this.prop.Document[this.prop.fieldKey] = this._text;
+ this._doc && (Doc.GetProto(this._doc).title = value);
const script = CompileScript(value, {
addReturn: true,
@@ -556,46 +322,22 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
const results = script.compiled && script.run();
if (results && results.success) {
- this._doc = results.result;
- this._document[this.prop.fieldKey] = results.result;
- this._docTitle = this._doc?.title;
-
+ this._rowDoc[this.renderFieldKey] = results.result;
return true;
}
return false;
}
+ componentWillUnmount() { this.onBlur(); }
+
+ onBlur = () => { this._overlayDisposer?.(); };
onFocus = () => {
- this._overlayDisposer?.();
+ this.onBlur();
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
}
@action
- onOpenClick = () => {
- this._preview = false;
- if (this._doc) {
- this.props.addDocTab(this._doc, "onRight");
- return true;
- }
- return false;
- }
-
- @action
- showPreview = (bool: boolean, e: any) => {
- if (this._isEditing) {
- this._preview = false;
- } else {
- if (bool) {
- this.props.showDoc(this._doc, this.prop.DataDoc, e.clientX, e.clientY);
- } else {
- this.props.showDoc(undefined);
- }
- }
- }
-
- @action
- isEditingCalling = (isEditing: boolean): void => {
- this.showPreview(false, "");
+ isEditingCallback = (isEditing: boolean): void => {
document.removeEventListener("keydown", this.onKeyDown);
isEditing && document.addEventListener("keydown", this.onKeyDown);
this._isEditing = isEditing;
@@ -603,227 +345,90 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
}
- onDown = (e: any) => {
- this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
- this.props.setPreviewDoc(this.props.rowProps.original);
-
- let url: string;
- if (url = StrCast(this.props.rowProps.row.href)) {
- try {
- new URL(url);
- const temp = window.open(url)!;
- temp.blur();
- window.focus();
- } catch { }
- }
-
- const field = this.props.rowProps.original[this.props.rowProps.column.id!];
- const doc = FieldValue(Cast(field, Doc));
- if (typeof field === "object" && doc) this.props.setPreviewDoc(doc);
-
- this.showPreview(true, e);
-
- }
-
render() {
- if (typeof this._field === "object" && this._doc && this._docTitle) {
- return (
- <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1}
- onPointerDown={(e) => { this.onDown(e); }}
- onPointerEnter={(e) => { this.showPreview(true, e); }}
- onPointerLeave={(e) => { this.showPreview(false, e); }}
+ return !this._doc ? this.renderCellWithType("document") :
+ <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1}
+ onPointerDown={this.onPointerDown}
+ >
+ <div className="collectionSchemaView-cellContents-document"
+ style={{ padding: "5.9px" }}
+ ref={this.dropRef}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
>
-
- <div className="collectionSchemaView-cellContents-document"
- style={{ padding: "5.9px" }}
- ref={this.dropRef}
- onFocus={this.onFocus}
- onBlur={() => this._overlayDisposer?.()}
- >
-
- <EditableView
- editing={this._isEditing}
- isEditingCallback={this.isEditingCalling}
- display={"inline"}
- contents={this._docTitle}
- height={"auto"}
- maxHeight={Number(MAX_ROW_HEIGHT)}
- GetValue={() => {
- return StrCast(this._docTitle);
- }}
- SetValue={action((value: string) => {
- this.onSetValue(value);
- this.showPreview(false, "");
- return true;
- })}
- />
- </div >
- <div onClick={this.onOpenClick} className="collectionSchemaView-cellContents-docButton">
- <FontAwesomeIcon icon="external-link-alt" size="lg" ></FontAwesomeIcon> </div>
+ <EditableView
+ editing={this._isEditing}
+ isEditingCallback={this.isEditingCallback}
+ display={"inline"}
+ contents={this._doc.title || "--"}
+ height={"auto"}
+ maxHeight={Number(MAX_ROW_HEIGHT)}
+ GetValue={() => StrCast(this._doc?.title)}
+ SetValue={action((value: string) => {
+ this.onSetValue(value);
+ return true;
+ })}
+ />
+ </div >
+ <div onClick={() => this._doc && this.props.addDocTab(this._doc, "add:right")} className="collectionSchemaView-cellContents-docButton">
+ <FontAwesomeIcon icon="external-link-alt" size="lg" />
</div>
- );
- } else {
- return this.renderCellWithType("document");
- }
+ </div>;
}
}
@observer
export class CollectionSchemaImageCell extends CollectionSchemaCell {
- // render() {
- // return this.renderCellWithType("image");
- // }
- choosePath(url: URL, dataDoc: any) {
- const lower = url.href.toLowerCase();
- if (url.protocol === "data") {
- return url.href;
- } else if (url.href.indexOf(window.location.origin) === -1) {
- return Utils.CorsProxy(url.href);
- } else if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) {
- return url.href;//Why is this here
- }
+ choosePath(url: URL) {
+ if (url.protocol === "data") return url.href;
+ if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
+ if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here
+
const ext = path.extname(url.href);
- const _curSuffix = "_o";
- return url.href.replace(ext, _curSuffix + ext);
+ return url.href.replace(ext, "_o" + path.extname(url.href));
}
render() {
- const props: FieldViewProps = {
- Document: this.props.rowProps.original,
- DataDoc: this.props.rowProps.original,
- LibraryPath: [],
- dropAction: "alias",
- bringToFront: emptyFunction,
- rootSelected: returnFalse,
- fieldKey: this.props.rowProps.column.id as string,
- ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
- isSelected: returnFalse,
- select: emptyFunction,
- renderDepth: this.props.renderDepth + 1,
- ScreenToLocalTransform: Transform.Identity,
- focus: emptyFunction,
- active: returnFalse,
- whenActiveChanged: emptyFunction,
- PanelHeight: returnZero,
- PanelWidth: returnZero,
- NativeHeight: returnZero,
- NativeWidth: returnZero,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- ContentScaling: returnOne,
- docFilters: returnEmptyFilter
- };
-
- let image = true;
- let url = [];
- if (props.DataDoc) {
- const field = Cast(props.DataDoc[props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(props.DataDoc[props.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
- const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url, props.DataDoc)); // access the primary layout data of the alternate documents
- const paths = field ? [this.choosePath(field.url, props.DataDoc), ...altpaths] : altpaths;
- if (paths.length) {
- url = paths;
- } else {
- url = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- image = false;
- }
- //url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- } else {
- url = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- image = false;
- }
-
- const heightToWidth = NumCast(props.DataDoc?._nativeHeight) / NumCast(props.DataDoc?._nativeWidth);
- const height = this.props.rowProps.width * heightToWidth;
+ const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(this._rowDoc[this.renderFieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
+ const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+ const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
+ const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- if (props.fieldKey === "data") {
- if (url !== []) {
- const reference = React.createRef<HTMLDivElement>();
- return (
- <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
- <div className="collectionSchemaView-cellContents" key={this._document[Id]} ref={reference}>
- <img src={url[0]} width={image ? this.props.rowProps.width : "30px"}
- height={image ? height : "30px"} />
- </div >
- </div>
- );
+ const heightToWidth = NumCast(this._rowDoc._nativeHeight) / NumCast(this._rowDoc._nativeWidth);
+ let width = Math.min(75, this.props.rowProps.width);
+ const height = Math.min(75, width * heightToWidth);
+ width = height / heightToWidth;
- } else {
- return this.renderCellWithType("image");
- }
- } else {
- return this.renderCellWithType("image");
- }
+ const reference = React.createRef<HTMLDivElement>();
+ return <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+ <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+ <img src={url[0]}
+ width={paths.length ? width : "20px"}
+ height={paths.length ? height : "20px"} />
+ </div >
+ </div>;
}
}
-
-
-
@observer
export class CollectionSchemaListCell extends CollectionSchemaCell {
-
_overlayDisposer?: () => void;
- private prop: FieldViewProps = {
- Document: this.props.rowProps.original,
- DataDoc: this.props.rowProps.original,
- LibraryPath: [],
- dropAction: "alias",
- bringToFront: emptyFunction,
- rootSelected: returnFalse,
- fieldKey: this.props.rowProps.column.id as string,
- ContainingCollectionView: this.props.CollectionView,
- ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
- isSelected: returnFalse,
- select: emptyFunction,
- renderDepth: this.props.renderDepth + 1,
- ScreenToLocalTransform: Transform.Identity,
- focus: emptyFunction,
- active: returnFalse,
- whenActiveChanged: emptyFunction,
- PanelHeight: returnZero,
- PanelWidth: returnZero,
- NativeHeight: returnZero,
- NativeWidth: returnZero,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- ContentScaling: returnOne,
- docFilters: returnEmptyFilter
- };
- @observable private _field = this.prop.Document[this.prop.fieldKey];
- @observable private _optionsList = this._field as List<any>;
+ @computed get _field() { return this._rowDoc[this.renderFieldKey]; }
+ @computed get _optionsList() { return this._field as List<any>; }
@observable private _opened = false;
@observable private _text = "select an item";
@observable private _selectedNum = 0;
@action
- toggleOpened(open: boolean) {
- this._opened = open;
- }
-
- // @action
- // onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
- // this._text = e.target.value;
-
- // // change if its a document
- // this._optionsList[this._selectedNum] = this._text;
- // }
-
- @action
onSetValue = (value: string) => {
-
-
- this._text = value;
-
// change if its a document
- this._optionsList[this._selectedNum] = this._text;
-
- (this.prop.Document[this.prop.fieldKey] as List<any>).splice(this._selectedNum, 1, value);
+ this._optionsList[this._selectedNum] = this._text = value;
+ (this._field as List<any>).splice(this._selectedNum, 1, value);
}
@action
@@ -837,55 +442,23 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
}
-
render() {
-
- const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
-
- let type = "list";
-
- let link = false;
- let doc = false;
+ const link = false;
const reference = React.createRef<HTMLDivElement>();
- if (typeof this._field === "object" && this._optionsList[0]) {
-
- const options = this._optionsList.map((element, index) => {
-
- if (element instanceof Doc) {
- doc = true;
- type = "document";
- if (this.prop.fieldKey.toLowerCase() === "links") {
- link = true;
- type = "link";
- }
- const document = FieldValue(Cast(element, Doc));
- const title = element.title;
- return <div
- className="collectionSchemaView-dropdownOption"
- onPointerDown={(e) => { this.onSelected(StrCast(element.title), index); }}
- style={{ padding: "6px" }}>
- {title}
- </div>;
-
- } else {
- return <div
- className="collectionSchemaView-dropdownOption"
- onPointerDown={(e) => { this.onSelected(StrCast(element), index); }}
- style={{ padding: "6px" }}>{element}</div>;
- }
- });
+ if (this._optionsList?.length) {
+ const options = !this._opened ? (null) :
+ <div>
+ {this._optionsList.map((element, index) => {
+ const val = Field.toString(element);
+ return <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: "6px" }} onPointerDown={(e) => this.onSelected(StrCast(element), index)} >
+ {val}
+ </div>;
+ })}
+ </div>;
const plainText = <div style={{ padding: "5.9px" }}>{this._text}</div>;
- // const textarea = <textarea onChange={this.onChange} value={this._text}
- // onFocus={doc ? this.onFocus : undefined}
- // onBlur={doc ? e => this._overlayDisposer?.() : undefined}
- // style={{ resize: "none" }}
- // placeholder={"select an item"}></textarea>;
-
- const textarea = <div className="collectionSchemaView-cellContents"
- style={{ padding: "5.9px" }}
- ref={type === undefined || type === "document" ? this.dropRef : null} key={this.prop.Document[Id]}>
+ const textarea = <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: "5.9px" }} ref={this.dropRef} >
<EditableView
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
@@ -893,11 +466,8 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
contents={this._text}
height={"auto"}
maxHeight={Number(MAX_ROW_HEIGHT)}
- GetValue={() => {
- return this._text;
- }}
+ GetValue={() => this._text}
SetValue={action((value: string) => {
-
// add special for params
this.onSetValue(value);
return true;
@@ -906,61 +476,35 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
</div >;
//☰
-
- const dropdown = <div>
- {options}
- </div>;
-
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
- <div className="collectionSchemaView-cellContents" key={this._document[Id]} ref={reference}>
+ <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
<div className="collectionSchemaView-dropDownWrapper">
- <button type="button" className="collectionSchemaView-dropdownButton" onClick={(e) => { this.toggleOpened(!this._opened); }}
- style={{ right: "length", position: "relative" }}>
- {this._opened ? <FontAwesomeIcon icon="caret-up" size="lg" ></FontAwesomeIcon>
- : <FontAwesomeIcon icon="caret-down" size="lg" ></FontAwesomeIcon>}
+ <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: "length", position: "relative" }}
+ onClick={action(e => this._opened = !this._opened)} >
+ <FontAwesomeIcon icon={this._opened ? "caret-up" : "caret-down"} size="sm" />
</button>
<div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
</div>
-
- {this._opened ? dropdown : null}
+ {options}
</div >
</div>
);
- } else {
- return this.renderCellWithType("list");
}
+ return this.renderCellWithType("list");
}
}
-
-
-
@observer
export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
- @observable private _isChecked: boolean = typeof this.props.rowProps.original[this.props.rowProps.column.id as string] === "boolean" ? BoolCast(this.props.rowProps.original[this.props.rowProps.column.id as string]) : false;
-
- @action
- toggleChecked = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._isChecked = e.target.checked;
- const script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } });
- if (script.compiled) {
- this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
- }
- }
+ @computed get _isChecked() { return BoolCast(this._rowDoc[this.renderFieldKey]); }
render() {
const reference = React.createRef<HTMLDivElement>();
- const onItemDown = (e: React.PointerEvent) => {
- (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
- SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
- };
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
- <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={this._document[Id]} ref={reference}>
- <input type="checkbox" checked={this._isChecked} onChange={this.toggleChecked} />
- </div >
+ <input type="checkbox" checked={this._isChecked} onChange={e => this._rowDoc[this.renderFieldKey] = e.target.checked} />
</div>
);
}
@@ -969,62 +513,15 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
@observer
export class CollectionSchemaButtons extends CollectionSchemaCell {
-
render() {
- // const reference = React.createRef<HTMLDivElement>();
- // const onItemDown = (e: React.PointerEvent) => {
- // (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined :
- // SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e));
- // };
- const doc = this.props.rowProps.original;
- let buttons: JSX.Element | undefined = undefined;
- buttons = <div style={{
- paddingTop: 8,
- paddingLeft: 3,
- }}><button onClick={() => {
- doc.searchMatch = false;
- setTimeout(() => doc.searchMatch = true, 0);
- }} style={{ padding: 2, left: 77 }}>
- <FontAwesomeIcon icon="arrow-up" size="sm" />
- </button>
- <button onClick={() => {
- {
- doc.searchMatchAlt = false;
- setTimeout(() => doc.searchMatchAlt = true, 0);
- }
- }} style={{ padding: 2 }}>
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button></div>;
- const type = StrCast(doc.type);
- if (type === "pdf") {
- buttons = <div><button
- style={{
- position: "relative",
- height: 30,
- width: 28,
- left: 1,
- }}
-
- onClick={() => {
- doc.searchMatch = false;
- setTimeout(() => doc.searchMatch = true, 0);
- }}>
- <FontAwesomeIcon icon="arrow-down" size="sm" />
- </button></div >;
- }
- else if (type !== "rtf") {
- buttons = undefined;
- }
-
- if (BoolCast(this.props.Document._searchDoc) === true) {
-
- }
- else {
- buttons = undefined;
- }
- return (
- <div> {buttons}</div>
- );
+ return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? <></> :
+ <div style={{ paddingTop: 8, paddingLeft: 3 }} >
+ <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
+ <FontAwesomeIcon icon="arrow-up" size="sm" />
+ </button>
+ <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)} >
+ <FontAwesomeIcon icon="arrow-down" size="sm" />
+ </button>
+ </div>;
}
-}
-
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 5c0e6581b..d2a1234ed 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -13,57 +13,11 @@ import { SearchBox } from "../search/SearchBox";
import { ColumnType } from "./CollectionSchemaView";
import "./CollectionSchemaView.scss";
import { CollectionView } from "./CollectionView";
-import * as fa from '@fortawesome/free-solid-svg-icons';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-export interface HeaderProps {
- keyValue: SchemaHeaderField;
- possibleKeys: string[];
- existingKeys: string[];
- keyType: ColumnType;
- typeConst: boolean;
- onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
- setIsEditing: (isEditing: boolean) => void;
- deleteColumn: (column: string) => void;
- setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
- setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
- setColumnColor: (column: SchemaHeaderField, color: string) => void;
-
-}
-
-export class CollectionSchemaHeader extends React.Component<HeaderProps> {
- render() {
- const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
- this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "sort-amount-down" :
- this.props.keyType === ColumnType.Image ? "image" : this.props.keyType === ColumnType.List ? "list-ul" : this.props.keyType === ColumnType.Date ? "calendar" :
- "align-justify";
- return (
- <div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}>
- <CollectionSchemaColumnMenu
- columnField={this.props.keyValue}
- // keyValue={this.props.keyValue.heading}
- possibleKeys={this.props.possibleKeys}
- existingKeys={this.props.existingKeys}
- // keyType={this.props.keyType}
- typeConst={this.props.typeConst}
- menuButtonContent={<div><FontAwesomeIcon icon={icon} size="sm" />{this.props.keyValue.heading}</div>}
- addNew={false}
- onSelect={this.props.onSelect}
- setIsEditing={this.props.setIsEditing}
- deleteColumn={this.props.deleteColumn}
- onlyShowOptions={false}
- setColumnType={this.props.setColumnType}
- setColumnSort={this.props.setColumnSort}
- setColumnColor={this.props.setColumnColor}
- />
- </div>
- );
- }
-}
-
export interface AddColumnHeaderProps {
createColumn: () => void;
@@ -79,7 +33,6 @@ export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHe
}
-
export interface ColumnMenuProps {
columnField: SchemaHeaderField;
// keyValue: string;
@@ -103,37 +56,29 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
@observable private _isOpen: boolean = false;
@observable private _node: HTMLDivElement | null = null;
- componentDidMount() {
- document.addEventListener("pointerdown", this.detectClick);
- }
+ componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
- componentWillUnmount() {
- document.removeEventListener("pointerdown", this.detectClick);
- }
+ componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
- detectClick = (e: PointerEvent): void => {
- if (this._node && this._node.contains(e.target as Node)) {
- } else {
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
+ @action
+ detectClick = (e: PointerEvent) => {
+ !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
}
@action
toggleIsOpen = (): void => {
- this._isOpen = !this._isOpen;
- this.props.setIsEditing(this._isOpen);
+ this.props.setIsEditing(this._isOpen = !this._isOpen);
}
- changeColumnType = (type: ColumnType): void => {
+ changeColumnType = (type: ColumnType) => {
this.props.setColumnType(this.props.columnField, type);
}
- changeColumnSort = (desc: boolean | undefined): void => {
+ changeColumnSort = (desc: boolean | undefined) => {
this.props.setColumnSort(this.props.columnField, desc);
}
- changeColumnColor = (color: string): void => {
+ changeColumnColor = (color: string) => {
this.props.setColumnColor(this.props.columnField, color);
}
@@ -145,7 +90,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
renderTypes = () => {
- if (this.props.typeConst) return <></>;
+ if (this.props.typeConst) return (null);
const type = this.props.columnField.type;
return (
@@ -291,7 +236,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@observable private _key: string = this.props.keyValue;
@observable private _searchTerm: string = this.props.keyValue;
@observable private _isOpen: boolean = false;
- @observable private _canClose: boolean = true;
+ @observable private _node: HTMLDivElement | null = null;
@observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
@action setSearchTerm = (value: string): void => { this._searchTerm = value; };
@@ -306,6 +251,31 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
this.props.setIsEditing(false);
}
+ @action
+ setNode = (node: HTMLDivElement): void => {
+ if (node) {
+ this._node = node;
+ }
+ }
+
+ componentDidMount() {
+ document.addEventListener("pointerdown", this.detectClick);
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters?.includes(this._key)) {
+ runInAction(() => this.closeResultsVisibility = "contents");
+ }
+ }
+
+ @action
+ detectClick = (e: PointerEvent): void => {
+ if (this._node && this._node.contains(e.target as Node)) {
+ } else {
+ this._isOpen = false;
+ this.props.setIsEditing(false);
+ }
+ }
+
+ private tempfilter: string = "";
@undoBatch
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
@@ -313,23 +283,27 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
const colpos = this._searchTerm.indexOf(":");
const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
if (temp === "") {
- Doc.setDocFilter(this.props.Document, this._key, temp, undefined);
+ Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+ this.updateFilter();
}
else {
- Doc.setDocFilter(this.props.Document, this._key, temp, "match");
+ Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+ this.tempfilter = temp;
+ Doc.setDocFilter(this.props.Document, this._key, temp, "check");
this.props.col.setColor("green");
+ this.closeResultsVisibility = "contents";
}
}
else {
+ Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined);
+ this.updateFilter();
let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- const blockedkeys = ["_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+ const blockedkeys = ["system", "title-custom", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
if (keyOptions.length) {
this.onSelect(keyOptions[0]);
- console.log("case1");
} else if (this._searchTerm !== "" && this.props.canAddNew) {
this.setSearchTerm(this._searchTerm || this._key);
- console.log("case2");
this.onSelect(this._searchTerm);
}
}
@@ -347,23 +321,6 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
@action
- onBlur = (e: React.FocusEvent): void => {
- if (this._canClose) {
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
- }
-
- @action
- onPointerEnter = (e: React.PointerEvent): void => {
- this._canClose = false;
- }
-
- @action
- onPointerOut = (e: React.PointerEvent): void => {
- this._canClose = true;
- }
- @action
renderOptions = (): JSX.Element[] | JSX.Element => {
if (!this._isOpen) {
this.defaultMenuHeight = 0;
@@ -375,21 +332,26 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
- keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
+ const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"];
+ keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL"));
const options = keyOptions.map(key => {
return <div key={key} className="key-option" style={{
border: "1px solid lightgray",
width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
}}
- onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}
+ onClick={() => {
+ this.onSelect(key);
+ this.setSearchTerm("");
+ }}>{key}</div>;
});
// if search term does not already exist as a group type, give option to create new group type
if (this._key !== this._searchTerm.slice(0, this._key.length)) {
- console.log("little further");
if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
options.push(<div key={""} className="key-option" style={{
border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
@@ -418,55 +380,55 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@action
renderFilterOptions = (): JSX.Element[] | JSX.Element => {
- if (!this._isOpen) {
+ if (!this._isOpen || !this.props.dataDoc) {
this.defaultMenuHeight = 0;
return <></>;
}
-
const keyOptions: string[] = [];
const colpos = this._searchTerm.indexOf(":");
const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
if (this.docSafe.length === 0) {
- this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
+ this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
}
const docs = this.docSafe;
docs.forEach((doc) => {
const key = StrCast(doc[this._key]);
- if (keyOptions.includes(key) === false && key.includes(temp)) {
+ if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
keyOptions.push(key);
}
});
const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
for (let i = 0; i < (filters?.length ?? 0) - 1; i += 3) {
if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i + 1]) === false) {
keyOptions.push(filters![i + 1]);
}
}
-
- if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
- this.props.col.setColor("rgb(241, 239, 235)");
- }
-
const options = keyOptions.map(key => {
- //Doc.setDocFilter(this.props.Document!, this._key, key, undefined);
let bool = false;
- console.log(filters);
if (filters !== undefined) {
bool = filters.includes(key) && filters[filters.indexOf(key) + 1] === "check";
- console.log(filters.includes(key));
}
return <div key={key} className="key-option" style={{
border: "1px solid lightgray", paddingLeft: 5, textAlign: "left",
width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
}}
>
- <input type="checkbox" onChange={(e) => {
- e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined);
- e.target.checked === true ? this.props.col.setColor("green") : "";
- e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined);
- }}
- checked={bool} ></input>
+ <input type="checkbox"
+ onPointerDown={e => e.stopPropagation()}
+ onClick={e => e.stopPropagation()}
+ onChange={(e) => {
+ e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined);
+ e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log("");
+ e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter();
+ e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined);
+ }}
+ checked={bool}
+ />
<span style={{ paddingLeft: 4 }}>
{key}
</span>
@@ -492,41 +454,67 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@observable defaultMenuHeight = 0;
+ updateFilter() {
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+ if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
+ }
+
get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; }
@computed get scriptField() {
- console.log("we kinda made it");
const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
return script ? () => script : undefined;
}
filterBackground = () => "rgba(105, 105, 105, 0.432)";
-
@observable filterOpen: boolean | undefined = undefined;
+ closeResultsVisibility: string = "none";
+
+ removeFilters = (e: React.PointerEvent): void => {
+ const keyOptions: string[] = [];
+ if (this.docSafe.length === 0 && this.props.dataDoc) {
+ this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]);
+ }
+ const docs = this.docSafe;
+ docs.forEach((doc) => {
+ const key = StrCast(doc[this._key]);
+ if (keyOptions.includes(key) === false) {
+ keyOptions.push(key);
+ }
+ });
+
+ Doc.setDocFilter(this.props.Document, this._key, "", "remove");
+ this.props.col.setColor("rgb(241, 239, 235)");
+ this.closeResultsVisibility = "none";
+ }
render() {
return (
- <div style={{ display: "flex" }}>
- <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
+ <div style={{ display: "flex" }} ref={this.setNode}>
+ <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
{/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
}} /> */}
- <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}>
+ <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
<input className="keys-search" style={{ width: "100%" }}
ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
onChange={e => this.onChange(e.target.value)}
- onClick={(e) => {
- //this._inputRef.current!.select();
- e.stopPropagation();
- }} onFocus={this.onFocus} onBlur={this.onBlur}></input>
- <div className="keys-options-wrapper" style={{
+ onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
+ onFocus={this.onFocus} ></input>
+ <div style={{ display: this.closeResultsVisibility }}>
+ <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
+ style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
+ </div>
+ {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
width: this.props.width, maxWidth: this.props.width, height: "auto",
- }}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}>
+ }}>
{this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()}
- </div>
+ </div>}
</div >
</div>
);
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index dade4f2f2..881246bd4 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -1,21 +1,17 @@
import React = require("react");
-import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table";
-import "./CollectionSchemaView.scss";
-import { Transform } from "../../util/Transform";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action } from "mobx";
+import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
import { Doc } from "../../../fields/Doc";
-import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
+import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { Cast, FieldValue, StrCast } from "../../../fields/Types";
-import { ContextMenu } from "../ContextMenu";
-import { action } from "mobx";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { DocumentManager } from "../../util/DocumentManager";
-import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { undoBatch } from "../../util/UndoManager";
+import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager";
import { SnappingManager } from "../../util/SnappingManager";
-
-library.add(faGripVertical, faTrash);
+import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
+import "./CollectionSchemaView.scss";
export interface MovableColumnProps {
columnRenderer: TableCellRenderer;
@@ -40,7 +36,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
onPointerLeave = (e: React.PointerEvent): void => {
this._header!.current!.className = "collectionSchema-col-wrapper";
document.removeEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointermove", this.onPointerMove);
+ !e.buttons && document.removeEventListener("pointermove", this.onPointerMove);
}
onDragMove = (e: PointerEvent): void => {
const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
@@ -68,6 +64,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
const before = x[0] < bounds[0];
const colDragData = de.complete.columnDragData;
if (colDragData) {
+ e.stopPropagation();
this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns);
return true;
}
@@ -108,8 +105,10 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
this._dragRef = ref;
const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
- this._startDragPosition = { x: dx, y: dy };
- document.addEventListener("pointermove", this.onPointerMove);
+ if (!(e.target as any)?.tagName.includes("INPUT")) {
+ this._startDragPosition = { x: dx, y: dy };
+ document.addEventListener("pointermove", this.onPointerMove);
+ }
}
@@ -164,6 +163,10 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (!before) this._header!.current!.className += " row-below";
e.stopPropagation();
}
+ componentWillUnmount() {
+
+ this._rowDropDisposer?.();
+ }
createRowDropTarget = (ele: HTMLDivElement) => {
this._rowDropDisposer?.();
@@ -198,6 +201,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
}
onRowContextMenu = (e: React.MouseEvent): void => {
+ e.preventDefault();
const description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" });
}
@@ -219,13 +223,15 @@ export class MovableRow extends React.Component<MovableRowProps> {
render() {
const { children = null, rowInfo } = this.props;
+
if (!rowInfo) {
return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>;
}
const { original } = rowInfo;
const doc = FieldValue(Cast(original, Doc));
- if (!doc) return <></>;
+
+ if (!doc) return (null);
const reference = React.createRef<HTMLDivElement>();
const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
@@ -238,11 +244,11 @@ export class MovableRow extends React.Component<MovableRowProps> {
<div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
<div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
<ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
- {/* <div className="row-dragger">
- <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
- <div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
- <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "onRight")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
- </div> */}
+ <div className="row-dragger">
+ <div className="row-option" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+ <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+ <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+ </div>
{children}
</ReactTableDefaults.TrComponent>
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 5c2931a8b..8d2f645d9 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -59,6 +59,7 @@
justify-content: space-between;
flex-wrap: nowrap;
touch-action: none;
+ padding: 2px;
div {
touch-action: none;
@@ -99,6 +100,9 @@
direction: ltr;
overflow: visible;
}
+ .rt-noData {
+ display: none;
+ }
.rt-thead {
width: 100%;
@@ -139,6 +143,10 @@
width: 100%;
direction: rtl;
overflow: visible;
+
+ .rt-td {
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+ }
}
.rt-tr-group {
@@ -158,6 +166,8 @@
font-size: 13px;
text-align: center;
white-space: nowrap;
+ display: flex;
+ align-items: center;
.imageBox-cont {
position: relative;
@@ -176,6 +186,11 @@
height: 100%;
}
}
+ .rt-td.rt-expandable {
+ display: flex;
+ align-items: center;
+ height: inherit;
+ }
.rt-resizer {
width: 8px;
@@ -211,7 +226,8 @@
height: auto;
z-index: 100;
position: absolute;
- background:white;
+ background: white;
+ padding: 5px;
.collectionSchema-header-toggler {
z-index: 100;
@@ -243,12 +259,13 @@ button.add-column {
.collectionSchema-header-menuOptions {
color: black;
- width: 200px;
+ width: 180px;
text-align: left;
.collectionSchema-headerMenu-group {
padding: 7px 0;
border-bottom: 1px solid lightgray;
+ cursor: pointer;
&:first-child {
padding-top: 0;
@@ -326,6 +343,7 @@ button.add-column {
background-color: white;
border: 1px solid lightgray;
padding: 2px 3px;
+
&:not(:first-child) {
border-top: 0;
}
@@ -356,47 +374,6 @@ button.add-column {
}
}
-.altcollectionTimeView-treeView {
- display: flex;
- flex-direction: column;
- width: 175px;
- height: auto;
- position: fixed;
- border-left: solid 1px;
- z-index: 1;
-
- .collectionTimeView-addfacet {
- display: inline-block;
- width: 200px;
- height: 30px;
- background: darkGray;
- text-align: left;
-
- .collectionTimeView-button {
- align-items: center;
- display: flex;
- width: 100%;
- height: 100%;
-
- .collectionTimeView-span {
- margin: auto;
- }
- }
-
- >div,
- >div>div {
- width: 100%;
- height: 100%;
- }
- }
-
- .altcollectionTimeView-tree {
- display: inline-block;
- width: 100%;
- height: calc(100% - 30px);
- }
-}
-
.collectionSchema-row {
height: 100%;
background-color: white;
@@ -414,11 +391,12 @@ button.add-column {
.row-dragger {
display: flex;
justify-content: space-around;
- flex: 50 0 auto;
- width: 50px;
+ //flex: 50 0 auto;
+ width: 0;
max-width: 50px;
- height: 100%;
+ //height: 100%;
min-height: 30px;
+ align-items: center;
color: lightgray;
background-color: white;
transition: color 0.1s ease;
@@ -426,10 +404,12 @@ button.add-column {
.row-option {
// padding: 5px;
cursor: pointer;
+ position: absolute;
transition: color 0.1s ease;
display: flex;
flex-direction: column;
justify-content: center;
+ z-index: 2;
&:hover {
color: gray;
@@ -459,15 +439,15 @@ button.add-column {
.collectionSchemaView-cellContainer {
width: 100%;
- height: 100%;
+ height: unset;
}
.collectionSchemaView-cellWrapper {
height: 100%;
padding: 4px;
- text-align:left;
- padding-left:19px;
-
+ text-align: left;
+ padding-left: 19px;
+
position: relative;
&:focus {
@@ -596,21 +576,23 @@ button.add-column {
.collectionSchemaView-table {
width: 100%;
height: 100%;
+ overflow: auto;
+ padding: 3px;
}
+.rt-td.rt-expandable {
+ overflow: visible;
+ position: relative;
+ height:100%;
+ z-index: 1;
+}
.reactTable-sub {
- padding: 10px 30px;
background-color: rgb(252, 252, 252);
width: 100%;
.rt-thead {
- display:none;
- }
- .collectionSchemaView-table{
- border: solid 1px;
- overflow: hidden;
+ display: none;
}
-
.row-dragger {
background-color: rgb(252, 252, 252);
@@ -621,20 +603,26 @@ button.add-column {
}
.collectionSchemaView-table {
- width: 100%;
+ width: 100%;
+ border: solid 1px;
+ overflow: visible;
+ padding: 0px;
}
}
.collectionSchemaView-expander {
height: 100%;
min-height: 30px;
- position: relative;
+ position: absolute;
color: gray;
+ width: 20;
+ height: auto;
+ left: 55;
svg {
position: absolute;
top: 50%;
- left: 50%;
+ left: 10;
transform: translate(-50%, -50%);
}
}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index f1de3cee7..1b68c0e1a 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -1,32 +1,29 @@
import React = require("react");
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCog, faPlus, faSortDown, faSortUp, faTable } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
import Measure from "react-measure";
import { Resize } from "react-table";
import "react-table/react-table.css";
-import { Doc } from "../../../fields/Doc";
+import { Doc, Opt } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { Cast, NumCast, BoolCast } from "../../../fields/Types";
+import { Cast, NumCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
import '../DocumentDecorations.scss';
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { KeysDropdown } from "./CollectionSchemaHeaders";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { SchemaTable } from "./SchemaTable";
-
-library.add(faCog, faPlus, faSortUp, faSortDown);
-library.add(faTable);
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
export enum ColumnType {
@@ -44,7 +41,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
["title", ColumnType.String],
["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
- ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+ ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
]);
@observer
@@ -62,8 +59,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _menuWidth = 0;
@observable _headerOpen = false;
- @observable _isOpen = false;
- @observable _node: HTMLDivElement | null = null;
@observable _headerIsEditing = false;
@observable _col: any = "";
@observable _menuHeight = 0;
@@ -73,7 +68,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@computed get menuCoordinates() {
let searchx = 0;
let searchy = 0;
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
if (el !== undefined) {
const rect = el.getBoundingClientRect();
@@ -86,7 +81,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
return this.props.ScreenToLocalTransform().transformPoint(x, y);
}
- @observable scale = this.props.ScreenToLocalTransform().Scale;
+ @computed get scale() { return this.props.ScreenToLocalTransform().Scale; }
@computed get columns() {
return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
@@ -111,33 +106,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
@computed get possibleKeys() { return this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); }
+ @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
- componentDidMount() {
- document.addEventListener("pointerdown", this.detectClick);
- }
-
- componentWillUnmount() {
- document.removeEventListener("pointerdown", this.detectClick);
- }
-
- @action setHeaderIsEditing = (isEditing: boolean) => {
- this._headerIsEditing = isEditing;
- }
-
- detectClick = (e: PointerEvent): void => {
- if (this._node && this._node.contains(e.target as Node)) {
- } else {
- this._isOpen = false;
- this.setHeaderIsEditing(false);
- this.closeHeader();
- }
- }
-
- @action
- toggleIsOpen = (): void => {
- this._isOpen = !this._isOpen;
- this.setHeaderIsEditing(this._isOpen);
- }
@action
changeColumnType = (type: ColumnType, col: any): void => {
@@ -190,16 +160,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.columns = columns;
}
- @action
- setNode = (node: HTMLDivElement): void => {
- node && (this._node = node);
- }
-
- @action
- typesDropdownChange = (bool: boolean) => {
- this._openTypes = bool;
- }
-
renderTypes = (col: any) => {
if (columnTypes.get(col.heading)) return (null);
@@ -263,10 +223,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
type === ColumnType.Date ? dateType : imageType;
return (
- <div className="collectionSchema-headerMenu-group">
- <div onClick={() => this.typesDropdownChange(!this._openTypes)}>
- <label>Column type:</label>
- <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right" }} />
+ <div className="collectionSchema-headerMenu-group" onClick={action(() => this._openTypes = !this._openTypes)}>
+ <div>
+ <label style={{ cursor: "pointer" }}>Column type:</label>
+ <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right", transform: `rotate(${this._openTypes ? "180deg" : 0})`, transition: "0.2s all ease" }} />
</div>
{this._openTypes ? allColumnTypes : justColType}
</div >
@@ -340,17 +300,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.columns = columns;
if (filter) {
Doc.setDocFilter(this.props.Document, newKey, filter, "match");
- if (this.props.Document.selectedDoc !== undefined) {
- const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
- Doc.setDocFilter(doc, newKey, filter, "match");
- }
}
else {
this.props.Document._docFilters = undefined;
- if (this.props.Document.selectedDoc !== undefined) {
- const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
- doc._docFilters = undefined;
- }
}
}
}
@@ -360,7 +312,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
openHeader = (col: any, screenx: number, screeny: number) => {
this._col = col;
- this._headerOpen = !this._headerOpen;
+ this._headerOpen = true;
this._pointerX = screenx;
this._pointerY = screeny;
}
@@ -392,7 +344,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
onHeaderClick = (e: React.PointerEvent) => {
- this.props.active(true);
e.stopPropagation();
}
@@ -408,7 +359,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
TraceMobx();
return <div className="collectionSchema-header-menuOptions">
{this.renderTypes(this._col)}
- {this.renderSorting(this._col)}
+ {/* {this.renderSorting(this._col)} */}
{this.renderColors(this._col)}
<div className="collectionSchema-headerMenu-group">
<button onClick={() => { this.deleteColumn(this._col.heading); }}
@@ -426,7 +377,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action setFocused = (doc: Doc) => this._focusedTable = doc;
- @action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
+ @action setPreviewDoc = (doc: Opt<Doc>) => {
+ SelectionManager.SelectSchemaDoc(this, doc);
+ this.previewDoc = doc;
+ }
//toggles preview side-panel of schema
@action
@@ -487,6 +441,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
PanelHeight={this.previewHeight}
ScreenToLocalTransform={this.getPreviewTransform}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
@@ -531,6 +486,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
documentKeys={this.documentKeys}
headerIsEditing={this._headerIsEditing}
openHeader={this.openHeader}
+ onClick={e => { e.stopPropagation(); this.closeHeader(); }}
onPointerDown={this.onTablePointerDown}
onResizedChange={this.onResizedChange}
setColumns={this.setColumns}
@@ -552,15 +508,29 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>
</div>;
}
+ onSpecificMenu = (e: React.MouseEvent) => {
+ if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) {
+ const cm = ContextMenu.Instance;
+ const options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ optionItems.push({ description: "remove", event: () => this.previewDoc && this.props.removeDocument(this.previewDoc), icon: "trash" });
+ !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+ cm.displayMenu(e.clientX, e.clientY);
+ (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
+ e.stopPropagation();
+ }
+ }
@action
onTablePointerDown = (e: React.PointerEvent): void => {
+ if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) {
+ this.setPreviewDoc(undefined);
+ }
this.setFocused(this.props.Document);
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected(true)) {
e.stopPropagation();
}
- this._pointerY = e.screenY;
- this._pointerX = e.screenX;
+ // this.closeHeader();
}
onResizedChange = (newResized: Resize[], event: any) => {
@@ -610,17 +580,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
render() {
let name = "collectionSchemaView-container";
- if (this.props.Document._searchDoc !== undefined) {
+ if (this.props.Document._searchDoc) {
name = "collectionSchemaView-searchContainer";
}
+ if (!this.props.active()) setTimeout(() => this.closeHeader(), 0);
TraceMobx();
const menuContent = this.renderMenuContent;
- const menu = <div className="collectionSchema-header-menu" ref={this.setNode}
+ const menu = <div className="collectionSchema-header-menu"
onWheel={e => this.onZoomMenu(e)}
onPointerDown={e => this.onHeaderClick(e)}
style={{
position: "fixed", background: "white", border: "black 1px solid",
- transform: `translate(${(this.menuCoordinates[0] / this.scale)}px, ${(this.menuCoordinates[1] / this.scale)}px)`
+ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)`
}}>
<Measure offset onResize={action((r: any) => {
const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
@@ -631,13 +602,14 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>;
return <div className={name}
style={{
- overflow: this.props.overflow === true ? "scroll" : undefined,
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
+ overflow: this.props.overflow === true ? "scroll" : undefined, backgroundColor: "white",
+ pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
- style={{ backgroundColor: "white", width: `calc(100% - ${this.previewWidth()}px)` }}
+ style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
onKeyPress={this.onKeyPress}
+ onContextMenu={this.onSpecificMenu}
onPointerDown={this.onPointerDown}
onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.onExternalDrop(e, {})}
@@ -646,7 +618,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
</div>
{this.dividerDragger}
{!this.previewWidth() ? (null) : this.previewPanel}
- {this._headerOpen ? menu : null}
+ {this._headerOpen && this.props.active() ? menu : null}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index fe3d57bdb..fb0bce53e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -38,6 +38,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
_masonryGridRef: HTMLDivElement | null = null;
_draggerRef = React.createRef<HTMLDivElement>();
_pivotFieldDisposer?: IReactionDisposer;
+ _autoHeightDisposer?: IReactionDisposer;
_docXfs: any[] = [];
_columnStart: number = 0;
@observable _heightMap = new Map<string, number>();
@@ -47,7 +48,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
@computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout); }
@computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 0)); } // 2 * this.gridGap)); }
+ @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 5)); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -76,6 +77,9 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const dxf = () => this.getDocTransform(d, dref.current!);
this._docXfs.push({ dxf, width, height });
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+ if (height() < 5) {
+ console.log("here" + height());
+ }
const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(d, (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc, dxf, width)}
@@ -148,10 +152,12 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
() => this.pivotField,
() => this.layoutDoc._columnHeaders = new List()
);
+ this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight, this.forceAutoHeight);
}
componentWillUnmount() {
super.componentWillUnmount();
this._pivotFieldDisposer?.();
+ this._autoHeightDisposer?.();
}
@action
@@ -193,7 +199,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) {
const height = () => this.getDocHeight(doc);
- const opacity = () => this.Document.currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document.currentFrame))?.opacity;
+ const opacity = () => this.Document._currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
return <ContentFittingDocumentView
Document={doc}
DataDoc={dataDoc || (doc[DataSym] !== doc && doc[DataSym])}
@@ -217,6 +223,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
opacity={opacity}
focus={this.focusDocument}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
@@ -249,8 +256,10 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
if (!(this.layoutDoc._columnsFill)) wid = Math.min(layoutDoc[WidthSym](), wid);
return wid * aspect;
}
- return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin :
- Math.min(wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1), this.props.PanelHeight() - 2 * this.yMargin) : Math.max(20, layoutDoc[HeightSym]());
+ return layoutDoc._fitWidth ?
+ (!nh ? this.props.PanelHeight() - 2 * this.yMargin :
+ Math.min(wid * nh / (nw || 1), this.layoutDoc._autoHeight ? 100000 : this.props.PanelHeight() - 2 * this.yMargin)) :
+ Math.max(20, layoutDoc[HeightSym]());
}
columnDividerDown = (e: React.PointerEvent) => {
@@ -340,9 +349,11 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
const key = this.pivotField;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
- const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
- if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
- type = types[0];
+ if (this.pivotField) {
+ const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
+ if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ type = types[0];
+ }
}
const cols = () => this.isStackingView ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
@@ -354,16 +365,16 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
this.observer = new _global.ResizeObserver(action((entries: any) => {
if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
- Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
+ Doc.Layout(doc)._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
}
}));
this.observer.observe(ref);
}
}}
- key={heading ? heading.heading : ""}
+ key={heading?.heading ?? ""}
cols={cols}
headings={this.headings}
- heading={heading ? heading.heading : ""}
+ heading={heading?.heading ?? ""}
headingObject={heading}
docList={docList}
parent={this}
@@ -383,6 +394,11 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
return this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + offsety);
}
+ forceAutoHeight = () => {
+ const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc;
+ Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
+ }
+
sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
const key = this.pivotField;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
@@ -449,6 +465,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const subItems: ContextMenuProps[] = [];
subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+ subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" });
ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
}
}
@@ -489,7 +506,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
transformOrigin: "top left",
}}
onScroll={action(e => {
- if (!this.props.isSelected() && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
+ if (!this.props.isSelected(true) && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}
onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index f193a9787..fd2ae03d8 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,35 +1,31 @@
import React = require("react");
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPalette } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, runInAction, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../fields/Doc";
import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { ScriptField } from "../../../fields/ScriptField";
-import { NumCast, StrCast, Cast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { ImageField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
+import { emptyFunction, setupMoveUpEvents } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { DragManager } from "../../util/DragManager";
+import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
-import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
import "./CollectionStackingView.scss";
-import { listSpec } from "../../../fields/Schema";
-import { SnappingManager } from "../../util/SnappingManager";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faPalette);
-
interface CSVFieldColumnProps {
cols: () => number;
headings: () => object[];
@@ -136,7 +132,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
addDocument = (value: string, shiftDown?: boolean) => {
if (!value) return false;
const key = StrCast(this.props.parent.props.Document._pivotField);
- const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
+ const newDoc = Docs.Create.TextDocument(value, { _height: 18, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value, _autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
@@ -269,7 +265,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
Doc.GetProto(this.props.parent.props.Document)[name] = "";
- const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
+ const created = Docs.Create.TextDocument("", { title: name, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 250, _autoHeight: true });
if (created) {
if (this.props.parent.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
@@ -289,9 +285,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const heading = this._heading;
const style = this.props.parent;
const singleColumn = style.isStackingView;
- const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin);
+ const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin, 5);
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
- const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+ const evContents = heading ? heading : this.props?.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
const headerEditableViewProps = {
GetValue: () => evContents,
SetValue: this.headingChanged,
@@ -310,7 +306,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
- marginTop: NumCast(this.props.parent.props.Document._yMargin),
+ marginTop: NumCast(this.props.parent.props.Document._yMargin, 5),
width: (style.columnWidth) /
((uniqueHeadings.length +
((this.props.parent.props.Document._chromeStatus !== 'view-mode' && this.props.parent.props.Document._chromeStatus !== 'disabled') ? 1 : 0)) || 1)
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3f2ad47a5..8f0710f4b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, reaction, observable, runInAction
import { basename } from 'path';
import CursorField from "../../../fields/CursorField";
import { Doc, Opt, Field, DocListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
+import { Id, ToString } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
@@ -111,6 +111,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
return this.props.ignoreFields?.includes("_docFilters") ? [] :
[...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])];
}
+ searchFilterDocs = () => {
+ return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
+ }
@computed get childDocs() {
let rawdocs: (Doc | Promise<Doc>)[] = [];
if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
@@ -128,52 +131,43 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
- let searchDocs = DocListCast(this.props.Document._searchDocs);
-
+ const docFilters = this.docFilters();
+ let searchDocs = this.searchFilterDocs();
+ if (this.props.Document.dontRegisterView || (!docFilters.length && !searchDocs.length)) return childDocs;
- let docsforFilter: Doc[] = childDocs;
-
- if (searchDocs !== undefined && searchDocs.length > 0) {
- docsforFilter = [];
- const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
- console.log(searchDocs);
- searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript);
- console.log(this.docFilters());
- console.log(searchDocs);
- childDocs.forEach((d) => {
- if (d.data !== undefined) {
- let newdocs = DocListCast(d.data);
- if (newdocs.length > 0) {
- let displaycheck: boolean | undefined = undefined;
- let newarray: Doc[] = [];
- while (newdocs.length > 0) {
- newarray = [];
- newdocs.forEach((t) => {
- if (d.data !== undefined) {
- const newdocs = DocListCast(t.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- if (searchDocs.includes(t)) {
- displaycheck = true;
- }
- });
- newdocs = newarray;
- }
- if (displaycheck === true) {
- docsforFilter.push(d);
- }
+ const docsforFilter: Doc[] = [];
+ const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+ childDocs.forEach((d) => {
+ if (this.props.Document.title === "lose this") {
+ console.log('here"')
+ }
+ if (d.title === "lose this") {
+ console.log('here"')
+ }
+ let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && (!docFilters.length || DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0));
+ const fieldKey = Doc.LayoutFieldKey(d);
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+ const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ if (data !== undefined) {
+ let subDocs = DocListCast(data);
+ if (subDocs.length > 0) {
+ let newarray: Doc[] = [];
+ notFiltered = notFiltered || (!searchDocs.length && docFilters.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript).length);
+ while (subDocs.length > 0 && !notFiltered) {
+ newarray = [];
+ subDocs.forEach((t) => {
+ const fieldKey = Doc.LayoutFieldKey(t);
+ const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
+ notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && (!docFilters.length || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript).length));
+ DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+ });
+ subDocs = newarray;
}
}
- if (searchDocs.includes(d)) {
- docsforFilter.push(d);
- }
- });
- return docsforFilter;
- }
- const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
- return this.props.Document.dontRegisterView ? childDocs : DocUtils.FilterDocs(childDocs, this.docFilters(), docRangeFilters, viewSpecScript);
+ }
+ notFiltered && docsforFilter.push(d);
+ });
+ return docsforFilter;
}
@action
@@ -240,13 +234,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
} else added = res;
- !added && alert("You don't have permission to perform this move");
- e.stopPropagation();
+ added && e.stopPropagation();
+ return added;
} else {
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
added = this.addDocument(docDragData.droppedDocuments);
}
- added && e.stopPropagation();
+ !added && alert("You cannot perform this move");
+ e.stopPropagation();
return added;
}
else if (de.complete.annoDragData) {
@@ -296,7 +291,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
}
} else if (text) {
- this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
+ this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 25 }));
}
return;
}
@@ -408,7 +403,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
_height: 315,
_nativeWidth: 850,
_nativeHeight: 962,
- UseCors: true
+ useCors: true
});
newDoc.data = new WebField(uriList.split("#annotations:")[0]); // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
this.addDocument(newDoc);
@@ -472,7 +467,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
completed?.();
} else {
if (text && !text.includes("https://")) {
- UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
+ UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop");
}
}
disposer();
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index c9bf82406..f96a5c4f0 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -85,6 +85,15 @@
white-space: pre-wrap;
min-width: 10px;
}
+.docContainer-system {
+ font-variant: all-small-caps;
+ border-radius: 5px;
+ background: grey;
+ color: white;
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-bottom: 2px;
+}
.treeViewItem-openRight {
display: none;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d096e7d66..f13fee776 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,17 +1,16 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { PrefetchProxy } from '../../../fields/Proxy';
import { Document, listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from '../../util/DocumentManager';
import { SnappingManager } from '../../util/SnappingManager';
import { DragManager, dropActionType } from "../../util/DragManager";
import { Scripting } from '../../util/Scripting';
@@ -21,7 +20,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from "../EditableView";
-import { MainView } from '../MainView';
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import { DocumentView } from '../nodes/DocumentView';
import { ImageBox } from '../nodes/ImageBox';
@@ -33,15 +31,16 @@ import { CollectionViewType } from './CollectionView';
import React = require("react");
import { makeTemplate } from '../../util/DropConverter';
import { TraceMobx } from '../../../fields/util';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionDockingView } from './CollectionDockingView';
export interface TreeViewProps {
document: Doc;
dataDoc?: Doc;
- libraryPath: Doc[] | undefined;
containingCollection: Doc;
prevSibling?: Doc;
renderDepth: number;
- deleteDoc: (doc: Doc | Doc[]) => boolean;
+ removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined;
moveDocument: DragManager.MoveFunction;
dropAction: dropActionType;
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
@@ -55,7 +54,7 @@ export interface TreeViewProps {
ScreenToLocalTransform: () => Transform;
backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
- treeViewDoc: Doc;
+ treeView: CollectionTreeView;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
treeViewHideHeaderFields: () => boolean;
@@ -64,6 +63,8 @@ export interface TreeViewProps {
onCheckedClick?: () => ScriptField;
onChildClick?: () => ScriptField;
ignoreFields?: string[];
+ firstLevel: boolean;
+ whenActiveChanged: (isActive: boolean) => void;
}
@observer
@@ -77,6 +78,7 @@ export interface TreeViewProps {
*/
class TreeView extends React.Component<TreeViewProps> {
private _editTitleScript: (() => ScriptField) | undefined;
+ private _openScript: (() => ScriptField) | undefined;
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
@@ -88,23 +90,25 @@ class TreeView extends React.Component<TreeViewProps> {
get doc() { return this.props.document; }
get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
- get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
+ get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
set treeViewOpen(c: boolean) {
if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
}
@computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
- @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); }
+ @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
@computed get dataDoc() { return this.doc[DataSym]; }
@computed get layoutDoc() { return Doc.Layout(this.doc); }
@computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; }
childDocList(field: string) {
const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined;
- return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
- (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
- DocListCast(this.doc[field])); // otherwise use the document's data field
+ return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+ (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
+ DocListCastOrNull(this.doc[field])); // otherwise use the document's data field
}
@computed get childDocs() { return this.childDocList(this.fieldKey); }
@computed get childLinks() { return this.childDocList("links"); }
@@ -114,22 +118,25 @@ class TreeView extends React.Component<TreeViewProps> {
Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey]));
}
- @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight", this.props.libraryPath);
+ @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right");
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- return this.doc !== target && this.props.deleteDoc(doc) && addDoc(doc);
+ return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
}
@undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
}
@undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
- flg && Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), doc), true);
+ flg && Doc.RemoveDocFromList(this.doc, Doc.LayoutFieldKey(this.doc), doc), true);
}
constructor(props: any) {
super(props);
- const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
- this._editTitleScript = script && (() => script);
+ const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" });
+ const openScript = ScriptField.MakeScript(`openOnRight(self)`);
+ const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`);
+ this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript);
+ this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined;
if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false);
}
@@ -193,13 +200,13 @@ class TreeView extends React.Component<TreeViewProps> {
}}
OnTab={undoBatch((shift?: boolean) => {
shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
- setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0);
+ setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", `${this.props.treeView._uniqueId}`, false), 0);
})}
/>)
preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.props.treeViewDoc === dragData.treeViewDoc ? "same" : dragData.dropAction);
+ dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
}
@undoBatch
@@ -274,11 +281,11 @@ class TreeView extends React.Component<TreeViewProps> {
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
(flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
- contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
- DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
+ contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
+ this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields);
+ [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged);
} else {
contentElement = <EditableView key="editableView"
contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -320,16 +327,16 @@ class TreeView extends React.Component<TreeViewProps> {
(doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
const sortKey = `${this.fieldKey}-sortAscending`;
- return <ul key={expandKey + "more"} onClick={(e) => {
+ return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true));
e.stopPropagation();
}}>
{!docs ? (null) :
- TreeView.GetChildElements(docs, this.props.treeViewDoc, this.layoutDoc,
+ TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc,
this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
- [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
+ [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul key={this.doc[Id] + this.doc.title}><div ref={this._dref} style={{ display: "inline-block" }} >
@@ -357,6 +364,7 @@ class TreeView extends React.Component<TreeViewProps> {
focus={returnFalse}
ScreenToLocalTransform={this.docTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.containingCollection}
ContainingCollectionView={undefined}
addDocument={returnFalse}
@@ -383,7 +391,7 @@ class TreeView extends React.Component<TreeViewProps> {
this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
heading: this.props.containingCollection.title,
checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check",
- containingTreeView: this.props.treeViewDoc,
+ containingTreeView: this.props.treeView.props.Document,
}, console.log);
} else {
this.treeViewOpen = !this.treeViewOpen;
@@ -405,11 +413,13 @@ class TreeView extends React.Component<TreeViewProps> {
showContextMenu = (e: React.MouseEvent) => {
this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
}
- focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true);
- contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
- truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0);
- showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
- onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.editTitleScript));
+ contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }];
+ truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0);
+ @computed get showTitleEdit() {
+ return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
+ }
+ onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+ onChildDoubleClick = () => (!this.props.treeView.props.Document.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
/**
* Renders the EditableView title element for placement into the tree.
*/
@@ -418,36 +428,38 @@ class TreeView extends React.Component<TreeViewProps> {
TraceMobx();
const headerElements = this.props.treeViewHideHeaderFields() ? (null) :
<>
- <FontAwesomeIcon icon="cog" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
+ <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
onPointerDown={action(() => {
if (this.treeViewOpen) {
- this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
- this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
- this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
- (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
- this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+ this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
+ this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+ this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
+ this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
+ (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
+ this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
}
this.treeViewOpen = true;
})}>
{this.treeViewExpandedView}
</span>
</>;
- const view = this.showTitleEdit() ? this.editableView("title") :
+ const view = this.showTitleEdit ? this.editableView("title") :
<DocumentView
ref={this._docRef}
Document={this.doc}
DataDoc={undefined}
- treeViewDoc={this.props.treeViewDoc}
- LibraryPath={this.props.libraryPath || emptyPath}
+ treeViewDoc={this.props.treeView.props.Document}
+ LibraryPath={emptyPath}
addDocument={undefined}
addDocTab={this.props.addDocTab}
rootSelected={returnTrue}
pinToPres={emptyFunction}
onClick={this.onChildClick}
+ onDoubleClick={this.onChildDoubleClick}
dropAction={this.props.dropAction}
moveDocument={this.move}
- removeDocument={this.removeDoc}
+ removeDocument={this.props.removeDoc}
ScreenToLocalTransform={this.getTransform}
ContentScaling={returnOne}
PanelWidth={this.truncateTitleWidth}
@@ -459,34 +471,32 @@ class TreeView extends React.Component<TreeViewProps> {
renderDepth={1}
focus={returnTrue}
parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
+ whenActiveChanged={this.props.whenActiveChanged}
bringToFront={emptyFunction}
- dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)}
+ dontRegisterView={BoolCast(this.props.treeView.props.Document.dontRegisterChildViews)}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={this.props.containingCollection}
/>;
return <>
- <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
+ <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`}
style={{
- fontWeight: this.doc.searchMatch ? "bold" : undefined,
+ fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
- outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined,
- pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none"
+ outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
+ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
}} >
{view}
</div >
- {headerElements}
- <div className="treeViewItem-openRight" onClick={this.openRight}>
- <FontAwesomeIcon title="open in a new pane" icon="external-link-alt" size="sm" />
- </div>
+ {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements}
</>;
}
render() {
TraceMobx();
const sorting = this.doc[`${this.fieldKey}-sortAscending`];
- if (this.showTitleEdit()) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
+ if (this.showTitleEdit) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll
let par: any = this._header?.current;
if (par) {
while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode;
@@ -497,34 +507,35 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
} else this._editMaxWidth = "";
- return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
- <li className="collection-child">
- <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
- if (this.props.active(true)) {
- e.stopPropagation();
- e.preventDefault();
- SelectionManager.DeselectAll();
- }
- }}
- onPointerDown={e => {
+ return this.doc.treeViewHideTitle && this.props.firstLevel ? this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent :
+ <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
+ <li className="collection-child">
+ <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
if (this.props.active(true)) {
e.stopPropagation();
e.preventDefault();
+ SelectionManager.DeselectAll();
}
}}
- onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.renderBullet}
- {this.renderTitle}
- </div>
- <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
- </div>
- </li>
- </div>;
+ onPointerDown={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }}
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ {this.renderBullet}
+ {this.renderTitle}
+ </div>
+ <div className="treeViewItem-border" style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
+ </div>
+ </li>
+ </div>;
}
public static GetChildElements(
childDocs: Doc[],
- treeViewDoc: Doc,
+ treeView: CollectionTreeView,
containingCollection: Doc,
dataDoc: Doc | undefined,
key: string,
@@ -546,10 +557,11 @@ class TreeView extends React.Component<TreeViewProps> {
treeViewHideHeaderFields: () => boolean,
treeViewPreventOpen: boolean,
renderedIds: string[],
- libraryPath: Doc[] | undefined,
onCheckedClick: undefined | (() => ScriptField),
onChildClick: undefined | (() => ScriptField),
- ignoreFields: string[] | undefined
+ ignoreFields: string[] | undefined,
+ firstLevel: boolean,
+ whenActiveChanged: (isActive: boolean) => void
) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
@@ -601,7 +613,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
const indent = i === 0 ? undefined : () => {
- if (StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
+ if (remove && StrCast(docs[i - 1].layout).indexOf('fieldKey') !== -1) {
const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
const fieldKey = fieldKeysub.split("\'")[1];
if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
@@ -612,7 +624,7 @@ class TreeView extends React.Component<TreeViewProps> {
}
};
const outdent = !parentCollectionDoc ? undefined : () => {
- if (StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
+ if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
const fieldKey = fieldKeysub.split("\'")[1];
Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
@@ -631,17 +643,16 @@ class TreeView extends React.Component<TreeViewProps> {
return !(child instanceof Doc) ? (null) : <TreeView
document={pair.layout}
dataDoc={pair.data}
- libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined}
containingCollection={containingCollection}
prevSibling={docs[i]}
- treeViewDoc={treeViewDoc}
+ treeView={treeView}
key={child[Id]}
indentDocument={indent}
outdentDocument={outdent}
onCheckedClick={onCheckedClick}
onChildClick={onChildClick}
renderDepth={renderDepth}
- deleteDoc={remove}
+ removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove}
addDocument={addDocument}
backgroundColor={backgroundColor}
panelWidth={rowWidth}
@@ -658,7 +669,9 @@ class TreeView extends React.Component<TreeViewProps> {
treeViewHideHeaderFields={treeViewHideHeaderFields}
treeViewPreventOpen={treeViewPreventOpen}
renderedIds={renderedIds}
- ignoreFields={ignoreFields} />;
+ ignoreFields={ignoreFields}
+ firstLevel={firstLevel}
+ whenActiveChanged={whenActiveChanged} />;
});
}
}
@@ -675,6 +688,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
private treedropDisposer?: DragManager.DragDropDisposer;
private _mainEle?: HTMLDivElement;
+ public _uniqueId = Utils.GenerateGuid();
@computed get doc() { return this.props.Document; }
@computed get dataDoc() { return this.props.DataDoc || this.doc; }
@@ -725,14 +739,14 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
- if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) {
- ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
- ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" });
+ if (!e.isPropagationStopped() && this.doc === CurrentUserUtils.MyDashboards) {
+ ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" });
+ ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) {
- ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List<Doc>(), icon: "plus" });
+ } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosedDocs) {
+ ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosedDocs = new List<Doc>(), icon: "plus" });
e.stopPropagation();
e.preventDefault();
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
@@ -788,6 +802,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
onClicks.push({
description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit"
});
+ onClicks.push({
+ description: `${this.props.Document.treeViewOutlineMode ? "Delay " : "Immediate "} Title Editing`, event: () => UndoManager.RunInBatch(() => this.props.Document.treeViewOutlineMode = !this.props.Document.treeViewOutlineMode, "edit onCheckedClick"), icon: "edit"
+ });
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
}
outerXf = () => Utils.GetScreenTransform(this._mainEle!);
@@ -805,6 +822,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
onChildClick = () => {
return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
}
+ _isChildActive = false;
+ whenActiveChanged = (isActive: boolean) => {
+ this.props.whenActiveChanged(this._isChildActive = isActive);
+ }
+ active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
render() {
TraceMobx();
if (!(this.doc instanceof Doc)) return (null);
@@ -812,6 +834,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc);
const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs;
+ const childElements = childDocs && TreeView.GetChildElements(childDocs, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
+ BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick,
+ this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged);
+ const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
+
return !childDocs ? (null) : (
<div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
<div className="collectionTreeView-dropTarget" id="body"
@@ -820,12 +849,12 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`,
paddingRight: `${NumCast(this.doc._xPadding, 10)}px`,
paddingTop: `${NumCast(this.doc._yPadding, 20)}px`,
- pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined,
}}
onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
- {this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : <EditableView
+ {hideTitle ? (null) : <EditableView
contents={this.dataDoc.title}
editing={false}
display={"block"}
@@ -840,42 +869,13 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
})} />}
{this.doc.allowClear ? this.renderClearButton : (null)}
- <ul className="no-indent" style={{ width: "max-content" }} >
- {
- TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
- this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
- BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick,
- this.onChildClick, this.props.ignoreFields)
- }
- </ul>
+ <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
</div >
</div>
);
}
}
-Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) {
- const allCollectionDocs = DocListCast(dataDoc[dataKey]);
- const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
- set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
-
- let nonNumbers = 0;
- facetValues.map(val => {
- const num = Number(val);
- if (Number.isNaN(num)) {
- nonNumbers++;
- }
- });
- const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
- const doc = new Doc();
- doc.title = facetValue.toString();
- doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
- return doc;
- });
- return new List<Doc>(facetValueDocSet);
-});
-
Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
for (let i = 0; i < docFilters.length; i += 3) {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6dd21ef7f..ba8e23447 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,6 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit, faEye } from '@fortawesome/free-regular-svg-icons';
-import { faColumns, faCopy, faEllipsisV, faFingerprint, faGlobeAmericas, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable } from 'mobx';
import { observer } from "mobx-react";
@@ -18,7 +15,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -53,8 +50,6 @@ export const Flyout = higflyout.default;
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
-library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faGlobeAmericas, faEllipsisV, faImage, faEye as any, faCopy);
-
export enum CollectionViewType {
Invalid = "invalid",
Freeform = "freeform",
@@ -69,7 +64,7 @@ export enum CollectionViewType {
Carousel = "carousel",
Carousel3D = "3D Carousel",
Linear = "linear",
- Staff = "staff",
+ //Staff = "staff",
Map = "map",
Grid = "grid",
Pile = "pileup"
@@ -139,6 +134,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
}
const docs = doc instanceof Doc ? [doc] : doc;
+
+
+ if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false;
const targetDataDoc = this.props.Document[DataSym];
const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
const added = docs.filter(d => !docList.includes(d));
@@ -159,7 +157,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
}
if (effectiveAcl === AclAddonly) {
- added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
+ added.map(doc => {
+ Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
+ doc.context = this.props.Document;
+ });
}
else {
added.map(doc => {
@@ -177,14 +178,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
doc.displayTimecode = undefined;
}
+ doc._stayInCollection = undefined;
doc.context = this.props.Document;
});
- added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
- // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
(targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
- const lastModified = "lastModified";
- targetDataDoc[lastModified] = new DateField(new Date(Date.now()));
}
}
}
@@ -200,11 +198,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const toRemove = value.filter(v => docs.includes(v));
if (toRemove.length !== 0) {
- const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc;
+ const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
toRemove.forEach(doc => {
Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
- doc.deleted = true;
});
return true;
}
@@ -253,7 +250,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
- case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
+ //case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />);
case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />);
case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />);
case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
@@ -274,7 +271,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return this.SubViewHelper(type, renderProps);
}
-
setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
const subItems: ContextMenuProps[] = [];
@@ -286,7 +282,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" });
subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
- subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" });
subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
@@ -309,7 +304,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
this.setupViewTypes("UI Controls...", vtype => {
const newRendition = Doc.MakeAlias(this.props.Document);
newRendition._viewType = vtype;
- this.props.addDocTab(newRendition, "onRight");
+ this.props.addDocTab(newRendition, "add:right");
return newRendition;
}, false);
@@ -317,10 +312,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const optionItems = options && "subitems" in options ? options.subitems : [];
!Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null;
if (this.props.Document.childLayout instanceof Doc) {
- optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" });
+ optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" });
}
if (this.props.Document.childClickedOpenTemplateView instanceof Doc) {
- optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" });
+ optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
}
!Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" });
@@ -335,7 +330,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
const alias = Doc.MakeAlias(this.props.Document);
DocUtils.makeCustomViewClicked(alias, undefined, func.key);
- this.props.addDocTab(alias, "onRight");
+ this.props.addDocTab(alias, "add:right");
}
}));
DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick =>
@@ -371,6 +366,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)}
onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />);
}
+
get _facetWidth() { return NumCast(this.props.Document._facetWidth); }
set _facetWidth(value) { this.props.Document._facetWidth = value; }
@@ -394,9 +390,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout);
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
}
+
get childDocList() {
return Cast(this.dataField, listSpec(Doc));
}
+
get childDocs() {
const dfield = this.dataField;
const rawdocs = (dfield instanceof Doc) ? [dfield] : Cast(dfield, listSpec(Doc), Cast(this.props.Document.rootDocument, Doc, null) ? [Cast(this.props.Document.rootDocument, Doc, null)] : []);
@@ -404,91 +402,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const viewSpecScript = ScriptCast(this.props.Document.viewSpecScript);
return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
}
- @computed get _allFacets() {
- TraceMobx();
- const facets = new Set<string>(["type", "text", "data", "author", "ACL"]);
- this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
- Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key)));
- return Array.from(facets).filter(f => !f.startsWith("_") && !["proto", "zIndex", "isPrototype", "context", "text-noTemplate"].includes(f)).sort();
- }
-
- /**
- * Responds to clicking the check box in the flyout menu
- */
- facetClick = (facetHeader: string) => {
- const facetCollection = this.props.Document;
- const found = DocListCast(facetCollection[this.props.fieldKey + "-filter"]).findIndex(doc => doc.title === facetHeader);
- if (found !== -1) {
- (facetCollection[this.props.fieldKey + "-filter"] as List<Doc>).splice(found, 1);
- const docFilter = Cast(this.props.Document._docFilters, listSpec("string"));
- if (docFilter) {
- let index: number;
- while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
- docFilter.splice(index, 3);
- }
- }
- const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"));
- if (docRangeFilters) {
- let index: number;
- while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) {
- docRangeFilters.splice(index, 3);
- }
- }
- } else {
- const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
- var rtfields = 0;
- const facetValues = Array.from(allCollectionDocs.reduce((set, child) => {
- const field = child[facetHeader] as Field;
- const fieldStr = Field.toString(field);
- if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++;
- return set.add(fieldStr);
- }, new Set<string>()));
-
- let nonNumbers = 0;
- let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
- facetValues.map(val => {
- const num = Number(val);
- if (Number.isNaN(num)) {
- nonNumbers++;
- } else {
- minVal = Math.min(num, minVal);
- maxVal = Math.max(num, maxVal);
- }
- });
- let newFacet: Opt<Doc>;
- if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) {
- newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
- Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
- newFacet.target = this.props.Document;
- newFacet._textBoxPadding = 4;
- const scriptText = `setDocFilter(this.target, "${facetHeader}", text, "match")`;
- newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
- } else if (nonNumbers / facetValues.length < .1) {
- newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
- const newFacetField = Doc.LayoutFieldKey(newFacet);
- const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader);
- Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
- const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
- const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
- newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
- newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
- Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
- Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
- newFacet.target = this.props.Document;
- const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`;
- newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
- Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
- } else {
- newFacet = new Doc();
- newFacet.title = facetHeader;
- newFacet.treeViewOpen = true;
- newFacet.type = DocumentType.COL;
- const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
- newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables);
- }
- newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet);
- }
- }
onPointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
@@ -497,77 +410,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
}), returnFalse, action(() => this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false);
}
- filterBackground = () => "rgba(105, 105, 105, 0.432)";
- get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
- @computed get scriptField() {
- const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
- const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
- return script ? () => script : undefined;
- }
- @computed get filterView() {
- TraceMobx();
- const facetCollection = this.props.Document;
- const flyout = (
- <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
- {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
- <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} />
- <span className="checkmark" />
- {facet}
- </label>)}
- </div>
- );
-
- return !this._facetWidth || this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}>
- <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}>
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
- <div className="collectionTimeView-button">
- <FontAwesomeIcon icon={faEdit} size={"lg"} />
- <span className="collectionTimeView-span">Facet Filters</span>
- </div>
- </Flyout>
- </div>
- <div className="collectionTimeView-tree" key="tree">
- <CollectionTreeView
- PanelPosition={""}
- Document={facetCollection}
- DataDoc={facetCollection}
- fieldKey={`${this.props.fieldKey}-filter`}
- CollectionView={this}
- docFilters={returnEmptyFilter}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- ContainingCollectionView={this.props.ContainingCollectionView}
- PanelWidth={this.facetWidth}
- PanelHeight={this.props.PanelHeight}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- LibraryPath={emptyPath}
- rootSelected={this.props.rootSelected}
- renderDepth={1}
- dropAction={this.props.dropAction}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- addDocTab={returnFalse}
- pinToPres={returnFalse}
- isSelected={returnFalse}
- select={returnFalse}
- bringToFront={emptyFunction}
- active={this.props.active}
- whenActiveChanged={returnFalse}
- treeViewHideTitle={true}
- ContentScaling={returnOne}
- focus={returnFalse}
- treeViewHideHeaderFields={true}
- onCheckedClick={this.scriptField}
- ignoreFields={this.ignoreFields}
- annotationsKey={""}
- dontRegisterView={true}
- backgroundColor={this.filterBackground}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- addDocument={returnFalse} />
- </div>
- </div>;
- }
-
childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString);
@@ -584,10 +426,10 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
ChildLayoutTemplate: this.childLayoutTemplate,
ChildLayoutString: this.childLayoutString,
};
- const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
- `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+ const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
+ `${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
- style={{ pointerEvents: this.props.Document.isBackground ? "none" : undefined, boxShadow }}>
+ style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
{this.showIsTagged()}
<div className="collectionView-facetCont" style={{ display: this.props.PanelPosition === "absolute" ? "flex" : "", justifyContent: this.props.PanelPosition === "absolute" ? "center" : "", width: `calc(100% - ${this.facetWidth()}px)` }}>
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
@@ -598,11 +440,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href
:
""))}
- {(Doc.UserDoc()?.noviceMode || !this.props.isSelected() && !this.props.Document.forceActive) || this.props.Document.hideFilterView ? (null) :
- <div className="collectionView-filterDragger" title="library View Dragger" onPointerDown={this.onPointerDown}
- style={{ right: this.facetWidth() - 1, top: this.props.Document._viewType === CollectionViewType.Docking ? "25%" : "60%" }} />
- }
- {Doc.UserDoc()?.noviceMode || this.facetWidth() < 10 ? (null) : this.filterView}
</div>);
}
}
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
deleted file mode 100644
index 149d4927b..000000000
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import * as React from "react";
-import './ParentDocumentSelector.scss';
-import { Doc } from "../../../fields/Doc";
-import { observer } from "mobx-react";
-import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx";
-import { Id } from "../../../fields/FieldSymbols";
-import { SearchUtil } from "../../util/SearchUtil";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { NumCast, StrCast } from "../../../fields/Types";
-import { CollectionViewType } from "./CollectionView";
-import { DocumentButtonBar } from "../DocumentButtonBar";
-import { DocumentManager } from "../../util/DocumentManager";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faCog, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { DocumentView } from "../nodes/DocumentView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Tooltip } from "@material-ui/core";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-library.add(faCog);
-
-type SelectorProps = {
- Document: Doc,
- Stack?: any,
- addDocTab(doc: Doc, location: string): void
-};
-
-@observer
-export class SelectorContextMenu extends React.Component<SelectorProps> {
- @observable private _docs: { col: Doc, target: Doc }[] = [];
- @observable private _otherDocs: { col: Doc, target: Doc }[] = [];
- _reaction: IReactionDisposer | undefined;
-
- componentDidMount() {
- this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true });
- }
- componentWillUnmount() {
- this._reaction?.();
- }
- async fetchDocuments() {
- const aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document));
- const containerProtoSets = await Promise.all(aliases.map(async alias =>
- ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs)));
- const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
- const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => {
- return (SearchUtil.GetAliasesOfDocument(container));
- }));
- const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
- const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => {
- return (SearchUtil.GetAliasesOfDocument(dp));
- }));
- const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>()).keys());
- runInAction(() => {
- this._docs = doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: this.props.Document }));
- this._otherDocs = [];
- });
- }
-
- getOnClick({ col, target }: { col: Doc, target: Doc }) {
- return () => {
- col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
- if (col._viewType === CollectionViewType.Freeform) {
- const newPanX = NumCast(target.x) + NumCast(target._width) / 2;
- const newPanY = NumCast(target.y) + NumCast(target._height) / 2;
- col._panX = newPanX;
- col._panY = newPanY;
- }
- this.props.addDocTab(col, "inTab"); // bcz: dataDoc?
- };
- }
-
- render() {
- return <div >
- <p key="contexts">Contexts:</p>
- {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title?.toString()}</a></p>)}
- {this._otherDocs.length ? <hr key="hr" /> : null}
- {this._otherDocs.map(doc => <p key={"p" + doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title?.toString()}</a></p>)}
- </div>;
- }
-}
-
-@observer
-export class ParentDocSelector extends React.Component<SelectorProps> {
- render() {
- const flyout = (
- <div className="parentDocumentSelector-flyout" title=" ">
- <SelectorContextMenu {...this.props} />
- </div>
- );
- return <div title="Show Contexts" onPointerDown={e => e.stopPropagation()} className="parentDocumentSelector-linkFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
- <span className="parentDocumentSelector-button" >
- <FontAwesomeIcon icon={faChevronCircleUp} size={"lg"} />
- </span>
- </Flyout>
- </div>;
- }
-}
-
-@observer
-export class DockingViewButtonSelector extends React.Component<{ views: () => DocumentView[], Stack: any }> {
- customStylesheet(styles: any) {
- return {
- ...styles,
- panel: {
- ...styles.panel,
- minWidth: "100px"
- },
- };
- }
- _ref = React.createRef<HTMLDivElement>();
-
- @computed get flyout() {
- return (
- <div className="ParentDocumentSelector-flyout" title=" " ref={this._ref}>
- <DocumentButtonBar views={this.props.views} stack={this.props.Stack} />
- </div>
- );
- }
-
- render() {
- return <Tooltip title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
- <span onPointerDown={e => {
- if (getComputedStyle(this._ref.current!).width !== "100%") {
- e.stopPropagation(); e.preventDefault();
- }
- this.props.views()[0]?.select(false);
- }} className="buttonSelector">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
- <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
- </Flyout>
- </span>
- </Tooltip>;
- }
-}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index a2c529a21..1fb7aa04a 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -5,16 +5,15 @@ import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
import "react-table/react-table.css";
-import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, Opt, AclPrivate, AclReadonly, DataSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { ComputedField } from "../../../fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -22,11 +21,15 @@ import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell } from "./CollectionSchemaCells";
import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
import { CollectionView } from "./CollectionView";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { GetEffectiveAcl } from "../../../fields/util";
+import { DateField } from "../../../fields/DateField";
+import { ImageField } from "../../../fields/URLField";
enum ColumnType {
@@ -45,7 +48,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
["title", ColumnType.String],
["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
- ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+ ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
]);
export interface SchemaTableProps {
@@ -70,11 +73,12 @@ export interface SchemaTableProps {
isSelected: (outsideReaction?: boolean) => boolean;
isFocused: (document: Doc, outsideReaction: boolean) => boolean;
setFocused: (document: Doc) => void;
- setPreviewDoc: (document: Doc) => void;
+ setPreviewDoc: (document: Opt<Doc>) => void;
columns: SchemaHeaderField[];
documentKeys: any[];
headerIsEditing: boolean;
openHeader: (column: any, screenx: number, screeny: number) => void;
+ onClick: (e: React.MouseEvent) => void;
onPointerDown: (e: React.PointerEvent) => void;
onResizedChange: (newResized: Resize[], event: any) => void;
setColumns: (columns: SchemaHeaderField[]) => void;
@@ -90,7 +94,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@observable _cellIsEditing: boolean = false;
@observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 };
- @observable _openCollections: Array<string> = [];
+ @observable _openCollections: Set<number> = new Set;
@observable _showDoc: Doc | undefined;
@observable _showDataDoc: any = "";
@@ -135,16 +139,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeSorting = (col: any) => {
- if (col.desc === undefined) {
- // no sorting
- this.props.changeColumnSort(col, true);
- } else if (col.desc === true) {
- // descending sort
- this.props.changeColumnSort(col, false);
- } else if (col.desc === false) {
- // ascending sort
- this.props.changeColumnSort(col, undefined);
- }
+ this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
}
@action
@@ -152,7 +147,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
-
const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
const columns: Column<Doc>[] = [];
const tableIsFocused = this.props.isFocused(this.props.Document, false);
@@ -160,26 +154,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const focusedCol = this._focusedCell.col;
const isEditable = !this.props.headerIsEditing;
- if (this.childDocs.reduce((found, doc) => found || doc.type === DocumentType.COL, false)) {
- columns.push(
- {
- expander: true,
- Header: "",
- width: 30,
- Expander: (rowInfo) => {
- if (rowInfo.original.type === "collection") {
- if (rowInfo.isExpanded) return <div className="collectionSchemaView-expander" onClick={() => this.onCloseCollection(rowInfo.original)}><FontAwesomeIcon icon={"sort-up"} size="sm" /></div>;
- if (!rowInfo.isExpanded) return <div className="collectionSchemaView-expander" onClick={() => this.onExpandCollection(rowInfo.original)}><FontAwesomeIcon icon={"sort-down"} size="sm" /></div>;
- } else {
- return null;
- }
- }
- }
- );
- }
- this.props.active;
-
- const cols = this.props.columns.map(col => {
+ columns.push({
+ expander: true, Header: "", width: 58,
+ Expander: (rowInfo) => {
+ return rowInfo.original.type !== DocumentType.COL ? (null) :
+ <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}>
+ <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" />
+ </div>;
+ }
+ });
+ columns.push(...this.props.columns.map(col => {
const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" :
this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" :
@@ -207,29 +191,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
width={"100%"}
/>;
-
-
const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
-
- const header =
- <div //className="collectionSchemaView-header"
- //onClick={e => this.props.openHeader(col, menuContent, e.clientX, e.clientY)}
- className="collectionSchemaView-menuOptions-wrapper"
- style={{
- background: col.color, padding: "2px",
- display: "flex", cursor: "default", height: "100%",
- }}>
- {/* <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> */}
- {keysDropdown}
- <div onClick={e => this.changeSorting(col)}
- style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
- <FontAwesomeIcon icon={sortIcon} size="lg" />
- </div>
- </div>;
+ const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
+ {keysDropdown}
+ <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
+ <FontAwesomeIcon icon={sortIcon} size="lg" />
+ </div>
+ </div>;
return {
Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
- accessor: (doc: Doc) => doc ? doc[col.heading] : 0,
+ accessor: (doc: Doc) => doc ? Field.toString(doc[col.heading] as Field) : 0,
id: col.heading,
Cell: (rowProps: CellInfo) => {
const rowIndex = rowProps.index;
@@ -258,21 +230,22 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
showDoc: this.showDoc,
};
- const colType = this.getColumnType(col);
- if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />;
- if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
- if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
- if (colType === ColumnType.Doc) return <CollectionSchemaDocCell {...props} />;
- if (colType === ColumnType.Image) return <CollectionSchemaImageCell {...props} />;
- if (colType === ColumnType.List) return <CollectionSchemaListCell {...props} />;
- if (colType === ColumnType.Date) return <CollectionSchemaDateCell {...props} />;
- return <CollectionSchemaCell {...props} />;
+
+ switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
+ case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />;
+ case ColumnType.String: return <CollectionSchemaStringCell {...props} />;
+ case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />;
+ case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />;
+ case ColumnType.Image: return <CollectionSchemaImageCell {...props} />;
+ case ColumnType.List: return <CollectionSchemaListCell {...props} />;
+ case ColumnType.Date: return <CollectionSchemaDateCell {...props} />;
+ default:
+ return <CollectionSchemaCell {...props} />;
+ }
},
minWidth: 200,
};
- });
- columns.push(...cols);
-
+ }));
columns.push({
Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
accessor: (doc: Doc) => 0,
@@ -281,8 +254,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const rowIndex = rowProps.index;
const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
- const props: CellProps = {
- row: rowIndex,
+ return <CollectionSchemaButtons {...{
+ row: rowProps.index,
col: columnIndex,
rowProps: rowProps,
isFocused: isFocused,
@@ -301,9 +274,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setComputed: this.setComputed,
getField: this.getField,
showDoc: this.showDoc,
- };
-
- return <CollectionSchemaButtons {...props} />;
+ }} />;
},
width: 28,
resizable: false
@@ -314,14 +285,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
constructor(props: SchemaTableProps) {
super(props);
- // convert old schema columns (list of strings) into new schema columns (list of schema header fields)
- const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
- if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
- const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb"),
- new SchemaHeaderField("text", "#f1efeb"), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb")]);
+ if (this.props.Document._schemaHeaders === undefined) {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date),
+ new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]);
}
}
@@ -334,7 +300,15 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
- return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+ const tableDoc = this.props.Document[DataSym];
+ const effectiveAcl = GetEffectiveAcl(tableDoc);
+
+ if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
+ doc.context = this.props.Document;
+ tableDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
+ }
+ return false;
}
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
@@ -360,19 +334,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
// TODO: editing border doesn't work :(
return {
- style: {
- border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb"
- }
+ style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" }
};
}
- @action
- onCloseCollection = (collection: Doc): void => {
- const index = this._openCollections.findIndex(col => col === collection[Id]);
- if (index > -1) this._openCollections.splice(index, 1);
- }
-
- @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]);
@action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
@action
@@ -383,6 +348,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const pdoc = FieldValue(this.childDocs[this._focusedCell.row]);
pdoc && this.props.setPreviewDoc(pdoc);
+ e.stopPropagation();
+ } else if (e.keyCode === 27) {
+ this.props.setPreviewDoc(undefined);
+ e.stopPropagation(); // stopPropagation for left/right arrows
}
}
@@ -406,9 +375,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@undoBatch
- createRow = () => {
- this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
- }
+ createRow = action(() => {
+ this.props.addDocument(Docs.Create.TextDocument("", { title: "", _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 30 }));
+ this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
+ });
@undoBatch
@action
@@ -423,22 +393,21 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@action
- getColumnType = (column: SchemaHeaderField): ColumnType => {
- // added functionality to convert old column type stuff to new column type stuff -syip
+ getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
+ if (doc && field && column.type === ColumnType.Any) {
+ const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)];
+ if (val instanceof ImageField) return ColumnType.Image;
+ if (val instanceof Doc) return ColumnType.Doc;
+ if (val instanceof DateField) return ColumnType.Date;
+ if (val instanceof List) return ColumnType.List;
+ }
if (column.type && column.type !== 0) {
return column.type;
}
if (columnTypes.get(column.heading)) {
- column.type = columnTypes.get(column.heading)!;
- return columnTypes.get(column.heading)!;
- }
- const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc));
- if (!typesDoc) {
- column.type = ColumnType.Any;
- return ColumnType.Any;
+ return column.type = columnTypes.get(column.heading)!;
}
- column.type = NumCast(typesDoc[column.heading]);
- return NumCast(typesDoc[column.heading]);
+ return column.type = ColumnType.Any;
}
@undoBatch
@@ -467,11 +436,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed
get reactTable() {
const children = this.childDocs;
- const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
- const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
- const expanded = {};
- //@ts-ignore
- expandedRowsList.forEach(row => expanded[row] = true);
+ const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
+ const expanded: { [name: string]: any } = {};
+ Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true);
const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
return <ReactTable
@@ -489,17 +456,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
expanded={expanded}
resized={this.resized}
onResizedChange={this.props.onResizedChange}
- SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
+ SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
<div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
/>;
}
onContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" });
- ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
- }
+ ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
}
getField = (row: number, col?: number) => {
@@ -560,10 +524,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
script =
`const $ = (row:number, col?:number) => {
- if(col === undefined) {
- return (doc as any)[key][row + ${row}];
- }
- return (doc as any)[key][row + ${row}][(doc as any)._schemaHeaders[col + ${col}].heading];
+ const rval = (doc as any)[key][row + ${row}];
+ return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
}
return ${script}`;
const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) });
@@ -583,9 +545,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
onOpenClick = () => {
- if (this._showDoc) {
- this.props.addDocTab(this._showDoc, "onRight");
- }
+ this._showDoc && this.props.addDocTab(this._showDoc, "add:right");
}
getPreviewTransform = (): Transform => {
@@ -594,13 +554,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
render() {
const preview = "";
- return <div className="collectionSchemaView-table" onPointerDown={this.props.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()}
+ return <div className="collectionSchemaView-table"
+ onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
- {StrCast(this.props.Document.type) !== "search" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ {StrCast(this.props.Document._chromeStatus) !== "disabled" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
: undefined}
{!this._showDoc ? (null) :
- <div className="collectionSchemaView-documentPreview" //onClick={() => { this.onOpenClick(); }}
+ <div className="collectionSchemaView-documentPreview"
style={{
position: "absolute", width: 150, height: 150,
background: "dimGray", display: "block", top: 0, left: 0,
@@ -621,6 +582,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
PanelHeight={() => 150}
ScreenToLocalTransform={this.getPreviewTransform}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
new file mode 100644
index 000000000..fdb801e03
--- /dev/null
+++ b/src/client/views/collections/TabDocView.scss
@@ -0,0 +1,22 @@
+input.lm_title:focus {
+ max-width: max-content !important;
+}
+.miniMap {
+ position: absolute;
+ overflow: hidden;
+ right: 10;
+ bottom: 10;
+ border: solid 1px;
+ box-shadow: black 0.4vw 0.4vw 0.8vw;
+
+ .miniOverlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+
+ .miniThumb {
+ background: #25252525;
+ position: absolute;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
new file mode 100644
index 000000000..fb4f5c366
--- /dev/null
+++ b/src/client/views/collections/TabDocView.tsx
@@ -0,0 +1,383 @@
+import 'golden-layout/src/css/goldenlayout-base.css';
+import 'golden-layout/src/css/goldenlayout-dark-theme.css';
+import { clamp } from 'lodash';
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { observer } from "mobx-react";
+import * as ReactDOM from 'react-dom';
+import { DataSym, Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { Id } from '../../../fields/FieldSymbols';
+import { FieldId } from "../../../fields/RefField";
+import { listSpec } from '../../../fields/Schema';
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils";
+import { DocServer } from "../../DocServer";
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from "../../util/DragManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { DocumentView } from "../nodes/DocumentView";
+import { PresBox } from '../nodes/PresBox';
+import { CollectionDockingView } from './CollectionDockingView';
+import "./TabDocView.scss";
+import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionViewType } from './CollectionView';
+import React = require("react");
+const _global = (window /* browser */ || global /* node */) as any;
+
+interface TabDocViewProps {
+ documentId: FieldId;
+ glContainer: any;
+}
+@observer
+export class TabDocView extends React.Component<TabDocViewProps> {
+ _mainCont: HTMLDivElement | null = null;
+ _tabReaction: IReactionDisposer | undefined;
+ @observable private _panelWidth = 0;
+ @observable private _panelHeight = 0;
+ @observable private _isActive: boolean = false;
+ @observable private _document: Doc | undefined;
+ @observable private _view: DocumentView | undefined;
+
+ get stack(): any { return (this.props as any).glContainer.parent.parent; }
+ get tab() { return (this.props as any).glContainer.tab; }
+ get view() { return this._view; }
+
+ @action
+ init = (tab: any, doc: Opt<Doc>) => {
+ if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+ tab._disposers = {} as { [name: string]: IReactionDisposer };
+ tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
+ tab.DashDoc = doc;
+ CollectionDockingView.Instance.tabMap.add(tab);
+
+ // setup the title element and set its size according to the # of chars in the title. Show the full title when clicked.
+ const titleEle = tab.titleElement[0];
+ titleEle.size = StrCast(doc.title).length + 3;
+ titleEle.value = doc.title;
+ titleEle.style["max-width"] = "100px";
+ titleEle.onchange = (e: any) => {
+ titleEle.size = e.currentTarget.value.length + 3;
+ Doc.GetProto(doc).title = e.currentTarget.value;
+ };
+ // shifts the focus to this tab when another tab is dragged over it
+ tab.element[0].onmouseenter = (e: MouseEvent) => {
+ if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
+ tab.header.parent.setActiveContentItem(tab.contentItem);
+ console.log("Seetting " + titleEle.value);
+ tab.setActive(true);
+ }
+ };
+ const onPointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, (e) => {
+ !e.defaultPrevented && DragManager.StartDocumentDrag([dragHdl], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY);
+ return !e.defaultPrevented;
+ }, returnFalse, emptyFunction);
+ };
+
+ // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
+ tab.element[0].onclick = (e: any) => {
+ if (e.target.className !== "lm_close_tab" && this.view) {
+ SelectionManager.SelectDoc(this.view, false);
+ if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
+ titleEle.lastClick = Date.now();
+ (document.activeElement !== titleEle) && titleEle.focus();
+ }
+ };
+ tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.topMost && v.props.Document === doc),
+ (selected) => selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
+ UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch"));
+
+ //attach the selection doc buttons menu to the drag handle
+ const stack = tab.contentItem.parent;
+ const dragHdl = document.createElement("span");
+ dragHdl.className = "collectionDockingView-gear";
+ dragHdl.style.position = "relative";
+ dragHdl.style.paddingLeft = "0px";
+ dragHdl.style.paddingRight = "12px";
+ tab._disposers.buttonDisposer = reaction(() => this.view, (view) => view &&
+ [ReactDOM.render(
+ <span title="Drag as document" className="collectionDockingView-drag" onPointerDown={onPointerDown} >
+ <CollectionDockingViewMenu views={() => [view]} Stack={stack} />
+ </span>, dragHdl),
+ tab._disposers.buttonDisposer?.()],
+ { fireImmediately: true });
+ tab.reactComponents = [dragHdl];
+ tab.element.append(dragHdl);
+
+ // highlight the tab when the tab document is brushed in any part of the UI
+ tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
+ titleEle.value = title;
+ titleEle.style.padding = degree ? 0 : 2;
+ titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
+ }, { fireImmediately: true });
+
+ // clean up the tab when it is closed
+ tab.closeElement.off('click') //unbind the current click handler
+ .click(function () {
+ Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+ Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
+ SelectionManager.DeselectAll();
+ tab.contentItem.remove();
+ });
+ }
+ }
+ /**
+ * Adds a document to the presentation view
+ **/
+ @undoBatch
+ @action
+ public static PinDoc(doc: Doc, unpin = false) {
+ if (unpin) TabDocView.UnpinDoc(doc);
+ else {
+ //add this new doc to props.Document
+ const curPres = CurrentUserUtils.ActivePresentation;
+ if (curPres) {
+ const pinDoc = Doc.MakeAlias(doc);
+ pinDoc.presentationTargetDoc = doc;
+ pinDoc.presZoomButton = true;
+ pinDoc.context = curPres;
+ Doc.AddDocToList(curPres, "data", pinDoc);
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+ if (!DocumentManager.Instance.getDocumentView(curPres)) {
+ CollectionDockingView.AddSplit(curPres, "right");
+ }
+ DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null));
+ }
+ }
+ }
+ /**
+ * Adds a document to the presentation view
+ **/
+ @undoBatch
+ @action
+ public static UnpinDoc(doc: Doc) {
+ //add this new doc to props.Document
+ const curPres = CurrentUserUtils.ActivePresentation;
+ if (curPres) {
+ const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
+ ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
+ }
+ }
+
+ componentDidMount() {
+ const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
+ const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document);
+ const updateTabColor = () => this.tab?.titleElement[0] && (this.tab.titleElement[0].style.backgroundColor = selected() ? color() : "");
+ const observer = new _global.ResizeObserver(action((entries: any) => {
+ for (const entry of entries) {
+ this._panelWidth = entry.contentRect.width;
+ this._panelHeight = entry.contentRect.height;
+ }
+ updateTabColor();
+ }));
+ observer.observe(this.props.glContainer._element[0]);
+ this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
+ this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged();
+ this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), updateTabColor, { fireImmediately: true });
+ }
+
+ componentWillUnmount() {
+ this._tabReaction?.();
+ this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
+ }
+
+ @action.bound
+ private onActiveContentItemChanged() {
+ if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) {
+ this._isActive = this.props.glContainer.tab.isActive;
+ this._isActive && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0);
+ (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged();
+ !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
+ }
+ }
+
+ get layoutDoc() { return this._document && Doc.Layout(this._document); }
+ nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0;
+ panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
+ (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
+ panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
+ nativeWidth = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeWidth) || this._panelWidth : 0;
+ nativeHeight = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeHeight) || this._panelHeight : 0;
+
+ contentScaling = () => {
+ const nativeH = this.nativeHeight();
+ const nativeW = this.nativeWidth();
+ let scaling = 1;
+ if (!this.layoutDoc?._fitWidth && (!nativeW || !nativeH)) {
+ scaling = 1;
+ } else if (NumCast(this.layoutDoc?._nativeWidth) && ((this.layoutDoc?._fitWidth) ||
+ this._panelHeight / NumCast(this.layoutDoc?._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc?._nativeWidth))) {
+ scaling = this._panelWidth / NumCast(this.layoutDoc?._nativeWidth);
+ } else if (nativeW && nativeH) {
+ const wscale = this.panelWidth() / nativeW;
+ scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
+ }
+ return scaling;
+ }
+
+ ScreenToLocalTransform = () => {
+ if (this._mainCont?.children) {
+ const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0]?.firstChild as HTMLElement);
+ const scale = Utils.GetScreenTransform(this._mainCont).scale;
+ return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
+ }
+ return Transform.Identity();
+ }
+ get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; }
+
+ // adds a tab to the layout based on the locaiton parameter which can be:
+ // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab,
+ // add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right
+ // replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
+ // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
+ // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
+ // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
+ addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
+ SelectionManager.DeselectAll();
+ if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+ const locationFields = location.split(":");
+ const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+ switch (locationFields[0]) {
+ case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
+ case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+ case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
+ case "inPlace":
+ case "add":
+ default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
+ }
+ }
+
+ @computed get renderContentBounds() {
+ const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
+ const xbounds = bounds[2] - bounds[0];
+ const ybounds = bounds[3] - bounds[1];
+ const dim = Math.max(xbounds, ybounds);
+ return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
+ }
+ @computed get miniLeft() { return 50 + (NumCast(this._document?._panX) - this.renderContentBounds.cx) / this.renderContentBounds.dim * 100 - this.miniWidth / 2; }
+ @computed get miniTop() { return 50 + (NumCast(this._document?._panY) - this.renderContentBounds.cy) / this.renderContentBounds.dim * 100 - this.miniHeight / 2; }
+ @computed get miniWidth() { return this.panelWidth() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
+ @computed get miniHeight() { return this.panelHeight() / NumCast(this._document?._viewScale, 1) / this.renderContentBounds.dim * 100; }
+ childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null);
+ returnMiniSize = () => NumCast(this._document?._miniMapSize, 150);
+ miniDown = (e: React.PointerEvent) => {
+ this._document && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+ this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim);
+ this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim);
+ return false;
+ }), emptyFunction, emptyFunction);
+ }
+ getCurrentFrame = (): number => {
+ const presTargetDoc = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null);
+ return Cast(presTargetDoc._currentFrame, "number", null);
+ }
+
+ renderMiniMap() {
+ return <div className="miniMap" style={{
+ width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor,
+ StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))),
+ }}>
+ <CollectionFreeFormView
+ Document={this._document!}
+ LibraryPath={emptyPath}
+ CollectionView={undefined}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid havin to set stuff like this.
+ noOverlay={true} // don't render overlay Docs since they won't scale
+ active={returnTrue}
+ select={emptyFunction}
+ dropAction={undefined}
+ isSelected={returnFalse}
+ dontRegisterView={true}
+ annotationsKey={""}
+ fieldKey={Doc.LayoutFieldKey(this._document!)}
+ bringToFront={emptyFunction}
+ rootSelected={returnTrue}
+ addDocument={returnFalse}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ ContentScaling={returnOne}
+ PanelWidth={this.returnMiniSize}
+ PanelHeight={this.returnMiniSize}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ ScreenToLocalTransform={this.ScreenToLocalTransform}
+ renderDepth={0}
+ whenActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+ addDocTab={this.addDocTab}
+ pinToPres={TabDocView.PinDoc}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+ fitToBox={true}
+ />
+ <div className="miniOverlay" onPointerDown={this.miniDown} >
+ <div className="miniThumb" style={{
+ width: `${this.miniWidth}% `,
+ height: `${this.miniHeight}% `,
+ left: `${this.miniLeft}% `,
+ top: `${this.miniTop}% `,
+ }}
+ />
+ </div>
+ </div>;
+ }
+ focusFunc = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => void) => afterFocus?.();
+ setView = action((view: DocumentView) => this._view = view);
+ @computed get docView() {
+ TraceMobx();
+ return !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
+ <><DocumentView key={this._document[Id]}
+ LibraryPath={emptyPath}
+ Document={this._document}
+ getView={this.setView}
+ DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
+ bringToFront={emptyFunction}
+ rootSelected={returnTrue}
+ addDocument={undefined}
+ removeDocument={undefined}
+ ContentScaling={this.contentScaling}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ NativeHeight={this.nativeHeight}
+ NativeWidth={this.nativeWidth}
+ ScreenToLocalTransform={this.ScreenToLocalTransform}
+ renderDepth={0}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ focus={this.focusFunc}
+ backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
+ addDocTab={this.addDocTab}
+ pinToPres={TabDocView.PinDoc}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
+ {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
+ </>;
+ }
+
+ render() {
+ return (<div className="collectionDockingView-content" ref={ref => {
+ if (this._mainCont = ref) {
+ (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
+ this.tab && DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.init(this.tab, this._document)));
+ }
+ }}
+ style={{
+ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`,
+ height: this.layoutDoc?._fitWidth ? undefined : "100%",
+ width: this.widthpercent
+ }}>
+ {this.docView}
+ </div >);
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index b00074cc6..bc2cb2d20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -10,6 +10,7 @@ import { Id, ToString } from "../../../../fields/FieldSymbols";
import { ObjectField } from "../../../../fields/ObjectField";
import { RefField } from "../../../../fields/RefField";
import { listSpec } from "../../../../fields/Schema";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
export interface ViewDefBounds {
type: string;
@@ -359,7 +360,7 @@ export function computeTimelineLayout(
groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 46e30f616..306dfe797 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,4 @@
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
@@ -15,7 +12,7 @@ import { ScriptField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { TraceMobx } from "../../../../fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils, setupMoveUpEvents } from "../../../../Utils";
+import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -24,10 +21,12 @@ import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager, dropActionType } from "../../../util/DragManager";
import { HistoryUtil } from "../../../util/History";
import { InteractionUtils } from "../../../util/InteractionUtils";
+import { LinkManager } from "../../../util/LinkManager";
+import { SearchUtil } from "../../../util/SearchUtil";
import { SelectionManager } from "../../../util/SelectionManager";
import { SnappingManager } from "../../../util/SnappingManager";
import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { undoBatch } from "../../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss";
import { Timeline } from "../../animationtimeline/Timeline";
import { ContextMenu } from "../../ContextMenu";
@@ -37,29 +36,25 @@ import { DocumentLinksButton } from "../../nodes/DocumentLinksButton";
import { DocumentViewProps } from "../../nodes/DocumentView";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
+import { PresBox } from "../../nodes/PresBox";
import { CollectionDockingView } from "../CollectionDockingView";
import { CollectionSubView } from "../CollectionSubView";
import { CollectionViewType } from "../CollectionView";
import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
-import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { PresBox } from "../../nodes/PresBox";
-import { SearchUtil } from "../../../util/SearchUtil";
-import { LinkManager } from "../../../util/LinkManager";
-
-library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
export const panZoomSchema = createSchema({
_panX: "number",
_panY: "number",
- currentTimecode: "number",
+ _currentTimecode: "number",
displayTimecode: "number",
- currentFrame: "number",
+ _currentFrame: "number",
arrangeInit: ScriptField,
- useClusters: "boolean",
+ _useClusters: "boolean",
fitToBox: "boolean",
_xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
_yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
@@ -178,8 +173,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
newBox.y = y;
}
}
- if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
- CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document.currentFrame);
+ if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document._currentFrame, true);
}
}
return retVal;
@@ -189,7 +184,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
}
- public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
+ public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document._currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
public getActiveDocuments = () => {
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
@@ -212,9 +207,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
- if (this.Document.currentFrame !== undefined) {
+ if (this.Document._currentFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, vals.opacity);
+ CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.h, vals.w, vals.scroll, vals.opacity);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
d.y = y + NumCast(d.y) - dropPos[1];
@@ -222,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
layoutDoc._width = NumCast(layoutDoc._width, 300);
layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
- d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+ d._isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -261,7 +256,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
- // if (this.props.Document.isBackground) return false;
+ // if (this.props.Document._isBackground) return false;
const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
if (this.isAnnotationOverlay !== true && de.complete.linkDragData) {
return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
@@ -306,8 +301,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@undoBatch
@action
- updateClusters(useClusters: boolean) {
- this.props.Document.useClusters = useClusters;
+ updateClusters(_useClusters: boolean) {
+ this.props.Document._useClusters = _useClusters;
this._clusterSets.length = 0;
this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
}
@@ -315,7 +310,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
updateClusterDocs(docs: Doc[]) {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document.useClusters) {
+ if (this.props.Document._useClusters) {
const docFirst = docs[0];
docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
const preferredInd = NumCast(docFirst.cluster);
@@ -325,7 +320,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
docFirst.cluster = i;
}
})));
- if (docFirst.cluster === -1 && preferredInd !== -1 && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+ if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
docFirst.cluster = preferredInd;
}
this._clusterSets.map((set, i) => {
@@ -338,7 +333,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
doc.cluster = this._clusterSets.length;
this._clusterSets.push([doc]);
});
- } else {
+ } else if (this._clusterSets.length) {
for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
}
@@ -350,7 +345,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
updateCluster(doc: Doc) {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document.useClusters) {
+ if (this.props.Document._useClusters) {
this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
const preferredInd = NumCast(doc.cluster);
doc.cluster = -1;
@@ -359,7 +354,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
doc.cluster = i;
}
}));
- if (doc.cluster === -1 && preferredInd !== -1 && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+ if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
doc.cluster = preferredInd;
}
this._clusterSets.map((set, i) => {
@@ -370,7 +365,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (doc.cluster === -1) {
doc.cluster = this._clusterSets.length;
this._clusterSets.push([doc]);
- } else {
+ } else if (this._clusterSets.length) {
for (let i = this._clusterSets.length; i <= doc.cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
this._clusterSets[doc.cluster].push(doc);
}
@@ -380,7 +375,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getClusterColor = (doc: Doc) => {
let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1);
const cluster = NumCast(doc.cluster);
- if (this.Document.useClusters) {
+ if (this.Document._useClusters) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => this.updateCluster(doc), 0);
} else {
@@ -389,8 +384,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
clusterColor = colors[cluster % colors.length];
const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
// override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
- set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
- set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+ set && set.filter(s => !s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
+ set && set.filter(s => s._isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
}
}
return clusterColor;
@@ -402,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
return;
}
- this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
+ this._hitCluster = this.props.Document._useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false;
if (e.button === 0 && (!e.shiftKey || this._hitCluster) && !e.altKey && !e.ctrlKey && this.props.active(true)) {
// if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) {
@@ -591,7 +586,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onClick = (e: React.MouseEvent) => {
if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
if (Date.now() - this._lastTap < 300) {
- runInAction(() => DocumentLinksButton.StartLink = undefined);
+ runInAction(() => {
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ });
const docpt = this.getTransform().transformPoint(e.clientX, e.clientY);
this.scaleAtPt(docpt, 1);
e.stopPropagation();
@@ -605,7 +603,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
// bcz: theres should be a better way of doing these than referencing these static instances directly
MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2
- // PDFMenu.Instance.fadeOut(true); (commented out for mobile)
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true);
@@ -641,7 +638,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
- // panning a workspace
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
const pt = myTouches[0];
@@ -843,14 +839,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
bringToFront = action((doc: Doc, sendToBack?: boolean) => {
- if (sendToBack || doc.isBackground) {
+ if (sendToBack || doc._isBackground) {
doc.zIndex = 0;
} else if (doc.isInkMask) {
doc.zIndex = 5000;
} else {
const docs = this.childLayoutPairs.map(pair => pair.layout);
docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- let zlast = docs.length ? NumCast(docs[docs.length - 1].zIndex) : 1;
+ let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1;
if (zlast - docs.length > 100) {
for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
zlast = docs.length + 1;
@@ -908,10 +904,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
- // if (!willZoom && DocumentView._focusHack.length) {
- // Doc.BrushDoc(this.props.Document);
- // !doc.z && NumCast(this.layoutDoc.scale) < 1 && this.scaleAtPt(DocumentView._focusHack, 1); // [NumCast(doc.x), NumCast(doc.y)], 1);
- // } else {
if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
// glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
if (!doc.z) this.setPan(newPanX, newPanY, doc.presTransition || doc.presTransition === 0 ? `transform ${doc.presTransition}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
@@ -920,7 +912,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.props.focus(this.props.Document);
willZoom && this.setScaleToZoom(layoutdoc, scale);
Doc.linkFollowHighlight(doc);
- //}
afterFocus && setTimeout(() => {
if (afterFocus?.()) {
@@ -939,10 +930,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
- @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
+ @computed get backgroundActive() { return this.layoutDoc._isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); }
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
- backgroundHalo = () => BoolCast(this.Document.useClusters);
+ backgroundHalo = () => BoolCast(this.Document._useClusters);
parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.backgroundActive ? true : false;
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
@@ -975,6 +966,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.Document,
docFilters: this.docFilters,
+ searchFilterDocs: this.searchFilterDocs,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
backgroundHalo: this.backgroundHalo,
@@ -1008,8 +1000,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
});
getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc, docs: Doc[], state: any }): PoolData {
const layoutDoc = Doc.Layout(params.pair.layout);
- const { x, y, opacity } = this.Document.currentFrame === undefined ? params.pair.layout :
- CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document.currentFrame || 0);
+ const { x, y, opacity } = this.Document._currentFrame === undefined ? params.pair.layout :
+ CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame || 0);
const { z, color, zIndex } = params.pair.layout;
return {
x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
@@ -1045,7 +1037,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
} else if (viewDef.type === "div") {
return [x, y].some(val => val === undefined) ? undefined :
{
- ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z} onClick={e => this.onViewDefDivClick(e, viewDef)}
+ ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)}
style={{ width, height, backgroundColor: color, transform }} />,
bounds: viewDef
};
@@ -1140,7 +1132,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
}
- this.Document.useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
+ this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
return elements;
}
@@ -1175,7 +1167,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
(e as any).handlePan = true;
- if (this._marqueeRef?.current) {
+ if (!this.props.Document._noAutoscroll && this._marqueeRef?.current) {
const dragX = e.detail.clientX;
const dragY = e.detail.clientY;
const bounds = this._marqueeRef.current?.getBoundingClientRect();
@@ -1252,15 +1244,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
!Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
- !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }) : null;
+ !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
!viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
const options = ContextMenu.Instance.findByDescription("Options...");
const optionItems = options && "subitems" in options ? options.subitems : [];
!this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
- optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: faEye });
+ optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: "eye" });
this.props.ContainingCollectionView &&
- optionItems.push({ description: "Undo Collection", event: this.promoteCollection, icon: "table" });
+ optionItems.push({ description: "Move Items Out of Collection", event: this.promoteCollection, icon: "table" });
optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
if (!Doc.UserDoc().noviceMode) {
@@ -1315,7 +1307,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
@action
- setupDragLines = () => {
+ setupDragLines = (snapToDraggedDoc: boolean = false) => {
const activeDocs = this.getActiveDocuments();
if (activeDocs.length > 50) {
DragManager.SetSnapLines([], []);
@@ -1331,13 +1323,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
};
const snappableDocs: Doc[] = []; // the set of documents in the visible viewport that we will try to snap to;
const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
- this.getActiveDocuments().filter(doc => !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
+ this.getActiveDocuments().filter(doc => !doc._isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
!snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => isDocInView(doc, selRect)); // if not, see if there are background docs to snap to
!snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => isDocInView(doc, otherBounds)); // if not, then why not snap to floating docs
const horizLines: number[] = [];
const vertLines: number[] = [];
- snappableDocs.filter(doc => !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
+ snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top);
const docSize = this.getTransform().inverse().transformDirection(width, height);
@@ -1349,7 +1341,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
onPointerOver = (e: React.PointerEvent) => {
if (SnappingManager.GetIsDragging()) {
- this.setupDragLines();
+ this.setupDragLines(e.ctrlKey || e.shiftKey);
}
e.stopPropagation();
}
@@ -1406,12 +1398,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
centeringShiftY={this.centeringShiftY}
presPaths={BoolCast(this.Document.presPathView)}
progressivize={BoolCast(this.Document.editProgressivize)}
- zoomProgressivize={BoolCast(this.Document.editZoomProgressivize)}
+ presPinView={BoolCast(this.Document.presPinView)}
transition={Cast(this.layoutDoc._viewTransition, "string", null)}
viewDefDivClick={this.props.viewDefDivClick}
zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}>
{this.children}
- </CollectionFreeFormViewPannableContents></div>
+ </CollectionFreeFormViewPannableContents>
+ </div>
{this.showTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
</MarqueeView>;
}
@@ -1424,7 +1417,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const wscale = nw ? this.props.PanelWidth() / nw : 1;
return wscale < hscale ? wscale : hscale;
}
- @computed get backgroundEvents() { return this.layoutDoc.isBackground && SnappingManager.GetIsDragging(); }
+ @computed get backgroundEvents() { return this.layoutDoc._isBackground && SnappingManager.GetIsDragging(); }
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1492,7 +1485,7 @@ interface CollectionFreeFormViewPannableContentsProps {
transition?: string;
presPaths?: boolean;
progressivize?: boolean;
- zoomProgressivize?: boolean;
+ presPinView?: boolean;
}
@observer
@@ -1559,48 +1552,27 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
doc.style.top = toNumber(top, e.movementY) + 'px';
doc.style.left = toNumber(left, e.movementX) + 'px';
}
- this.updateAll(height, width, top, left);
+ // this.updateAll(height, width, top, left);
return false;
}
return true;
}
- @action
- updateAll = (width: number, height: number, top: number, left: number) => {
- const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- this.updateList(targetDoc, activeItem["viewfinder-width-indexed"], width);
- this.updateList(targetDoc, activeItem["viewfinder-height-indexed"], height);
- this.updateList(targetDoc, activeItem["viewfinder-top-indexed"], top);
- this.updateList(targetDoc, activeItem["viewfinder-left-indexed"], left);
- }
-
- @action
- updateList = (doc: Doc, list: any, val: number) => {
- const x: List<number> = list;
- if (x && x.length >= NumCast(doc.currentFrame) + 1) {
- x[NumCast(doc.currentFrame)] = val;
- list = x;
- } else if (doc && x) {
- x.length = NumCast(doc.currentFrame) + 1;
- x[NumCast(doc.currentFrame)] = val;
- list = x;
- }
- }
-
// scale: NumCast(targetDoc._viewScale),
@computed get zoomProgressivizeContainer() {
- const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- if (activeItem && activeItem.zoomProgressivize) {
- const vfLeft: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
- const vfWidth: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-width-indexed"]);
- const vfTop: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-top-indexed"]);
- const vfHeight: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-height-indexed"]);
+ const activeItem = PresBox.Instance.activeItem;
+ // const targetDoc = PresBox.Instance.targetDoc;
+ if (activeItem && activeItem.presPinView && activeItem.id) {
+ const vfLeft: number = NumCast(activeItem.presPinViewX);
+ const vfTop: number = NumCast(activeItem.presPinViewY);
+ const vfWidth: number = 100;
+ const vfHeight: number = 100;
+ console.log(vfTop + " | " + vfLeft);
+ console.log(this.props.presPinView);
return (
<>
- {!activeItem.editZoomProgressivize ? (null) : <div id="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width: vfWidth, height: vfHeight, top: vfTop, left: vfLeft, position: 'absolute' }}>
- <div className='resizers'>
+ {!this.props.presPinView ? (null) : <div id="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width: vfWidth, height: vfHeight, top: vfTop, left: vfLeft, position: 'absolute' }}>
+ <div className='resizers' key={'resizer' + activeItem.id}>
<div id="resizer-tl" className='resizer top-left' onPointerDown={this.onPointerDown}></div>
<div id="resizer-tr" className='resizer top-right' onPointerDown={this.onPointerDown}></div>
<div id="resizer-bl" className='resizer bottom-left' onPointerDown={this.onPointerDown}></div>
@@ -1613,7 +1585,7 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
}
@computed get zoomProgressivize() {
- return PresBox.Instance && this.props.zoomProgressivize ? this.zoomProgressivizeContainer : (null);
+ return PresBox.Instance && PresBox.Instance.activeItem && PresBox.Instance.activeItem.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
}
@computed get progressivize() {
@@ -1622,30 +1594,30 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
@computed get presPaths() {
const presPaths = "presPaths" + (this.props.presPaths ? "" : "-hidden");
- return !(PresBox.Instance) ? (null) : (<>
- {!this.props.presPaths ? (null) : <><div>{PresBox.Instance.order}</div>
- <svg className={presPaths}>
- <defs>
- <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" />
- </marker>
- <marker id="square" markerWidth="3" markerHeight="3" overflow="visible"
- refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
- <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" />
- </marker>
- <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4"
- orient="auto" overflow="visible">
- <rect x="1" y="1" width="5" height="5" fill="#69a6db" />
- </marker>
-
- <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7"
- orient="auto" overflow="visible">
- <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" />
- </marker>
- </defs>;
- {PresBox.Instance.paths}
- </svg></>}
- </>);
+ return !PresBox.Instance || !this.props.presPaths ? (null) : <>
+ <div key="presorder">{PresBox.Instance.order}</div>
+ <svg key="svg" className={presPaths}>
+ <defs>
+ <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
+ <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" />
+ </marker>
+ <marker id="square" markerWidth="3" markerHeight="3" overflow="visible"
+ refX="5" refY="5" orient="auto" markerUnits="strokeWidth">
+ <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" />
+ </marker>
+ <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4"
+ orient="auto" overflow="visible">
+ <rect x="1" y="1" width="5" height="5" fill="#69a6db" />
+ </marker>
+
+ <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7"
+ orient="auto" overflow="visible">
+ <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" />
+ </marker>
+ </defs>
+ {PresBox.Instance.paths}
+ </svg>
+ </>;
}
render() {
@@ -1659,7 +1631,8 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
return <div className={freeformclass}
style={{
transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
- transition: this.props.transition
+ transition: this.props.transition,
+ //willChange: "transform"
}}>
{this.props.children()}
{this.presPaths}
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
deleted file mode 100644
index d49ab27fb..000000000
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss
+++ /dev/null
@@ -1,68 +0,0 @@
-.antimodeMenu-button {
- width: 200px;
- position: relative;
- text-align: left;
-
- .color-previewI {
- width: 100%;
- height: 40%;
- }
-
- .color-previewII {
- width: 100%;
- height: 100%;
- }
-}
-
-.antimenu-Buttonup {
- position: absolute;
- width: 20;
- height: 10;
- right: 0;
- padding: 0;
-}
-
-.formatShapePane-inputBtn {
- width: inherit;
- position: absolute;
-}
-
-.btn-group-palette {
- .sketch-picker {
- background: #323232;
- width: 160px !important;
- height: 80% !important;
-
- .flexbox-fit {
- background: #323232;
- }
- }
-}
-
-.btn-group {
- display: grid;
- grid-template-columns: auto auto auto auto;
- /* Make the buttons appear below each other */
-}
-
-.btn-group-palette {
- display: block;
- /* Make the buttons appear below each other */
-}
-
-.btn-draw {
- display: inline;
- /* Make the buttons appear below each other */
-}
-
-.btn2-group {
- display: block;
- background: #323232;
- grid-template-columns: auto;
-
- /* Make the buttons appear below each other */
- .antimodeMenu-button {
- background: #323232;
- display: block;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
deleted file mode 100644
index 1ffa2fbed..000000000
--- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx
+++ /dev/null
@@ -1,558 +0,0 @@
-import React = require("react");
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field, Opt } from "../../../../fields/Doc";
-import { Document } from "../../../../fields/documentSchemas";
-import { InkField } from "../../../../fields/InkField";
-import { BoolCast, Cast, NumCast } from "../../../../fields/Types";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu from "../../AntimodeMenu";
-import "./FormatShapePane.scss";
-import { undoBatch } from "../../../util/UndoManager";
-import { ColorState, SketchPicker } from 'react-color';
-
-@observer
-export default class FormatShapePane extends AntimodeMenu {
- static Instance: FormatShapePane;
-
- private _lastFill = "#D0021B";
- private _lastLine = "#D0021B";
- private _lastDash = "2";
- private _mode = ["fill-drip", "ruler-combined"];
-
- @observable private _subOpen = [false, false];
- @observable private _currMode = "fill-drip";
- @observable _lock = false;
- @observable private _fillBtn = false;
- @observable private _lineBtn = false;
- @observable _controlBtn = false;
- @observable private _controlPoints: { X: number, Y: number }[] = [];
- @observable _currPoint = -1;
-
- getField(key: string) {
- return this.selectedInk?.reduce((p, i) =>
- (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt<string>);
- }
-
- @computed get selectedInk() {
- const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK);
- return inks.length ? inks : undefined;
- }
- @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; }
- @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; }
- @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; }
- @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; }
- @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; }
- @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
- @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
- @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
- @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
- @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
- @computed get shapeHgt() { return this.getField("_height"); }
- @computed get shapeWid() { return this.getField("_width"); }
- @computed get shapeXps() { return this.getField("x"); }
- @computed get shapeYps() { return this.getField("y"); }
- @computed get shapeRot() { return this.getField("rotation"); }
- set unFilled(value) { this.colorFil = value ? "" : this._lastFill; }
- set solidFil(value) { this.unFilled = !value; }
- set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); }
- set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); }
- set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); }
- set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); }
- set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
- set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
- set dashdStk(value) {
- value && (this._lastDash = value) && (this.unStrokd = false);
- this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined);
- }
- set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); }
- set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); }
- set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); }
- set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); }
- set shapeWid(value) {
- this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- const oldWidth = NumCast(i.rootDoc._width);
- i.rootDoc._width = Number(value);
- this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth);
- });
- }
- set shapeHgt(value) {
- this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- const oldHeight = NumCast(i.rootDoc._height);
- i.rootDoc._height = Number(value);
- this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight);
- });
- }
-
- constructor(props: Readonly<{}>) {
- super(props);
- FormatShapePane.Instance = this;
- this._canFade = false;
- this.Pinned = BoolCast(Doc.UserDoc()["menuFormatShape-pinned"]);
- }
-
- @action
- closePane = () => {
- this.fadeOut(false);
- this.Pinned = false;
- }
-
- @action
- upDownButtons = (dirs: string, field: string) => {
- switch (field) {
- case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
- // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break;
- case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break;
- case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break;
- case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
- case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- //redraw points
- const oldWidth = NumCast(i.rootDoc._width);
- const oldHeight = NumCast(i.rootDoc._height);
- const oldX = NumCast(i.rootDoc.x);
- const oldY = NumCast(i.rootDoc.y);
- i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10);
- this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height)));
- const doc = Document(i.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
- console.log(doc.x, doc.y, doc._height, doc._width);
- const ink = Cast(doc.data, InkField)?.inkData;
- console.log(ink);
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- ink.forEach(i => {
- // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth;
- const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight;
- newPoints.push({ X: newX, Y: newY });
- });
- doc.data = new InkField(newPoints);
- }
- }
- });
- break;
- case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => {
- const oldWidth = NumCast(i.rootDoc._width);
- const oldHeight = NumCast(i.rootDoc._height);
- const oldX = NumCast(i.rootDoc.x);
- const oldY = NumCast(i.rootDoc.y); i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10);
- this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width)));
- const doc = Document(i.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
- console.log(doc.x, doc.y, doc._height, doc._width);
- const ink = Cast(doc.data, InkField)?.inkData;
- console.log(ink);
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- ink.forEach(i => {
- // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
- const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth;
- const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight;
- newPoints.push({ X: newX, Y: newY });
- });
- doc.data = new InkField(newPoints);
- }
- }
- });
- break;
- }
- }
-
- @undoBatch
- @action
- addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => {
- this.selectedInk?.forEach(action(inkView => {
- if (this.selectedInk?.length === 1) {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const newPoints: { X: number, Y: number }[] = [];
- var counter = 0;
- for (var k = 0; k < index; k++) {
- control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++);
- }
- //decide where to put the new coordinate
- const spNum = Math.floor(counter / 2) * 4 + 2;
-
- for (var i = 0; i < spNum; i++) {
- newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- for (var j = 0; j < 4; j++) {
- newPoints.push({ X: x, Y: y });
-
- }
- for (var i = spNum; i < ink.length; i++) {
- newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- this._currPoint = -1;
- doc.data = new InkField(newPoints);
- }
- }
- }
- }));
- }
-
- @undoBatch
- @action
- deletePoints = () => {
- this.selectedInk?.forEach(action(inkView => {
- if (this.selectedInk?.length === 1 && this._currPoint !== -1) {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink && ink.length > 4) {
- const newPoints: { X: number, Y: number }[] = [];
-
- console.log(ink.length, this._currPoint, Math.floor((this._currPoint + 2) / 4));
-
- const toRemove = Math.floor(((this._currPoint + 2) / 4));
- for (var i = 0; i < ink.length; i++) {
- if (Math.floor((i + 2) / 4) !== toRemove) {
- console.log(i, toRemove);
- newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- }
- this._currPoint = -1;
- doc.data = new InkField(newPoints);
- if (newPoints.length === 4) {
- const newerPoints: { X: number, Y: number }[] = [];
- newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y });
- newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y });
- newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y });
- newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y });
- doc.data = new InkField(newerPoints);
-
- }
- }
- }
- }
- }));
- }
-
- @undoBatch
- @action
- rotate = (angle: number) => {
- const _centerPoints: { X: number, Y: number }[] = [];
- SelectionManager.SelectedDocuments().forEach(action(inkView => {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
- const xs = ink.map(p => p.X);
- const ys = ink.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
- _centerPoints.push({ X: left, Y: top });
- }
- }
- }));
-
- var index = 0;
- SelectionManager.SelectedDocuments().forEach(action(inkView => {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- doc.rotation = Number(doc.rotation) + Number(angle);
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
-
- const newPoints: { X: number, Y: number }[] = [];
- ink.forEach(i => {
- const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X;
- const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
- newPoints.push({ X: newX, Y: newY });
- });
- doc.data = new InkField(newPoints);
- const xs = newPoints.map(p => p.X);
- const ys = newPoints.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- const right = Math.max(...xs);
- const bottom = Math.max(...ys);
-
- doc._height = (bottom - top);
- doc._width = (right - left);
- }
- index++;
- }
- }));
- }
-
- @undoBatch
- @action
- control = (xDiff: number, yDiff: number, controlNum: number) => {
- this.selectedInk?.forEach(action(inkView => {
- if (this.selectedInk?.length === 1) {
- const doc = Document(inkView.rootDoc);
- if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
- const ink = Cast(doc.data, InkField)?.inkData;
- if (ink) {
-
- const newPoints: { X: number, Y: number }[] = [];
- const order = controlNum % 4;
- for (var i = 0; i < ink.length; i++) {
- if (controlNum === i ||
- (order === 0 && i === controlNum + 1) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 2) ||
- (order === 0 && controlNum !== 0 && i === controlNum - 1) ||
- (order === 3 && i === controlNum - 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) ||
- (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2)
- || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1))
- ) {
- newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) });
- }
- else {
- newPoints.push({ X: ink[i].X, Y: ink[i].Y });
- }
- }
- const oldx = doc.x;
- const oldy = doc.y;
- const xs = ink.map(p => p.X);
- const ys = ink.map(p => p.Y);
- const left = Math.min(...xs);
- const top = Math.min(...ys);
- doc.data = new InkField(newPoints);
- const xs2 = newPoints.map(p => p.X);
- const ys2 = newPoints.map(p => p.Y);
- const left2 = Math.min(...xs2);
- const top2 = Math.min(...ys2);
- const right2 = Math.max(...xs2);
- const bottom2 = Math.max(...ys2);
- doc._height = (bottom2 - top2);
- doc._width = (right2 - left2);
- //if points move out of bounds
-
- doc.x = oldx - (left - left2);
- doc.y = oldy - (top - top2);
-
- }
- }
- }
- }));
- }
-
- @undoBatch
- @action
- switchStk = (color: ColorState) => {
- const val = String(color.hex);
- this.colorStk = val;
- return true;
- }
-
- @undoBatch
- @action
- switchFil = (color: ColorState) => {
- const val = String(color.hex);
- this.colorFil = val;
- return true;
- }
-
-
- colorPicker(setter: (color: string) => {}, type: string) {
- return <div className="btn-group-palette" key="colorpicker" style={{ width: 160, margin: 10 }}>
- <SketchPicker onChange={type === "stk" ? this.switchStk : this.switchFil} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
- color={type === "stk" ? this.colorStk : this.colorFil} />
- </div>;
- }
- inputBox = (key: string, value: any, setter: (val: string) => {}) => {
- return <>
- <input style={{ color: "black", width: 40, position: "absolute", right: 20 }}
- type="text" value={value}
- onChange={undoBatch(action((e) => setter(e.target.value)))}
- autoFocus />
- <button className="antiMenu-Buttonup" key="up1" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))}>
- Ë„
- </button>
- <br />
- <button className="antiMenu-Buttonup" key="down1" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: -8 }}>
- Ë…
- </button>
- </>;
- }
-
- inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => {
- return <>
- {title1}
- <p style={{ marginTop: -20, right: 70, position: "absolute" }}>{title2}</p>
-
- <input style={{ color: "black", width: 40, position: "absolute", right: 130 }}
- type="text" value={value}
- onChange={e => setter(e.target.value)}
- autoFocus />
- <button className="antiMenu-Buttonup" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} style={{ right: 110 }}>
- Ë„
- </button>
- <button className="antiMenu-Buttonup" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} style={{ marginTop: 12, right: 110 }}>
- Ë…
- </button>
- {title2 === "" ? "" : <>
- <input style={{ color: "black", width: 40, position: "absolute", right: 20 }}
- type="text" value={value2}
- onChange={e => setter2(e.target.value)}
- autoFocus />
- <button className="antiMenu-Buttonup" key="up3" onPointerDown={undoBatch(action(() => this.upDownButtons("up", key2)))}>
- Ë„
- </button>
- <br />
- <button className="antiMenu-Buttonup" key="down3" onPointerDown={undoBatch(action(() => this.upDownButtons("down", key2)))} style={{ marginTop: -8 }}>
- Ë…
- </button></>}
- </>;
- }
-
-
- colorButton(value: string, setter: () => {}) {
- return <>
- <button className="antimodeMenu-button" key="color" onPointerDown={undoBatch(action(e => setter()))} style={{ position: "relative", marginTop: -5 }}>
- <div className="color-previewII" style={{ backgroundColor: value ?? "121212" }} />
- {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -23, position: "fixed" }}>☒</p> : ""}
- </button>
- </>;
- }
-
- controlPointsButton() {
- return <>
- <button className="antimodeMenu-button" title="Edit points" key="bezier" onPointerDown={action(() => this._controlBtn = this._controlBtn ? false : true)} style={{ position: "relative", marginTop: 10, backgroundColor: this._controlBtn ? "black" : "" }}>
- <FontAwesomeIcon icon="bezier-curve" size="lg" />
- </button>
- <button className="antimodeMenu-button" title="Lock ratio" key="ratio" onPointerDown={action(() => this._lock = this._lock ? false : true)} style={{ position: "relative", marginTop: 10, backgroundColor: this._lock ? "black" : "" }}>
- <FontAwesomeIcon icon="lock" size="lg" />
-
- </button>
- <button className="antimodeMenu-button" key="rotate" title="Rotate 90Ëš" onPointerDown={action(() => this.rotate(Math.PI / 2))} style={{ position: "relative", marginTop: 10, fontSize: 15 }}>
- ⟲
- </button>
- <br /> <br />
- </>;
- }
-
- lockRatioButton() {
- return <>
- <button className="antimodeMenu-button" key="lock" onPointerDown={action(() => this._lock = this._lock ? false : true)} style={{ position: "absolute", right: 80, backgroundColor: this._lock ? "black" : "" }}>
- {/* <FontAwesomeIcon icon="bezier-curve" size="lg" /> */}
- <FontAwesomeIcon icon="lock" size="lg" />
-
- </button>
- <br /> <br />
- </>;
- }
-
- rotate90Button() {
- return <>
- <button className="antimodeMenu-button" key="rot" onPointerDown={action(() => this.rotate(Math.PI / 2))} style={{ position: "absolute", right: 80, }}>
- {/* <FontAwesomeIcon icon="bezier-curve" size="lg" /> */}
- ⟲
-
- </button>
- <br /> <br />
- </>;
- }
- @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); }
- @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); }
-
- @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); }
- @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); }
-
- @computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); }
- @computed get dashInput() { return this.inputBox("dsh", this.widthStk, (val: string) => this.widthStk = val); }
-
- @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); }
- @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); }
- @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); }
-
- @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); }
-
- @computed get controlPoints() { return this.controlPointsButton(); }
- @computed get lockRatio() { return this.lockRatioButton(); }
- @computed get rotate90() { return this.rotate90Button(); }
- @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); }
-
-
- @computed get propertyGroupItems() {
- const fillCheck = <div key="fill" style={{ display: (this._subOpen[0] && this.selectedInk && this.selectedInk.length >= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- Fill:
- {this.fillButton}
- <div style={{ float: "left", width: 100 }} >
- Stroke:
- {this.lineButton}
- </div>
-
- {this._fillBtn ? this.fillPicker : ""}
- {this._lineBtn ? this.linePicker : ""}
- {this._fillBtn || this._lineBtn ? "" : <br />}
- {(this.solidStk || this.dashdStk) ? "Width" : ""}
- {(this.solidStk || this.dashdStk) ? this.stkInput : ""}
-
-
- {(this.solidStk || this.dashdStk) ? <input type="range" defaultValue={Number(this.widthStk)} min={1} max={100} onChange={undoBatch(action((e) => this.widthStk = e.target.value))} /> : (null)}
- <br />
- {(this.solidStk || this.dashdStk) ? <>
- <p style={{ position: "absolute", fontSize: 12 }}>Arrow Head</p>
- <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.markHead !== ""} onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} style={{ position: "absolute", right: 110, width: 20 }} />
- <p style={{ position: "absolute", fontSize: 12, right: 30 }}>Arrow End</p>
- <input key="markTail" className="formatShapePane-inputBtn" type="checkbox" checked={this.markTail !== ""} onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} style={{ position: "absolute", right: 0, width: 20 }} />
- <br />
- </> : ""}
- Dash: <input key="markHead" className="formatShapePane-inputBtn" type="checkbox" checked={this.dashdStk === "2"} onChange={undoBatch(action(() => this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} style={{ position: "absolute", right: 110, width: 20 }} />
-
-
-
- </div>;
-
-
-
- const sizeCheck =
-
- <div key="sizeCheck" style={{ display: (this._subOpen[1] && this.selectedInk && this.selectedInk.length >= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}>
- {this.controlPoints}
- {this.hgtInput}
- {this.XpsInput}
- {this.rotInput}
-
- </div>;
-
-
- const subMenus = this._currMode === "fill-drip" ? [`Appearance`, 'Transform'] : [];
- const menuItems = this._currMode === "fill-drip" ? [fillCheck, sizeCheck] : [];
- const indexOffset = 0;
-
- return <div className="antimodeMenu-sub" key="submenu" style={{ position: "absolute", width: "inherit", top: 60 }}>
- {subMenus.map((subMenu, i) =>
- <div key={subMenu} style={{ width: "inherit" }}>
- <button className="antimodeMenu-button" onPointerDown={action(() => this._subOpen[i + indexOffset] = !this._subOpen[i + indexOffset])}
- style={{ backgroundColor: "121212", position: "relative", width: "inherit" }}>
- {this._subOpen[i + indexOffset] ? "▼" : "▶︎"}
- {subMenu}
- </button>
- {menuItems[i]}
- </div>)}
- </div>;
- }
-
- @computed get closeBtn() {
- return <button className="antimodeMenu-button" key="close" onPointerDown={action(() => this.closePane())} style={{ position: "absolute", right: 0 }}>
- X
- </button>;
- }
-
- @computed get propertyGroupBtn() {
- return <div className="antimodeMenu-button-tab" key="modes">
- {this._mode.map(mode =>
- <button className="antimodeMenu-button" key={mode} onPointerDown={action(() => this._currMode = mode)}
- style={{ backgroundColor: this._currMode === mode ? "121212" : "", position: "relative", top: 30 }}>
- <FontAwesomeIcon icon={mode as IconProp} size="lg" />
- </button>)}
- </div>;
- }
-
- render() {
- return this.getElementVert([this.closeBtn,
- this.propertyGroupItems]);
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index f1df7998b..46298ec6f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,13 +1,12 @@
import React = require("react");
-import AntimodeMenu from "../../AntimodeMenu";
-import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { unimplementedFunction } from "../../../../Utils";
-import { undoBatch } from "../../../util/UndoManager";
import { Tooltip } from "@material-ui/core";
+import { observer } from "mobx-react";
+import { unimplementedFunction } from "../../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
@observer
-export default class MarqueeOptionsMenu extends AntimodeMenu {
+export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: MarqueeOptionsMenu;
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
@@ -16,6 +15,7 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public showMarquee: () => void = unimplementedFunction;
public hideMarquee: () => void = unimplementedFunction;
+ public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
constructor(props: Readonly<{}>) {
super(props);
@@ -53,6 +53,14 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
<FontAwesomeIcon icon="font" size="lg" />
</button>
</Tooltip>,
+ <Tooltip key="pinWithView" title={<><div className="dash-tooltip">Pin to presentation with selected view</div></>} placement="bottom">
+ <button
+ className="antimodeMenu-button"
+ onPointerDown={this.pinWithView}>
+ <FontAwesomeIcon icon="map-pin" size="lg" />
+ <div style={{ position: 'relative', fontSize: 25, fontWeight: 700, transform: 'translate(-4px, -22px)', color: 'rgba(250,250,250,0.55)' }}>V</div>
+ </button>
+ </Tooltip>,
];
return this.getElement(buttons);
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c0b19fcd2..d8e1bcc9c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,27 +1,28 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt, DocListCast, DataSym, AclEdit, AclAddonly, AclAdmin } from "../../../../fields/Doc";
-import { GetEffectiveAcl } from "../../../../fields/util";
+import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc";
import { InkData, InkField, InkTool } from "../../../../fields/InkField";
import { List } from "../../../../fields/List";
import { RichTextField } from "../../../../fields/RichTextField";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { GetEffectiveAcl } from "../../../../fields/util";
import { Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
+import { DocumentManager } from "../../../util/DocumentManager";
import { SelectionManager } from "../../../util/SelectionManager";
import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
import { ContextMenu } from "../../ContextMenu";
import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
import { PreviewCursor } from "../../PreviewCursor";
+import { CollectionDockingView } from "../CollectionDockingView";
import { SubCollectionViewProps } from "../CollectionSubView";
-import { CollectionView } from "../CollectionView";
-import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
+import { CollectionView, CollectionViewType } from "../CollectionView";
+import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
import "./MarqueeView.scss";
import React = require("react");
-import { ContextMenuItem } from "../../ContextMenuItem";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -75,7 +76,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY);
if (e.key === "?") {
cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
- Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight"));
+ Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", useCors: true }), "add:right"));
cm.displayMenu(this._downX, this._downY);
e.stopPropagation();
@@ -130,6 +131,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const tbox = Docs.Create.TextDocument("", {
_width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined,
title: "-typed text-"
});
const template = FormattedTextBox.DefaultLayout;
@@ -138,6 +140,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
tbox.layoutKey = "layout_" + StrCast(template.title);
Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
}
+ FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
this.props.addLiveTextDocument(tbox);
e.stopPropagation();
}
@@ -241,6 +244,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
MarqueeOptionsMenu.Instance.fadeOut(true);
document.removeEventListener("pointerdown", hideMarquee);
+ document.removeEventListener("wheel", hideMarquee);
};
if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
@@ -250,7 +254,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
+ MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
document.addEventListener("pointerdown", hideMarquee);
+ document.addEventListener("wheel", hideMarquee);
} else {
this.hideMarquee();
}
@@ -349,7 +355,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
}
- getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => {
+ getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, _isBackground?: boolean) => {
const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
Doc.GetProto(doc).title = "nested freeform";
@@ -357,8 +363,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.system = undefined;
- newCollection.isBackground = isBackground;
- newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined;
+ newCollection._isBackground = _isBackground;
+ newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : _isBackground ? "cyan" : undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
newCollection.x = this.Bounds.left;
@@ -381,6 +387,38 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
@undoBatch @action
+ pinWithView = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ const doc = this.props.Document;
+ const bounds = this.Bounds;
+ const selected = this.marqueeSelect(false);
+ const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+ if (curPres) {
+ const pinDoc = Doc.MakeAlias(doc);
+ pinDoc.presentationTargetDoc = doc;
+ pinDoc.presZoomButton = true;
+ pinDoc.context = curPres;
+ Doc.AddDocToList(curPres, "data", pinDoc);
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+ if (!DocumentManager.Instance.getDocumentView(curPres)) {
+ CollectionDockingView.AddSplit(curPres, "right");
+ }
+ if (e instanceof KeyboardEvent ? e.key === "c" : true) {
+ const x = this.Bounds.left + this.Bounds.width / 2;
+ const y = this.Bounds.top + this.Bounds.height / 2;
+ const panelWidth: number = this.props.PanelWidth();
+ const panelHeight: number = this.props.PanelHeight();
+ const scale = Math.min(Number(panelWidth) / this.Bounds.width, Number(panelHeight) / this.Bounds.height);
+ pinDoc.presPinView = true;
+ pinDoc.presPinViewX = x;
+ pinDoc.presPinViewY = y;
+ pinDoc.presPinViewScale = scale;
+ }
+ }
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ this.hideMarquee();
+ }
+
+ @undoBatch @action
collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const bounds = this.Bounds;
const selected = this.marqueeSelect(false);
@@ -492,7 +530,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.page = -1;
return d;
});
- const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+ const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
const portal = Doc.MakeAlias(summary);
Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
@@ -644,7 +682,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
marqueeSelect(selectBackgrounds: boolean = true) {
const selRect = this.Bounds;
const selection: Doc[] = [];
- this.props.activeDocuments().filter(doc => !doc.isBackground && !doc.z).map(doc => {
+ this.props.activeDocuments().filter(doc => !doc._isBackground && !doc.z).map(doc => {
const layoutDoc = Doc.Layout(doc);
const x = NumCast(doc.x);
const y = NumCast(doc.y);
@@ -719,14 +757,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
</div>;
} else {
- //subtracted 250 for offset
var str: string = "";
for (var i = 0; i < this._pointsX.length; i++) {
- var x = 0;
- x = this._pointsX[i] - 250;
- str += x.toString();
+ const pt = this.props.getContainerTransform().transformPoint(this._pointsX[i], this._pointsY[i]);
+ str += pt[0].toString();
str += ",";
- str += this._pointsY[i].toString();
+ str += pt[1].toString();
str += (" ");
}
@@ -747,7 +783,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
render() {
return <div className="marqueeView"
- style={{ overflow: StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
+ style={{ overflow: !this.props.ContainingCollectionView && this.props.annotationsKey ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }}
onDragOver={e => e.preventDefault()}
onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
{this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 4d8473be9..60ec02f47 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -134,17 +134,6 @@
}
-// .documentDecorations-container .documentDecorations-resizer {
-// pointer-events: none;
-// }
-
-// #documentDecorations-bottomRightResizer,
-// #documentDecorations-bottomLeftResizer,
-// #documentDecorations-topRightResizer,
-// #documentDecorations-topLeftResizer {
-// visibility: collapse;
-// }
-
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e6ac7021a..4e279c659 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -94,8 +94,8 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
*/
unflexedPosition(index: number): Omit<Layout, "i"> {
return {
- x: (index % Math.floor(this.numCols / this.defaultW)) * this.defaultW,
- y: Math.floor(index / Math.floor(this.numCols / this.defaultH)) * this.defaultH,
+ x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
+ y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
w: this.defaultW,
h: this.defaultH,
static: true
@@ -304,7 +304,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
(e: PointerEvent, doubleTap?: boolean) => {
if (doubleTap) {
undoBatch(action(() => {
- const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
+ const text = Docs.Create.TextDocument("", { _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 150, _height: 50 });
FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 21d283547..0afcab5a3 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -234,6 +234,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
ScreenToLocalTransform={dxf}
focus={this.props.focus}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index d02088a6c..53825eece 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -234,6 +234,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
ScreenToLocalTransform={dxf}
focus={this.props.focus}
docFilters={this.docFilters}
+ searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss
index 7eb07ff55..d0c90edcb 100644
--- a/src/client/views/globalCssVariables.scss
+++ b/src/client/views/globalCssVariables.scss
@@ -36,6 +36,12 @@ $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should
$COLLECTION_BORDER_WIDTH: 1;
$MINIMIZED_ICON_SIZE:25;
$MAX_ROW_HEIGHT: 44px;
+$DFLT_IMAGE_NATIVE_DIM: 900px;
+$MENU_PANEL_WIDTH: 60px;
+:root {
+ --flyoutHandleWidth: 28px;
+ --menuPanelWidth: 60px;
+}
:export {
contextMenuZindex: $contextMenu-zindex;
@@ -45,4 +51,6 @@ $MAX_ROW_HEIGHT: 44px;
SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
ANTIMODEMENU_HEIGHT: $antimodemenu-height;
SEARCH_PANEL_HEIGHT: $searchpanel-height;
+ DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
+ MENU_PANEL_WIDTH: $MENU_PANEL_WIDTH;
} \ 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 fd1f60f13..fb3a327d0 100644
--- a/src/client/views/globalCssVariables.scss.d.ts
+++ b/src/client/views/globalCssVariables.scss.d.ts
@@ -7,6 +7,8 @@ interface IGlobalScss {
SEARCH_THUMBNAIL_SIZE: string;
ANTIMODEMENU_HEIGHT: string;
SEARCH_PANEL_HEIGHT: string;
+ DFLT_IMAGE_NATIVE_DIM: string;
+ MENU_PANEL_WIDTH: string;
}
declare const globalCssVariables: IGlobalScss;
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index ed64bde32..d6c3702d1 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -1,5 +1,3 @@
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
import { action, computed, observable } from "mobx";
@@ -12,9 +10,6 @@ import { undoBatch } from "../../util/UndoManager";
import './LinkEditor.scss';
import React = require("react");
-library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus);
-
-
interface GroupTypesDropdownProps {
groupType: string;
setGroupType: (group: string) => void;
@@ -369,7 +364,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
<div className="linkEditor-followingDropdown-dropdown">
<div className="linkEditor-followingDropdown-header"
onPointerDown={this.changeDropdown}>
- {StrCast(this.props.linkDoc.followLinkLocation, "Default")}
+ {StrCast(this.props.linkDoc.followLinkLocation, "default")}
<FontAwesomeIcon className="linkEditor-followingDropdown-icon"
icon={this.openDropdown ? "chevron-up" : "chevron-down"}
size={"lg"} />
@@ -377,17 +372,37 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
<div className="linkEditor-followingDropdown-optionsList"
style={{ display: this.openDropdown ? "" : "none" }}>
<div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("Default")}>
+ onPointerDown={() => this.changeFollowBehavior("default")}>
Default
</div>
<div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("onRight")}>
- Always open in a new pane
+ onPointerDown={() => this.changeFollowBehavior("add:left")}>
+ Always open in new left pane
+ </div>
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("add:right")}>
+ Always open in new right pane
+ </div>
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("replace:right")}>
+ Always replace right tab
</div>
<div className="linkEditor-followingDropdown-option"
- onPointerDown={() => this.changeFollowBehavior("inTab")}>
+ onPointerDown={() => this.changeFollowBehavior("replace:left")}>
+ Always replace left tab
+ </div>
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
+ Always open full screen
+ </div>
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("add")}>
Always open in a new tab
</div>
+ <div className="linkEditor-followingDropdown-option"
+ onPointerDown={() => this.changeFollowBehavior("replace")}>
+ Replace Tab
+ </div>
{this.props.linkDoc.linksToAnnotation ?
<div className="linkEditor-followingDropdown-option"
onPointerDown={() => this.changeFollowBehavior("openExternal")}>
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 4dc25031d..0e03b46db 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -4,8 +4,6 @@
width: auto;
height: auto;
position: absolute;
- top: 0;
- left: 0;
z-index: 999;
.linkMenu-list {
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 8ecde959f..f5a1ae8e7 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,18 +1,14 @@
-import { action, observable, computed } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { DocumentView } from "../nodes/DocumentView";
-import { LinkEditor } from "./LinkEditor";
-import './LinkMenu.scss';
-import React = require("react");
import { Doc } from "../../../fields/Doc";
import { LinkManager } from "../../util/LinkManager";
-import { LinkMenuGroup } from "./LinkMenuGroup";
-import { faTrash } from '@fortawesome/free-solid-svg-icons';
-import { library } from "@fortawesome/fontawesome-svg-core";
import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
+import { DocumentView } from "../nodes/DocumentView";
import { LinkDocPreview } from "../nodes/LinkDocPreview";
-
-library.add(faTrash);
+import { LinkEditor } from "./LinkEditor";
+import './LinkMenu.scss';
+import { LinkMenuGroup } from "./LinkMenuGroup";
+import React = require("react");
interface Props {
docView: DocumentView;
@@ -93,12 +89,11 @@ export class LinkMenu extends React.Component<Props> {
}
render() {
- console.log("computed", this.position.x, this.position.b);
const sourceDoc = this.props.docView.props.Document;
const groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
- return <div className="linkMenu" ref={this._linkMenuRef} >
+ return <div className="linkMenu" style={{ left: this.position.x, top: this.props.docView.topMost ? undefined : this.position.b + 15, bottom: this.props.docView.topMost ? 20 : undefined }} ref={this._linkMenuRef} >
{!this._editingLink ?
- <div className="linkMenu-list" style={{ left: this.position.x, top: this.position.b + 15 }}>
+ <div className="linkMenu-list" >
{this.renderAllGroups(groups)}
</div> :
<div className="linkMenu-listEditor" style={{ left: this.position.x, top: this.position.b + 15 }}>
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 2ae87ac13..8e09052a3 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -66,7 +66,8 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
}
render() {
- const groupItems = this.props.group.map(linkDoc => {
+ const set = new Set<Doc>(this.props.group);
+ const groupItems = Array.from(set.keys()).map(linkDoc => {
const destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc) ||
LinkManager.Instance.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === this.props.sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null));
if (destination && this.props.sourceDoc) {
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 21c666a4d..17549e3cf 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,27 +1,23 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { action, observable, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { Cast, StrCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
+import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
-import './LinkMenuItem.scss';
-import React = require("react");
-import { DocumentManager } from '../../util/DocumentManager';
-import { setupMoveUpEvents, emptyFunction, Utils, simulateMouseClick } from '../../../Utils';
-import { DocumentView } from '../nodes/DocumentView';
import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
+import { DocumentView } from '../nodes/DocumentView';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
-import { Hypothesis } from '../../util/HypothesisUtils';
-import { Id } from '../../../fields/FieldSymbols';
-import { Tooltip } from '@material-ui/core';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { undoBatch } from '../../util/UndoManager';
-import { WebField } from '../../../fields/URLField';
-library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash);
+import './LinkMenuItem.scss';
+import React = require("react");
interface LinkMenuItemProps {
@@ -161,7 +157,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
return;
}
- if (linkDoc.followLinkLocation && linkDoc.followLinkLocation !== "Default") {
+ if (linkDoc.followLinkLocation && linkDoc.followLinkLocation !== "default") {
const annotationOn = this.props.destinationDoc.annotationOn as Doc;
this.props.addDocTab(annotationOn instanceof Doc ? annotationOn : this.props.destinationDoc, StrCast(linkDoc.followLinkLocation));
if (annotationOn) {
@@ -171,7 +167,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
});
}
} else {
- DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false);
+ DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "add:right"), false);
}
linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(this.props.linkDoc.annotationId), this.props.destinationDoc);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 77e2df94c..2edb73cee 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -109,12 +109,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._linkPlayDisposer?.();
this._scrubbingDisposer?.();
}
+
+ @action
componentDidMount() {
if (!this.dataDoc.markerAmount) {
this.dataDoc.markerAmount = 0;
}
- runInAction(() => this.audioState = this.path ? "paused" : undefined);
+ this.audioState = this.path ? "paused" : undefined;
this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
scrollLinkId => {
if (scrollLinkId) {
@@ -169,11 +171,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
DocListCast(this.dataDoc.links).map(l => {
const { la1, linkTime } = this.getLinkData(l);
- if (linkTime > NumCast(this.layoutDoc.currentTimecode) && linkTime < htmlEle.currentTime) {
+ if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) {
Doc.linkFollowHighlight(la1);
}
});
- this.layoutDoc.currentTimecode = htmlEle.currentTime;
+ this.layoutDoc._currentTimecode = htmlEle.currentTime;
}
}
@@ -223,7 +225,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
if (this._paused) {
this._pausedTime += (new Date().getTime() - this._recordStart) / 1000;
} else {
- this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
+ this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
}
}
}
@@ -285,7 +287,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
// creates a text document for dictation
onFile = (e: any) => {
const newDoc = Docs.Create.TextDocument("", {
- title: "", _chromeStatus: "disabled",
+ _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: "", _chromeStatus: "disabled",
x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
_width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
});
@@ -395,7 +397,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x));
return false;
},
- () => this._ele!.currentTime = this.layoutDoc.currentTimecode = toTimeline(e.clientX - rect.x),
+ () => this._ele!.currentTime = this.layoutDoc._currentTimecode = toTimeline(e.clientX - rect.x),
emptyFunction);
}
@@ -463,7 +465,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
color={"darkblue"}
height={this._waveHeight}
barWidth={0.1}
- // pos={this.layoutDoc.currentTimecode} need to correctly resize parent to make this work (not very necessary for function)
+ // pos={this.layoutDoc._currentTimecode} need to correctly resize parent to make this work (not very necessary for function)
pos={this.audioDuration}
duration={this.audioDuration}
peaks={this._buckets.length === 100 ? this._buckets : undefined}
@@ -544,7 +546,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
<div className="buttons" onClick={this._paused ? this.recordPlay : this.recordPause}>
<FontAwesomeIcon style={{ width: "100%" }} icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
</div>
- <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}</div>
+ <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
</div>
:
<button className={`audiobox-record${interactive}`} style={{ backgroundColor: "black" }}>
@@ -562,7 +564,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
if (e.target !== this._audioRef.current) {
const wasPaused = this.audioState === "paused";
- this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
+ this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
wasPaused && this.pause();
}
@@ -624,11 +626,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
})}
{this._visible ? this.selectionContainer : null}
- <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
+ <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
{this.audio}
</div>
<div className="current-time">
- {formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
+ {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
</div>
<div className="total-time">
{formatTime(Math.round(this.audioDuration))}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 52f6a66c8..16b2f7c2a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -58,21 +58,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
- @computed get renderScriptDim() {
- if (this.Document.renderScript) {
- const someView = Cast(this.props.Document.someView, Doc);
- const minimap = Cast(this.props.Document.minimap, Doc);
- if (someView instanceof Doc && minimap instanceof Doc) {
- const x = (NumCast(someView._panX) - NumCast(someView._width) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap._width) - NumCast(minimap._width) / 2;
- const y = (NumCast(someView._panY) - NumCast(someView._height) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap._height) - NumCast(minimap._height) / 2;
- const w = NumCast(someView._width) / NumCast(someView._viewScale) / NumCast(minimap.fitW) * NumCast(minimap.width);
- const h = NumCast(someView._height) / NumCast(someView._viewScale) / NumCast(minimap.fitH) * NumCast(minimap.height);
- return { x: x, y: y, width: w, height: h };
- }
- }
- return undefined;
- }
-
public static getValues(doc: Doc, time: number) {
const timecode = Math.round(time);
return ({
@@ -123,11 +108,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
setTimeout(() => doc.dataTransition = "inherit", 1010);
}
- public static setupScroll(doc: Doc, timecode: number, scrollProgressivize: boolean = false) {
+ public static setupScroll(doc: Doc, timecode: number) {
const scrollList = new List<number>();
scrollList[timecode] = NumCast(doc._scrollTop);
doc["scroll-indexed"] = scrollList;
- doc.activeFrame = ComputedField.MakeFunction("self.currentFrame");
+ doc.activeFrame = ComputedField.MakeFunction("self._currentFrame");
doc._scrollTop = ComputedField.MakeInterpolated("scroll", "activeFrame");
}
@@ -165,7 +150,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
- public static setupZoom(doc: Doc, targDoc: Doc, zoomProgressivize: boolean = false) {
+ public static setupZoom(doc: Doc, targDoc: Doc) {
const width = new List<number>();
const height = new List<number>();
const top = new List<number>();
@@ -180,32 +165,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
doc["viewfinder-left-indexed"] = left;
}
- public static setupKeyframes(docs: Doc[], timecode: number, progressivize: boolean = false) {
- docs.forEach((doc, i) => {
- if (doc.appearFrame === undefined) doc.appearFrame = i;
- const curTimecode = progressivize ? i : timecode;
- const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const wlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const hlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const olist = new List<number>(numberRange(timecode + 1).map(t => progressivize && t < (doc.appearFrame ? doc.appearFrame : i) ? 0 : 1));
- const oarray = olist;
- oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- oarray.fill(1, NumCast(doc.appearFrame), timecode);
- // oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- // oarray.fill(1, NumCast(doc.appearFrame), timecode);\
+ public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
+ docs.forEach(doc => {
+ if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
+ const curTimecode = currTimecode;
+ const xlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const ylist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const wlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const hlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const olist = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
wlist[curTimecode] = NumCast(doc._width);
hlist[curTimecode] = NumCast(doc._height);
xlist[curTimecode] = NumCast(doc.x);
ylist[curTimecode] = NumCast(doc.y);
- doc.xArray = xlist;
- doc.yArray = ylist;
doc["x-indexed"] = xlist;
doc["y-indexed"] = ylist;
doc["w-indexed"] = wlist;
doc["h-indexed"] = hlist;
- doc["opacity-indexed"] = oarray;
- doc.activeFrame = ComputedField.MakeFunction("self.context?.currentFrame||0");
+ doc["opacity-indexed"] = olist;
+ doc.activeFrame = ComputedField.MakeFunction("self.context?._currentFrame||0");
doc._height = ComputedField.MakeInterpolated("h", "activeFrame");
doc._width = ComputedField.MakeInterpolated("w", "activeFrame");
doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
@@ -274,8 +252,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
boxShadow:
this.Opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
+ this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc._isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.layoutDoc._isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
outline: this.Highlight ? "orange solid 2px" : "",
@@ -286,7 +264,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
zIndex: this.ZInd,
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
display: this.ZInd === -99 ? "none" : undefined,
- pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
+ pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
}} >
{Doc.UserDoc().renderStyle !== "comic" ? (null) :
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 88eb48f51..b3b7cc4f3 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -120,6 +120,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
@@ -149,6 +150,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
ContainingCollectionDoc={undefined}
fitToBox={true}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2408b3906..1b2070c0f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,14 +18,14 @@ import { DocHolderBox } from "./DocHolderBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
-import { MenuIconBox } from "./MenuIconBox";
import { FieldView, FieldViewProps } from "./FieldView";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox";
import { ImageBox } from "./ImageBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
import { PresBox } from "./PresBox";
import { SearchBox } from "../search/SearchBox";
+import { FilterBox } from "./FilterBox";
import { ColorBox } from "./ColorBox";
import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { LinkAnchorBox } from "./LinkAnchorBox";
@@ -93,7 +93,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
}
@observer
-export class DocumentContentsView extends React.Component<DocumentViewProps & {
+export class DocumentContentsView extends React.Component<DocumentViewProps & FormattedTextBoxProps & {
isSelected: (outsideReaction: boolean) => boolean,
select: (ctrl: boolean) => void,
layoutKey: string,
@@ -190,9 +190,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
blacklistedAttrs={[]}
renderInWrapper={false}
components={{
- FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, MenuIconBox, LabelBox, SliderBox, FieldView,
+ FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
- PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox,
+ PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
ScreenshotBox, HTMLtag, ComparisonBox
}}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index cf8645e4c..ab6cae0ad 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -25,7 +25,7 @@ export const Flyout = higflyout.default;
interface DocumentLinksButtonProps {
View: DocumentView;
- Offset?: number[];
+ Offset?: (number | undefined)[];
AlwaysOn?: boolean;
InMenu?: boolean;
StartLink?: boolean;
@@ -36,6 +36,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
private _linkButton = React.createRef<HTMLDivElement>();
@observable public static StartLink: Doc | undefined;
+ @observable public static StartLinkView: DocumentView | undefined;
@observable public static AnnotationId: string | undefined;
@observable public static AnnotationUri: string | undefined;
@@ -79,8 +80,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
//action(() => Doc.BrushDoc(this.props.View.Document));
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
} else if (!this.props.InMenu) {
DocumentLinksButton.EditLink = this.props.View;
@@ -95,8 +98,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
//action(() => Doc.BrushDoc(this.props.View.Document));
@@ -110,6 +115,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
if (doubleTap && !this.props.StartLink) {
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
@@ -129,9 +135,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = e.screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
}
@@ -144,6 +156,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
@@ -151,8 +164,12 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
if (endLinkView) {
- startLink._link = endLinkView._link = linkDoc;
- setTimeout(action(() => startLink._link = endLinkView._link = undefined), 0);
+ endLinkView._link = linkDoc;
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = linkDoc);
+ setTimeout(action(() => {
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = undefined);
+ endLinkView._link = undefined;
+ }), 0);
}
LinkManager.currentLink = linkDoc;
@@ -176,6 +193,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
}
+
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
}
}
@@ -186,12 +214,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@action clearLinks() {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
}
@computed
get linkButton() {
TraceMobx();
- const links = this.props.links;
+ const links = new Set<Doc>(this.props.links);
const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
const buttonTitle = "Tap to view links";
@@ -213,7 +242,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
id={"link-icon"}
src={`/assets/${"link.png"}`} />;
- const linkButton = <div ref={this._linkButton} style={{ minWidth: 20, minHeight: 20, position: "absolute", left: this.props.Offset?.[0] }}>
+ const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{
+ minWidth: 20, minHeight: 20, position: "absolute",
+ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3]
+ }}>
<div className={"documentLinksButton"} style={{
backgroundColor: this.props.InMenu ? "" : "#add8e6",
color: this.props.InMenu ? "white" : "black",
@@ -233,7 +265,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
<FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */}
{this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
- link : links.length}
+ link : Array.from(links).length}
</div>
{this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
@@ -245,7 +277,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
border: DocumentLinksButton.StartLink ? "" : "none"
}}
onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.screenX, e.screenY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+ onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
}
{
DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
@@ -255,7 +287,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
</div >;
- return (!links.length) && !this.props.AlwaysOn ? (null) :
+ return (!Array.from(links).length) && !this.props.AlwaysOn ? (null) :
this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
<Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
{linkButton}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index e6b8928d4..2dd3bba91 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -133,6 +133,8 @@
bottom: 0;
width: 100%;
transform-origin: bottom left;
+ opacity: 0.1;
+ transition: opacity 0.5s;
}
}
@@ -144,4 +146,9 @@
display:inline-block;
}
}
+ > .documentView-styleWrapper {
+ > .documentView-captionWrapper {
+ opacity: 1;
+ }
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d92dc0ec2..f6360fc87 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -21,7 +21,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
import { LinkManager } from '../../util/LinkManager';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from "../../util/SelectionManager";
-import SharingManager from '../../util/SharingManager';
+import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from "../../util/Transform";
import { undoBatch, UndoManager } from "../../util/UndoManager";
@@ -30,6 +30,7 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
+import { InkStrokeProperties } from '../InkStrokeProperties';
import { DocumentContentsView } from "./DocumentContentsView";
import { DocumentLinksButton } from './DocumentLinksButton';
import "./DocumentView.scss";
@@ -38,6 +39,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { RadialMenu } from './RadialMenu';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
export type DocFocusFunc = () => boolean;
@@ -45,11 +47,13 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
FreezeDimensions?: boolean;
NativeWidth: () => number;
NativeHeight: () => number;
Document: Doc;
DataDoc?: Doc;
+ getView?: (view: DocumentView) => any;
LayoutTemplateString?: string;
LayoutTemplate?: () => Opt<Doc>;
LibraryPath: Doc[];
@@ -69,7 +73,7 @@ export interface DocumentViewProps {
removeDocument?: (doc: Doc | Doc[]) => boolean;
moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- setupDragLines?: () => void;
+ setupDragLines?: (snapToDraggedDoc: boolean) => void;
renderDepth: number;
ContentScaling: () => number;
PanelWidth: () => number;
@@ -104,17 +108,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _lastTap: number = 0;
private _doubleTap = false;
private _mainCont = React.createRef<HTMLDivElement>();
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _showKPQuery: boolean = false;
- private _queries: string = "";
private _titleRef = React.createRef<EditableView>();
+ private _dropDisposer?: DragManager.DragDropDisposer;
private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
- private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
@computed get topMost() { return this.props.renderDepth === 0; }
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@@ -128,6 +130,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClickFunc = () => this.onClickHandler;
onDoubleClickFunc = () => this.onDoubleClickHandler;
+ constructor(props: any) {
+ super(props);
+ props.getView?.(this);
+ }
+
handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
this.removeMoveListeners();
this.removeEndListeners();
@@ -174,10 +181,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
- // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
(effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
- // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
@@ -191,7 +198,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._mainCont.current && (this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)));
// this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)));
- if (!this.props.dontRegisterView) {
+ if (!BoolCast(this.rootDoc.dontRegisterView, this.props.dontRegisterView)) {
DocumentManager.Instance.DocumentViews.push(this);
}
}
@@ -285,13 +292,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+ _timeout: NodeJS.Timeout | undefined;
+
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
let stopPropagate = true;
let preventDefault = true;
- !this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
- if (this._doubleTap && this.props.renderDepth) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
+ !this.props.Document._isBackground && this.props.bringToFront(this.props.Document);
+ if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
+ if (this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = undefined;
+ }
if (!(e.nativeEvent as any).formattedHandled) {
if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
const func = () => this.onDoubleClickHandler.script.run({
@@ -301,40 +314,53 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
shiftKey: e.shiftKey
}, console.log);
func();
- } else {
- UndoManager.RunInBatch(() => {
- let fullScreenDoc = this.props.Document;
- if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
- fullScreenDoc = Doc.MakeAlias(this.props.Document);
- fullScreenDoc.layoutKey = "layout_fullScreen";
- }
- this.props.addDocTab(fullScreenDoc, "inTab");
- }, "double tap");
- SelectionManager.DeselectAll();
+ } else if (!Doc.IsSystem(this.props.Document)) {
+ if (this.props.Document.type === DocumentType.INK) {
+ InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
+ } else {
+ UndoManager.RunInBatch(() => {
+ let fullScreenDoc = this.props.Document;
+ if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
+ fullScreenDoc = Doc.MakeAlias(this.props.Document);
+ fullScreenDoc.layoutKey = "layout_fullScreen";
+ }
+ this.props.addDocTab(fullScreenDoc, "add");
+ }, "double tap");
+ SelectionManager.DeselectAll();
+ }
Doc.UnBrushDoc(this.props.Document);
}
}
} else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+ const shiftKey = e.shiftKey;
const func = () => this.onClickHandler.script.run({
this: this.layoutDoc,
self: this.rootDoc,
scriptContext: this.props.scriptContext,
thisContainer: this.props.ContainingCollectionDoc,
documentView: this,
- shiftKey: e.shiftKey
+ shiftKey
}, console.log);
- if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
- UndoManager.RunInBatch(func, "on click");
- } else func();
+ const clickFunc = () => {
+ if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) &&
+ !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc) &&
+ !this.onClickHandler.script.originalScript.includes("selectMainMenu")) {
+ UndoManager.RunInBatch(func, "on click");
+ } else func();
+ };
+ if (this.onDoubleClickHandler) {
+ this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500);
+ } else clickFunc();
} else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
- this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight");
+ this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right");
} else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
} else {
if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
} else {
- SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey);
+ this.select(e.ctrlKey || e.shiftKey);
+ //SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey);
}
preventDefault = false;
}
@@ -564,8 +590,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
deleteClicked = (): void => {
- if (Doc.UserDoc().activeWorkspace === this.props.Document) {
- alert("Can't delete the active workspace");
+ if (CurrentUserUtils.ActiveDashboard === this.props.Document) {
+ alert("Can't delete the active dashboard");
} else {
const selected = SelectionManager.SelectedDocuments().slice();
SelectionManager.DeselectAll();
@@ -601,8 +627,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
drop = async (e: Event, de: DragManager.DropEvent) => {
- if (this.props.Document === Doc.UserDoc().activeWorkspace) {
- alert("linking to document tabs not yet supported. Drop link on document content.");
+ if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
+ if ((e.target as any)?.closest?.("*.lm_content")) {
+ alert("You can't perform this move most likely because you don't have permission to modify the destination.");
+ }
+ else alert("linking to document tabs not yet supported. Drop link on document content.");
return;
}
const makeLink = action((linkDoc: Doc) => {
@@ -617,15 +646,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
LinkDescriptionPopup.popupY = de.y;
LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
});
if (de.complete.annoDragData) {
/// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner
e.stopPropagation();
- de.complete.annoDragData.linkedToDoc = true;
-
- const linkDoc = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
- linkDoc && makeLink(linkDoc);
+ de.complete.annoDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
+ de.complete.annoDragData.linkDocument && makeLink(de.complete.annoDragData.linkDocument);
}
if (de.complete.linkDragData) {
e.stopPropagation();
@@ -666,12 +703,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
toggleBackground = () => {
- this.Document.isBackground = (this.Document.isBackground ? undefined : true);
- this.Document._overflow = this.Document.isBackground ? "visible" : undefined;
- if (this.Document.isBackground) {
+ this.Document._isBackground = (this.Document._isBackground ? undefined : true);
+ this.Document._overflow = this.Document._isBackground ? "visible" : undefined;
+ if (this.Document._isBackground) {
this.props.bringToFront(this.props.Document, true);
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = this.Document[WidthSym]();
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = this.Document[HeightSym]();
+ const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
+ const hgt = this.Document[HeightSym]();
+ this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = wid;
+ this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = hgt;
}
}
@@ -684,7 +723,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
- onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
+ onContextMenu = (e: React.MouseEvent | Touch) => {
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
if (!(e instanceof Touch)) {
if (e.button === 0 && !e.ctrlKey) {
@@ -703,38 +742,41 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
const cm = ContextMenu.Instance;
- if (!cm) return;
+ if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
this.props.contextMenuItems?.().forEach(item =>
- cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+ item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
const appearance = cm.findByDescription("UI Controls...");
const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
- templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
+ templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
//DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
!appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
const options = cm.findByDescription("Options...");
const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
- optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
+ optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
+ !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
- const existingOnClick = cm.findByDescription("OnClick...");
- const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
- onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
- onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
- !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
- onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
- onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
- onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
- !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+ const existingOnClick = cm.findByDescription("OnClick...");
+ const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+ onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+ onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
+ !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
+ onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
+ onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ }
const funcs: ContextMenuProps[] = [];
if (this.layoutDoc.onDragStart) {
@@ -760,18 +802,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
- Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
+ Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
}
- moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+ const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
+ if ((collectionAcl === AclAdmin || collectionAcl === AclEdit) && this.props.removeDocument) {
+ moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+ }
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
- !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
- helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
+ !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+ helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
@@ -811,13 +856,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
}
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
- @computed.struct get linkOffset() { return [-15, 0]; }
+ @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, undefined]; }
@computed get contents() {
const pos = this.props.relative ? "relative " : "absolute";
TraceMobx();
return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this.props.ContainingCollectionView}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
NativeWidth={this.NativeWidth}
@@ -879,31 +925,34 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
- @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.Document); }
+ @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
@computed.struct get allLinks() { return DocListCast(this.Document.links); }
@computed.struct get allAnchors() {
TraceMobx();
if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null;
- return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
+ if ((this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
this.layoutDoc.presBox || // presentationbox nodes
this.rootDoc.type === DocumentType.LINK ||
- this.props.dontRegisterView ? (null) : // view that are not registered
- DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-anchorCont" key={i + 1}>
- <DocumentView {...this.props}
- Document={d}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
- PanelWidth={this.anchorPanelWidth}
- PanelHeight={this.anchorPanelHeight}
- ContentScaling={returnOne}
- dontRegisterView={false}
- forcedBackgroundColor={returnTransparent}
- removeDocument={this.hideLinkAnchor}
- pointerEvents={false}
- LayoutTemplate={undefined}
- LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
- </div >);
+ this.props.dontRegisterView) {// view that are not registered
+ return (null);
+ }
+ const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink(d));
+ return filtered.map((d, i) =>
+ <div className="documentView-anchorCont" key={i + 1}>
+ <DocumentView {...this.props}
+ Document={d}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
+ PanelWidth={this.anchorPanelWidth}
+ PanelHeight={this.anchorPanelHeight}
+ ContentScaling={returnOne}
+ dontRegisterView={false}
+ forcedBackgroundColor={returnTransparent}
+ removeDocument={this.hideLinkAnchor}
+ pointerEvents={false}
+ LayoutTemplate={undefined}
+ LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
+ </div >);
}
@computed get innards() {
TraceMobx();
@@ -925,6 +974,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const captionView = (!showCaption ? (null) :
<div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
+ yMargin={10}
+ xMargin={10}
hideOnLeave={true}
LayoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`}
ContentScaling={returnOne}
@@ -955,7 +1006,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@computed get ignorePointerEvents() {
return this.props.pointerEvents === false ||
- (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
+ (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
(this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
}
@undoBatch
@@ -977,11 +1028,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
renderLock() {
- return (this.Document.isBackground !== undefined || this.isSelected(false)) &&
+ return (this.Document._isBackground !== undefined || this.isSelected(false)) &&
((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
this.props.renderDepth > 0 && !this.props.treeViewDoc ?
<div className="documentView-lock" onClick={this.toggleBackground}>
- <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" />
+ <FontAwesomeIcon icon={this.Document._isBackground ? "unlock" : "lock"} style={{ color: this.Document._isBackground ? "red" : undefined }} size="lg" />
</div>
: (null);
}
@@ -997,7 +1048,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
const borderRounding = this.layoutDoc.borderRounding;
const localScale = fullDegree;
- const highlightColors = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ?
+ const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 92ca276cb..0d1e063af 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -17,7 +17,7 @@ export interface RectangleTemplate {
}
@observer
-export default class FaceRectangles extends React.Component<FaceRectanglesProps> {
+export class FaceRectangles extends React.Component<FaceRectanglesProps> {
render() {
const faces = DocListCast(this.props.document.faces);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9d61ec6d1..fceeced36 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,6 +29,7 @@ export interface FieldViewProps {
dropAction: dropActionType;
backgroundHalo?: () => boolean;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
isSelected: (outsideReaction?: boolean) => boolean;
select: (isCtrlPressed: boolean) => void;
rootSelected: (outsideReaction?: boolean) => boolean;
@@ -58,9 +59,6 @@ export interface FieldViewProps {
ChromeHeight?: () => number;
childLayoutTemplate?: () => Opt<Doc>;
- highlighting?: string[];
- lines?: string[];
- doc?: Doc;
// properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
height?: number;
width?: number;
@@ -133,7 +131,7 @@ export class FieldView extends React.Component<FieldViewProps> {
// );
}
else if (field instanceof List) {
- return <div> {field.map(f => Field.toString(f)).join(", ")} </div>;
+ return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"} </div>;
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
else if (field instanceof WebField) {
diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss
new file mode 100644
index 000000000..b39793f01
--- /dev/null
+++ b/src/client/views/nodes/FilterBox.scss
@@ -0,0 +1,54 @@
+
+
+.filterBox-flyout {
+ width: 400px;
+ display: block;
+ text-align: left;
+ .filterBox-flyout-facet {
+ background-color: lightgray;
+ text-align: left;
+ display: inline-block;
+ position: relative;
+ width: 100%;
+ }
+}
+.filterBox-treeView {
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-left: solid 1px;
+ z-index: 1;
+
+ .filterBox-addfacet {
+ display: inline-block;
+ width: 200px;
+ height: 30px;
+ background: darkGray;
+ text-align: left;
+
+ .filterBox-addFacetButton {
+ display: flex;
+ margin: auto;
+
+ .filterBox-span {
+ margin-right: 15px;
+ }
+ }
+
+ >div,
+ >div>div {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .filterBox-tree {
+ display: inline-block;
+ width: 100%;
+ height: calc(100% - 30px);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
new file mode 100644
index 000000000..790901a29
--- /dev/null
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -0,0 +1,215 @@
+import React = require("react");
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { computed } from "mobx";
+import { observer } from "mobx-react";
+import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { List } from "../../../fields/List";
+import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { Cast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionTreeView } from "../collections/CollectionTreeView";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { SearchBox } from "../search/SearchBox";
+import { FieldView, FieldViewProps } from './FieldView';
+import './FilterBox.scss';
+import { Scripting } from "../../util/Scripting";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
+const FilterBoxDocument = makeInterface(documentSchema);
+
+@observer
+export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
+
+ @computed get allDocs() {
+ const allDocs = new Set<Doc>();
+ if (CollectionDockingView.Instance) {
+ const activeTabs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+ SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc));
+ setTimeout(() => CollectionDockingView.Instance.props.Document.allDocuments = new List<Doc>(Array.from(allDocs)));
+ }
+ return allDocs;
+ }
+
+ @computed get _allFacets() {
+ const noviceReqFields = ["author", "tags", "text", "type"];
+ const noviceLayoutFields: string[] = [];//["_curPage"];
+ const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
+
+ const keys = new Set<string>(noviceFields);
+ this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
+ return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort();
+ }
+ /**
+ * Responds to clicking the check box in the flyout menu
+ */
+ facetClick = (facetHeader: string) => {
+ const targetDoc = CollectionDockingView.Instance.props.Document;
+ const found = DocListCast(this.dataDoc[this.props.fieldKey]).findIndex(doc => doc.title === facetHeader);
+ if (found !== -1) {
+ (this.dataDoc[this.props.fieldKey] as List<Doc>).splice(found, 1);
+ const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+ if (docFilter) {
+ let index: number;
+ while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
+ docFilter.splice(index, 3);
+ }
+ }
+ const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+ if (docRangeFilters) {
+ let index: number;
+ while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) {
+ docRangeFilters.splice(index, 3);
+ }
+ }
+ } else {
+ const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data);
+ var rtfields = 0;
+ const facetValues = Array.from(allCollectionDocs.reduce((set, child) => {
+ const field = child[facetHeader] as Field;
+ const fieldStr = Field.toString(field);
+ if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++;
+ return set.add(fieldStr);
+ }, new Set<string>()));
+
+ let nonNumbers = 0;
+ let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+ facetValues.map(val => {
+ const num = Number(val);
+ if (Number.isNaN(num)) {
+ nonNumbers++;
+ } else {
+ minVal = Math.min(num, minVal);
+ maxVal = Math.max(num, maxVal);
+ }
+ });
+ let newFacet: Opt<Doc>;
+ if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) {
+ newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
+ Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+ newFacet._textBoxPadding = 4;
+ const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
+ newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
+ } else if (facetHeader !== "tags" && nonNumbers / facetValues.length < .1) {
+ newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true });
+ const newFacetField = Doc.LayoutFieldKey(newFacet);
+ const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
+ Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+ const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
+ const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
+ newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
+ newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
+ Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
+ Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
+ const scriptText = `setDocFilterRange(this?.target, "${facetHeader}", range)`;
+ newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
+ } else {
+ newFacet = new Doc();
+ newFacet.sytem = true;
+ newFacet.title = facetHeader;
+ newFacet.treeViewOpen = true;
+ newFacet.type = DocumentType.COL;
+ const capturedVariables = { layoutDoc: targetDoc, dataDoc: (targetDoc.data as any)[0][DataSym] };
+ newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables);
+ }
+ newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
+ }
+ }
+ filterBackground = () => "rgba(105, 105, 105, 0.432)";
+ get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
+ @computed get scriptField() {
+ const scriptText = "setDocFilter(this?.target, heading, this.title, checked)";
+ const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+ return script ? () => script : undefined;
+ }
+
+ render() {
+ const facetCollection = this.props.Document.proto as Doc;
+ const flyout = <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
+ {this._allFacets.map(facet => <label className="filterBox-flyout-facet" key={`${facet}`} onClick={e => this.facetClick(facet)}>
+ <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
+ <span className="checkmark" />
+ {facet}
+ </label>)}
+ </div>;
+
+ return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
+ <div className="filterBox-addFacet" style={{ width: "100%" }} onPointerDown={e => e.stopPropagation()}>
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
+ <div className="filterBox-addFacetButton">
+ <FontAwesomeIcon icon={"edit"} size={"lg"} />
+ <span className="filterBox-span">Choose Facets</span>
+ </div>
+ </Flyout>
+ </div>
+ <div className="filterBox-tree" key="tree">
+ <CollectionTreeView
+ PanelPosition={""}
+ Document={facetCollection}
+ DataDoc={Doc.GetProto(facetCollection)}
+ fieldKey={`${this.props.fieldKey}`}
+ CollectionView={undefined}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ LibraryPath={emptyPath}
+ rootSelected={this.props.rootSelected}
+ renderDepth={1}
+ dropAction={this.props.dropAction}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ isSelected={returnFalse}
+ select={returnFalse}
+ bringToFront={emptyFunction}
+ active={this.props.active}
+ whenActiveChanged={returnFalse}
+ treeViewHideTitle={true}
+ ContentScaling={returnOne}
+ focus={returnFalse}
+ treeViewHideHeaderFields={true}
+ onCheckedClick={this.scriptField}
+ ignoreFields={this.ignoreFields}
+ annotationsKey={""}
+ dontRegisterView={true}
+ backgroundColor={this.filterBackground}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ addDocument={returnFalse} />
+ </div>
+ </div>;
+ }
+}
+
+Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
+ const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
+ const set = new Set<string>();
+ if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+ else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
+ let facetValues = Array.from(set).filter(v => v);
+
+ let nonNumbers = 0;
+ facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
+ const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+ const doc = new Doc();
+ doc.system = true;
+ doc.title = facetValue.toString();
+ doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
+ return doc;
+ });
+ return new List<Doc>(facetValueDocSet);
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 75bc90d7a..33ac85a0e 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -34,12 +34,14 @@
}
}
+.menuButton-circle,
.menuButton-round {
border-radius: 100%;
background-color: black;
+ padding: 0;
.fontIconBox-label {
- margin-left: -10px; // button padding is 10px;
+ //margin-left: -10px; // button padding is 10px;
bottom: 0;
position: absolute;
}
@@ -52,7 +54,6 @@
.menuButton-square {
padding-top: 3px;
padding-bottom: 3px;
- padding-left: 5px;
.fontIconBox-label {
border-radius: 0px;
@@ -62,9 +63,11 @@
}
.menuButton,
+.menuButton-circle,
.menuButton-round,
.menuButton-square {
- width: 100%;
+ margin-left: -5%;
+ width: 110%;
height: 100%;
pointer-events: all;
touch-action: none;
@@ -72,11 +75,7 @@
.menuButton-wrap {
touch-action: none;
border-radius: 8px;
-
- // &:hover {
- // background: rgb(61, 61, 61);
- // cursor: pointer;
- // }
+ width: 100%;
}
.menuButton-icon-square {
@@ -89,4 +88,13 @@
width: 95% !important;
height: 95%;
}
+}
+.menuButton-round {
+ width: 100%;
+ svg {
+ width: 50% !important;
+ height: 50%;
+ position: relative;
+ bottom: 2px;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index fd71876b0..a34bf64b0 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -39,7 +39,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this.props.addDocTab(dragFactory, "onRight");
+ dragFactory && this.props.addDocTab(dragFactory, "add:right");
}
dragAsTemplate = (): void => {
this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
@@ -63,15 +63,18 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
const color = StrCast(this.layoutDoc.color, this._foregroundColor);
const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth)));
- const shape = StrCast(this.layoutDoc.iconShape, "round");
-
+ const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
+ const icon = StrCast(this.dataDoc.icon, "user") as any;
+ const presSize = shape === 'round' ? 25 : 30;
+ const presTrailsIcon = <img src={`/assets/${"presTrails.png"}`}
+ style={{ width: presSize, height: presSize, filter: `invert(${color === "white" ? "100%" : "0%"})`, marginBottom: "5px" }} />;
const button = <button className={`menuButton-${shape}`} ref={this._ref} onContextMenu={this.specificContextMenu}
style={{
boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined,
backgroundColor: this.layoutDoc.iconShape === "square" ? backgroundColor : "",
}}>
<div className="menuButton-wrap">
- {<FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={StrCast(this.dataDoc.icon, "user") as any} color={color}
+ {icon === 'pres-trail' ? presTrailsIcon : <FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={icon} color={color}
size={this.layoutDoc.iconShape === "square" ? "sm" : "lg"} />}
{!label ? (null) : <div className="fontIconBox-label" style={{ color, backgroundColor }}> {label} </div>}
{this.props.Document.watchedDocuments ? <FontIconBadge collection={Cast(this.props.Document.watchedDocuments, Doc, null)} /> : (null)}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 216c5f39e..688bac725 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,8 +1,5 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx';
import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
import { documentSchema } from '../../../fields/documentSchemas';
@@ -14,7 +11,8 @@ import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { AudioField, ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
+import { emptyFunction, returnOne, returnZero, Utils } from '../../../Utils';
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs } from '../../documents/Documents';
import { Networking } from '../../Network';
@@ -24,22 +22,16 @@ import { ContextMenu } from "../../views/ContextMenu";
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import FaceRectangles from './FaceRectangles';
+import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-const requestImageSize = require('../../util/request-image-size');
const path = require('path');
const { Howl } = require('howler');
-library.add(faImage, faEye as any, faPaintBrush, faBrain);
-library.add(faFileAudio, faAsterisk);
-
-
export const pageSchema = createSchema({
- curPage: "number",
+ _curPage: "number",
fitWidth: "boolean",
googlePhotosUrl: "string",
googlePhotosTags: "string"
@@ -70,15 +62,25 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _dropDisposer?: DragManager.DragDropDisposer;
+ private _pathDisposer?: IReactionDisposer;
@observable private _audioState = 0;
@observable static _showControls: boolean;
@observable uploadIcon = uploadIcons.idle;
protected createDropTarget = (ele: HTMLDivElement) => {
- this._dropDisposer && this._dropDisposer();
+ this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
+ componentDidMount() {
+ this._pathDisposer = reaction(() => this.paths.length && this.resize(this.paths[0]),
+ () => true,
+ { fireImmediately: true });
+ }
+ componentWillUnmount() {
+ this._pathDisposer?.();
+ }
+
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
@@ -111,23 +113,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}).then(function (stream) {
gumStream = stream;
recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async function (e: any) {
- const formData = new FormData();
- formData.append("file", e.data);
- const res = await fetch(Utils.prepend("/uploadFormData"), {
- method: 'POST',
- body: formData
- });
- const files = await res.json();
- const url = Utils.prepend(files[0].path);
- // upload to server with known URL
- const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = "layout";
- const audioAnnos = Cast(this.dataDoc[this.fieldKey + "-audioAnnotations"], listSpec(Doc));
- if (audioAnnos === undefined) {
- this.dataDoc[this.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
- } else {
- audioAnnos.push(audioDoc);
+ recorder.ondataavailable = async (e: any) => {
+ const [{ result }] = await Networking.UploadFilesToServer(e.data);
+ if (!(result instanceof Error)) {
+ const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+ audioDoc.treeViewExpandedView = "layout";
+ const audioAnnos = Cast(self.dataDoc[self.fieldKey + "-audioAnnotations"], listSpec(Doc));
+ if (audioAnnos === undefined) {
+ self.dataDoc[self.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
+ } else {
+ audioAnnos.push(audioDoc);
+ }
}
};
runInAction(() => self._audioState = 2);
@@ -158,7 +154,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
- funcs.push({ description: "Make Background", event: () => { this.layoutDoc.isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Make Background", event: () => { this.layoutDoc._isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
if (!Doc.UserDoc().noviceMode) {
funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
@@ -255,29 +251,29 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
resize = (imgPath: string) => {
const basePath = imgPath.replace(/_[oms]./, "");
+ const curPath = this.dataDoc[this.fieldKey + "-path"];
const cachedNativeSize = {
- width: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0,
- height: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0,
+ width: basePath === curPath || !curPath ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0,
+ height: basePath === curPath || !curPath ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0,
};
const docAspect = this.layoutDoc[HeightSym]() / this.layoutDoc[WidthSym]();
const cachedAspect = cachedNativeSize.height / cachedNativeSize.width;
if (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(NumCast(this.layoutDoc._width) / NumCast(this.layoutDoc._height) - cachedNativeSize.width / cachedNativeSize.height) > 0.05) {
if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) {
- requestImageSize(imgPath).then(action((inquiredSize: any) => {
- const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
- const rotatedNativeSize = { width: inquiredSize.width, height: inquiredSize.height };
- if (inquiredSize.orientation === 6 || rotation === 90 || rotation === 270) {
- rotatedNativeSize.width = inquiredSize.height;
- rotatedNativeSize.height = inquiredSize.width;
- }
- const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width;
- if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
- this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect;
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.layoutDoc._nativeWidth = this.layoutDoc._width;
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.layoutDoc._nativeHeight = this.layoutDoc._height;
- this.dataDoc[this.fieldKey + "-path"] = basePath;
- }
- })).catch(console.log);
+ const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
+ const orientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"]);
+ if (orientation === 6 || rotation === 90 || rotation === 270) {
+ this.layoutDoc._nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+ this.layoutDoc._nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
+ } else {
+ this.layoutDoc._nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
+ this.layoutDoc._nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+ }
+ const rotatedAspect = NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth);
+ if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
+ this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect;
+ this.dataDoc[this.fieldKey + "-path"] = basePath;
+ }
} else if (Math.abs(1 - docAspect / cachedAspect) > 0.1) {
this.layoutDoc._width = this.layoutDoc[WidthSym]() || cachedNativeSize.width;
this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect;
@@ -398,8 +394,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
const { nativeWidth, nativeHeight } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
const aspect = (rotation % 180) ? nativeHeight / nativeWidth : 1;
- const shift = (rotation % 180) ? (nativeHeight - nativeWidth) * (1 - 1 / aspect) : 0;
- this.resize(srcpath);
let transformOrigin = "center center";
let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`;
if (rotation === 90 || rotation === -270) {
@@ -437,7 +431,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
>
<FontAwesomeIcon className="imageBox-audioFont"
style={{ color: [DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }}
- icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : faFileAudio} size="sm" />
+ icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
</div>}
{this.considerDownloadIcon}
{this.considerGooglePhotosLink()}
@@ -465,7 +459,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
}} >
<CollectionFreeFormView {...this.props}
@@ -489,6 +483,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
ScreenToLocalTransform={this.screenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index b732f5f83..c5ff42a1a 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -230,7 +230,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
openItems.push({
description: "Default Perspective", event: () => {
this.props.addDocTab(this.props.Document, "close");
- this.props.addDocTab(this.fieldDocToLayout, "onRight");
+ this.props.addDocTab(this.fieldDocToLayout, "add:right");
}, icon: "image"
});
!open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4568a6b16..74d10d087 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
@@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
}
@@ -56,7 +56,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
Document: this.props.doc,
DataDoc: this.props.doc,
LibraryPath: [],
- docFilters:returnEmptyFilter,
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: undefined,
ContainingCollectionDoc: undefined,
fieldKey: this.props.keyName,
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index b605df262..109a02df4 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -8,6 +8,7 @@
}
.labelBox-mainButton {
+ max-width: 100%;
width: fit-content;
height: max-content;
border-radius: inherit;
diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index 42ef2958e..62ee9513c 100644
--- a/src/client/views/nodes/LinkAnchorBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -22,6 +22,11 @@
padding-left: 2px;
padding-top: 1px;
}
+ .linkAnchorBox-button {
+ pointer-events: all;
+ position: relative;
+ display: inline-block;
+ }
}
.linkAnchorBox-cont-small {
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 50b2af0d7..10b6aa02e 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -73,7 +73,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false);
+ DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "add:right")), false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
@@ -87,14 +87,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
}
openLinkDocOnRight = (e: React.MouseEvent) => {
- this.props.addDocTab(this.rootDoc, "onRight");
+ this.props.addDocTab(this.rootDoc, "add:right");
}
openLinkTargetOnRight = (e: React.MouseEvent) => {
const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
alias.isLinkButton = undefined;
- alias.isBackground = undefined;
+ alias._isBackground = undefined;
alias.layoutKey = "layout";
- this.props.addDocTab(alias, "onRight");
+ this.props.addDocTab(alias, "add:right");
}
@action
openLinkEditor = action((e: React.MouseEvent) => {
@@ -148,7 +148,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
}} >
{!this._editing && !this._forceOpen ? (null) :
<Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}>
- <span className="parentDocumentSelector-button" >
+ <span className="linkAnchorBox-button" >
<FontAwesomeIcon icon={"eye"} size={"lg"} />
</span>
</Flyout>}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 532e7dc15..a067f23af 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -26,6 +26,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
NativeWidth={returnZero}
ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
annotationsKey={""}
+ dontRegisterView={true}
+ renderDepth={this.props.renderDepth + 1}
CollectionView={undefined}
addDocument={returnFalse}
removeDocument={returnFalse}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c4481b213..dddefc17f 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import wiki from "wikijs";
import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { Transform } from "../../util/Transform";
@@ -22,6 +22,7 @@ interface Props {
}
@observer
export class LinkDocPreview extends React.Component<Props> {
+ static TargetDoc: Doc | undefined;
@observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>;
@observable _targetDoc: Opt<Doc>;
@observable _toolTipText = "";
@@ -40,14 +41,16 @@ export class LinkDocPreview extends React.Component<Props> {
async followDefault() {
DocumentLinksButton.EditLink = undefined;
LinkDocPreview.LinkInfo = undefined;
- this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "onRight"), false) : null;
+ this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "add:right"), false) : null;
}
+ componentWillUnmount() { LinkDocPreview.TargetDoc = undefined; }
componentDidUpdate() { this.updatePreview(); }
componentDidMount() { this.updatePreview(); }
async updatePreview() {
const linkDoc = this.props.linkDoc;
const linkSrc = this.props.linkSrc;
+ LinkDocPreview.TargetDoc = undefined;
if (this.props.href) {
if (this.props.href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(this.props.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
@@ -59,7 +62,7 @@ export class LinkDocPreview extends React.Component<Props> {
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
runInAction(() => {
this._toolTipText = "";
- this._targetDoc = target;
+ LinkDocPreview.TargetDoc = this._targetDoc = target;
if (anchor !== this._targetDoc && anchor && this._targetDoc) {
this._targetDoc._scrollY = NumCast(anchor?.y);
}
@@ -69,13 +72,13 @@ export class LinkDocPreview extends React.Component<Props> {
pointerDown = (e: React.PointerEvent) => {
if (this.props.linkDoc && this.props.linkSrc) {
DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
} else if (this.props.href) {
- this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight");
+ this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
}
}
- width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
- height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
+ width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)) - 16;
+ height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)) - 16
@computed get targetDocView() {
return !this._targetDoc ?
<div style={{
@@ -101,11 +104,12 @@ export class LinkDocPreview extends React.Component<Props> {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
renderDepth={0}
- PanelWidth={() => this.width() - 16} //Math.min(350, NumCast(target._width, 350))}
- PanelHeight={() => this.height() - 16} //Math.min(250, NumCast(target._height, 250))}
+ PanelWidth={this.width} //Math.min(350, NumCast(target._width, 350))}
+ PanelHeight={this.height} //Math.min(250, NumCast(target._height, 250))}
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
@@ -119,7 +123,7 @@ export class LinkDocPreview extends React.Component<Props> {
return <div className="linkDocPreview"
style={{
position: "absolute", left: this.props.location[0],
- top: this.props.location[1], width: this.width(), height: this.height(),
+ top: this.props.location[1], width: this.width() + 16, height: this.height() + 16,
zIndex: 1000,
border: "8px solid white", borderRadius: "7px",
boxShadow: "3px 3px 1.5px grey",
diff --git a/src/client/views/nodes/MenuIconBox.scss b/src/client/views/nodes/MenuIconBox.scss
deleted file mode 100644
index 1b72f5a8f..000000000
--- a/src/client/views/nodes/MenuIconBox.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-.menuButton {
- //padding: 7px;
- padding-left: 7px;
- width: 100%;
- width: 60px;
- height: 70px;
-
- .menuButton-wrap {
- width: 45px;
- /* padding: 5px; */
- touch-action: none;
- background: black;
- transform-origin: top left;
- /* margin-bottom: 5px; */
- margin-top: 5px;
- margin-right: 25px;
- border-radius: 8px;
-
- &:hover {
- background: rgb(61, 61, 61);
- cursor: pointer;
- }
- }
-
- .menuButton-label {
- color: white;
- margin-right: 4px;
- border-radius: 8px;
- width: 42px;
- position: relative;
- text-align: center;
- font-size: 8px;
- margin-top: 1px;
- letter-spacing: normal;
- padding: 3px;
- background-color: inherit;
- }
-
- .menuButton-icon {
- width: auto;
- height: 35px;
- padding: 5px;
- }
-
- svg {
- width: 95% !important;
- height: 95%;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx
deleted file mode 100644
index 5ed8a9b78..000000000
--- a/src/client/views/nodes/MenuIconBox.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { createSchema, makeInterface } from '../../../fields/Schema';
-import { StrCast } from '../../../fields/Types';
-import { DocComponent } from '../DocComponent';
-import { FieldView, FieldViewProps } from './FieldView';
-import './MenuIconBox.scss';
-const MenuIconSchema = createSchema({
- icon: "string"
-});
-
-type MenuIconDocument = makeInterface<[typeof MenuIconSchema]>;
-const MenuIconDocument = makeInterface(MenuIconSchema);
-@observer
-export class MenuIconBox extends DocComponent<FieldViewProps, MenuIconDocument>(MenuIconDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MenuIconBox, fieldKey); }
- _ref: React.RefObject<HTMLButtonElement> = React.createRef();
-
- render() {
-
- const color = this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) === "lightgrey" ? "black" : "white";
- const menuBTN = <div className="menuButton" style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }}>
- <div className="menuButton-wrap"
- style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }} >
- <FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size="lg" />
- <div className="menuButton-label" style={{ color: color }}> {this.dataDoc.title} </div>
- </div>
- </div>;
-
- return menuBTN;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 255a1b2d0..a2b406c3f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -21,6 +21,7 @@ import { KeyCodes } from '../../util/KeyCodes';
import "./PDFBox.scss";
import React = require("react");
import { documentSchema } from '../../../fields/documentSchemas';
+import { CollectionViewType } from '../collections/CollectionView';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -99,20 +100,28 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
public search = (string: string, fwd: boolean) => { this._pdfViewer?.search(string, fwd); };
public prevAnnotation = () => { this._pdfViewer?.prevAnnotation(); };
public nextAnnotation = () => { this._pdfViewer?.nextAnnotation(); };
- public backPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) - 1); };
- public forwardPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) + 1); };
- public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); };
+ public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; };
+ public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; };
+ public gotoPage = (p: number) => { this.Document._curPage = p; };
@undoBatch
onKeyDown = action((e: KeyboardEvent) => {
+ let processed = false;
if (e.key === "f" && e.ctrlKey) {
this._searching = true;
setTimeout(() => this._searchRef.current && this._searchRef.current.focus(), 100);
+ processed = true;
+ }
+ if (e.key === "PageDown") processed = this.forwardPage();
+ if (e.key === "PageUp") processed = this.backPage();
+ if (e.target instanceof HTMLInputElement || this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) {
+ if (e.key === "ArrowDown" || e.key === "ArrowRight") processed = this.forwardPage();
+ if (e.key === "ArrowUp" || e.key === "ArrowLeft") processed = this.backPage();
+ }
+ if (processed) {
e.stopImmediatePropagation();
e.preventDefault();
}
- if (e.key === "PageDown" || e.key === "ArrowDown" || e.key === "ArrowRight") this.forwardPage();
- if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") this.backPage();
});
@undoBatch
@@ -150,7 +159,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
</button>
</>;
const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
- const curPage = this.Document.curPage || 1;
+ const curPage = this.Document._curPage || 1;
return !this.active() ? (null) :
(<div className="pdfBox-ui" onKeyDown={e => e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true}
onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none" }}>
@@ -177,7 +186,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
<div className="pdfBox-pageNums">
<input value={curPage}
- onChange={e => this.gotoPage(Number(e.currentTarget.value))}
+ onChange={e => this.Document._curPage = Number(e.currentTarget.value)}
style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: "all" }}
onClick={action(() => this._pageControls = !this._pageControls)} />
{this._pageControls ? pageBtns : (null)}
@@ -246,7 +255,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
<PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
- addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters}
+ addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters} searchFilterDocs={this.props.searchFilterDocs}
pinToPres={this.props.pinToPres} addDocument={this.addDocument}
Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index c4d8f1a4f..08160a2f4 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -488,7 +488,7 @@ $light-background: #ececec;
max-width: 200px;
overflow: visible;
-
+
.presBox-dropdownOption {
cursor: pointer;
font-size: 11;
@@ -967,10 +967,10 @@ $light-background: #ececec;
/* border: solid 1px; */
color: white;
/* box-shadow: black 0.4vw 0.4vw 0.8vw; */
-
+
.miniPresOverlay {
display: grid;
- grid-template-columns: auto auto auto auto auto auto auto auto;
+ grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
grid-template-rows: 100%;
height: 100%;
justify-items: center;
@@ -1028,4 +1028,4 @@ $light-background: #ececec;
background-color: #5a5a5a;
}
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 5fc76223d..07b2d51d1 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,34 +1,33 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, DocCastAsync, WidthSym } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types";
-import { returnFalse, returnOne, numberRange, returnTrue } from "../../../Utils";
+import { ColorState, SketchPicker } from "react-color";
+import { Doc, DocCastAsync, DocListCast } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
-import { DocumentManager } from "../../util/DocumentManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView";
-import { CollectionView, CollectionViewType } from "../collections/CollectionView";
-import { FieldView, FieldViewProps } from './FieldView';
-import { DocumentType } from "../../documents/DocumentTypes";
-import "./PresBox.scss";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { InkTool } from "../../../fields/InkField";
+import { List } from "../../../fields/List";
import { PrefetchProxy } from "../../../fields/Proxy";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { returnFalse, returnOne } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DocumentManager } from "../../util/DocumentManager";
import { Scripting } from "../../util/Scripting";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { List } from "../../../fields/List";
-import { Tooltip } from "@material-ui/core";
-import { actionAsync } from "mobx-utils";
import { SelectionManager } from "../../util/SelectionManager";
+import { undoBatch } from "../../util/UndoManager";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionView, CollectionViewType } from "../collections/CollectionView";
+import { TabDocView } from "../collections/TabDocView";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { AudioBox } from "./AudioBox";
-import { DocumentView } from "./DocumentView";
-import { SketchPicker, ColorState } from "react-color";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./PresBox.scss";
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
const PresBoxDocument = makeInterface(documentSchema);
@@ -58,9 +57,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@observable private expandBoolean: boolean = false;
@observable private openMovementDropdown: boolean = false;
@observable private openEffectDropdown: boolean = false;
-
@computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
@computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
+ @computed get activeItem() { return Cast(this.childDocs[this.itemIndex], Doc, null); }
+ @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
@computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
constructor(props: any) {
super(props);
@@ -86,14 +86,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
} else { return undefined; }
}
@computed get isPres(): boolean {
+ document.removeEventListener("keydown", this.keyEvents, true);
if (this.selectedDoc?.type === DocumentType.PRES) {
- document.removeEventListener("keydown", this.keyEvents, true);
document.addEventListener("keydown", this.keyEvents, true);
return true;
- } else {
- document.removeEventListener("keydown", this.keyEvents, true);
- return false;
}
+ return false;
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@@ -108,6 +106,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.rootDoc._replacedChrome = "replaced";
this.layoutDoc.presStatus = "edit";
this.layoutDoc._gridGap = 5;
+ if (!DocListCast((Doc.UserDoc().myPresentations as Doc).data).includes(this.rootDoc)) {
+ Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc);
+ }
}
updateCurrentPresentation = () => {
@@ -123,37 +124,40 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
next = () => {
this.updateCurrentPresentation();
const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const presTargetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- const childDocs = DocListCast(presTargetDoc[Doc.LayoutFieldKey(presTargetDoc)]);
- const currentFrame = Cast(presTargetDoc.currentFrame, "number", null);
- const lastFrame = Cast(presTargetDoc.lastFrame, "number", null);
- const curFrame = NumCast(presTargetDoc.currentFrame);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+ const currentFrame = Cast(targetDoc._currentFrame, "number", null);
+ const lastFrame = Cast(targetDoc.lastFrame, "number", null);
+ const curFrame = NumCast(targetDoc._currentFrame);
let internalFrames: boolean = false;
- if (presTargetDoc.presProgressivize || activeItem.zoomProgressivize || presTargetDoc.scrollProgressivize) internalFrames = true;
+ if (targetDoc.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
// Case 1: There are still other frames and should go through all frames before going to next slide
if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
- presTargetDoc._viewTransition = "all 1s";
- setTimeout(() => presTargetDoc._viewTransition = undefined, 1010);
- presTargetDoc.currentFrame = curFrame + 1;
- if (presTargetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(presTargetDoc, currentFrame);
- if (presTargetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, presTargetDoc);
- else presTargetDoc.editing = true;
- if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(presTargetDoc);
+ targetDoc._viewTransition = "all 1s";
+ setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+ targetDoc._currentFrame = curFrame + 1;
+ if (targetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(targetDoc, currentFrame);
+ if (targetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
+ else targetDoc.editing = true;
+ if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(targetDoc);
// Case 2: Audio or video therefore wait to play the audio or video before moving on
- } else if ((presTargetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio && this.layoutDoc.presStatus !== 'auto') {
+ } else if ((targetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio && this.layoutDoc.presStatus !== 'auto') {
AudioBox.Instance.playFrom(0);
this._moveOnFromAudio = true;
// Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
} else if (this.childDocs[this.itemIndex + 1] !== undefined) {
const nextSelected = this.itemIndex + 1;
- if (presTargetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause();
+ if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause();
this.gotoDocument(nextSelected, this.itemIndex);
const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
if (activeNext && targetNext.type === DocumentType.AUDIO && activeNext.playAuto) {
AudioBox.Instance.playFrom(0);
this._moveOnFromAudio = true;
} else this._moveOnFromAudio = false;
+ } else if (this.childDocs[this.itemIndex + 1] === undefined && this.layoutDoc.presLoop) {
+ const nextSelected = 0;
+ this.gotoDocument(0, this.itemIndex);
}
}
@@ -166,19 +170,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
back = () => {
this.updateCurrentPresentation();
- const docAtCurrent = this.childDocs[this.itemIndex];
- const targetDoc = Cast(docAtCurrent.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
- const curFrame = NumCast(targetDoc.currentFrame);
+ const curFrame = NumCast(targetDoc._currentFrame);
if (lastFrame !== undefined && curFrame >= 1) {
- this.prevKeyframe(targetDoc, docAtCurrent);
- } else if (docAtCurrent) {
+ this.prevKeyframe(targetDoc, activeItem);
+ } else if (activeItem) {
let prevSelected = this.itemIndex;
prevSelected = Math.max(0, prevSelected - 1);
this.gotoDocument(prevSelected, this.itemIndex);
- if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc.currentFrame = NumCast(prevTargetDoc.lastFrame);
+ if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame);
}
}
@@ -209,8 +213,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* on the right.
*/
navigateToElement = async (curDoc: Doc) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const srcContext = await DocCastAsync(targetDoc.context);
const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined;
@@ -233,7 +237,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
- this.props.addDocTab(activeItem, "replace");
+ this.props.addDocTab(activeItem, "replace:right");
} else
//docToJump stayed same meaning, it was not in the group or was the last element in the group
if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
@@ -254,9 +258,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// adjust the pan and scale to that of the pinView when it was added.
// TODO: Add option to remove presPinView
if (activeItem.presPinView) {
- targetDoc._panX = activeItem.presPinViewX;
- targetDoc._panY = activeItem.presPinViewY;
- targetDoc._viewScale = activeItem.presPinViewScale;
+ // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+ bestTarget && runInAction(() => {
+ bestTarget._viewTransition = "all 1s";
+ bestTarget._panX = activeItem.presPinViewX;
+ bestTarget._panY = activeItem.presPinViewY;
+ bestTarget._viewScale = activeItem.presPinViewScale;
+ });
+ //setTimeout(() => targetDoc._viewTransition = undefined, 1010);
}
// If website and has presWebsite data associated then on click it should
// go back to that specific website
@@ -271,7 +281,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* @param presTargetDoc: document for which internal zoom is used
*/
zoomProgressivizeNext = (activeItem: Doc) => {
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
const srcContext = Cast(targetDoc?.context, Doc, null);
const docView = DocumentManager.Instance.getDocumentView(targetDoc);
const vfLeft = this.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
@@ -346,8 +356,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
startAutoPres = (startSlide: number) => {
this.updateCurrentPresentation();
- let activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- let targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ let activeItem: Doc = this.activeItem;
+ let targetDoc: Doc = this.targetDoc;
let duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
const load = async () => { // Wrap the loop into an async function for this to work
@@ -363,17 +373,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
await timer(duration); this.next(); // then the created Promise can be awaited
- if (i === this.childDocs.length - 1) setTimeout(() => { clearTimeout(this._presTimer); if (this.layoutDoc.presStatus === 'auto') this.layoutDoc.presStatus = "manual"; }, duration);
+ if (i === this.childDocs.length - 1) {
+ setTimeout(() => {
+ clearTimeout(this._presTimer);
+ if (this.layoutDoc.presStatus === 'auto' && !this.layoutDoc.presLoop) this.layoutDoc.presStatus = "manual";
+ else if (this.layoutDoc.presLoop) this.startAutoPres(0);
+ }, duration);
+ }
}
};
+ this.layoutDoc.presStatus = "auto";
+ this.startPresentation(startSlide);
+ this.gotoDocument(startSlide, this.itemIndex);
+ load();
+ }
+
+ @action
+ pauseAutoPres = () => {
if (this.layoutDoc.presStatus === "auto") {
if (this._presTimer) clearTimeout(this._presTimer);
this.layoutDoc.presStatus = "manual";
- } else {
- this.layoutDoc.presStatus = "auto";
- this.startPresentation(startSlide);
- this.gotoDocument(startSlide, this.itemIndex);
- load();
+ this.layoutDoc.presLoop = false;
}
}
@@ -401,12 +421,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
startPresentation = (startIndex: number) => {
this.updateCurrentPresentation();
this.childDocs.map(doc => {
- const presTargetDoc = doc.presentationTargetDoc as Doc;
+ const tagDoc = doc.presentationTargetDoc as Doc;
if (doc.presHideTillShownButton && this.childDocs.indexOf(doc) > startIndex) {
- presTargetDoc.opacity = 0;
+ tagDoc.opacity = 0;
}
if (doc.presHideAfterButton && this.childDocs.indexOf(doc) < startIndex) {
- presTargetDoc.opacity = 0;
+ tagDoc.opacity = 0;
}
});
}
@@ -418,11 +438,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
updateMinimize = () => {
+ const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
if (this.layoutDoc.inOverlay) {
this.layoutDoc.presStatus = 'edit';
- Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
- CollectionDockingView.AddRightSplit(this.rootDoc);
+ Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+ CollectionDockingView.AddSplit(this.rootDoc, "right");
this.layoutDoc.inOverlay = false;
+ } else if (this.layoutDoc.context && docView) {
+ this.layoutDoc.presStatus = 'manual';
+ const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
+ this.rootDoc.y = pt[1] + 10;
+ this.rootDoc._height = 35;
+ this.rootDoc._width = 250;
+ docView.props.removeDocument?.(this.layoutDoc);
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
} else {
this.layoutDoc.presStatus = 'manual';
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
@@ -431,7 +461,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.rootDoc._height = 35;
this.rootDoc._width = 250;
this.props.addDocTab?.(this.rootDoc, "close");
- Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
}
}
@@ -453,31 +483,32 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* When the movement dropdown is changes
*/
@undoBatch
- movementChanged = action((movement: string) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ updateMovement = action((movement: any, activeItem: Doc, targetDoc: Doc) => {
switch (movement) {
case 'zoom': //Pan and zoom
- activeItem.presZoomButton = !activeItem.presZoomButton;
- if (activeItem.presZoomButton) activeItem.presMovement = 'Zoom';
- else activeItem.presMovement = 'None';
activeItem.presNavButton = false;
+ activeItem.presZoomButton = !activeItem.presZoomButton;
+ targetDoc.presTransition = activeItem.presTransition;
+ if (activeItem.presZoomButton) activeItem.presMovement = 'zoom';
+ else activeItem.presMovement = 'none';
break;
case 'pan': //Pan
activeItem.presZoomButton = false;
activeItem.presNavButton = !activeItem.presNavButton;
- if (activeItem.presNavButton) activeItem.presMovement = 'Pan';
- else activeItem.presMovement = 'None';
+ targetDoc.presTransition = activeItem.presTransition;
+ if (activeItem.presNavButton) activeItem.presMovement = 'pan';
+ else activeItem.presMovement = 'none';
break;
case 'jump': //Jump Cut
+ activeItem.presTransition = targetDoc.presTransition;
targetDoc.presTransition = 0;
activeItem.presZoomButton = true;
activeItem.presSwitchButton = !activeItem.presSwitchButton;
- if (activeItem.presSwitchButton) activeItem.presMovement = 'Jump cut';
- else activeItem.presMovement = 'None';
+ if (activeItem.presSwitchButton) activeItem.presMovement = 'jump';
+ else activeItem.presMovement = 'none';
break;
case 'none': default:
- activeItem.presMovement = 'None';
+ activeItem.presMovement = 'none';
activeItem.presZoomButton = false;
activeItem.presNavButton = false;
activeItem.presSwitchButton = false;
@@ -485,6 +516,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
});
+ setMovementName = action((movement: any, activeItem: Doc): string => {
+ let output: string = 'none';
+ switch (movement) {
+ case 'zoom': output = 'Zoom'; break; //Pan and zoom
+ case 'pan': output = 'Pan'; break; //Pan
+ case 'jump': output = 'Jump cut'; break; //Jump Cut
+ case 'none': output = 'None'; break; //None
+ default: output = 'Zoom'; activeItem.presMovement = 'zoom'; break; //default set as zoom
+ }
+ return output;
+ });
+
whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
// For dragging documents into the presentation trail
addDocumentFilter = (doc: Doc | Doc[]) => {
@@ -501,10 +544,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return true;
}
childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
- removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
+ removeDocument = (doc: Doc) => { Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
panelHeight = () => this.props.PanelHeight() - 40;
- active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground) &&
+ active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
(this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
/**
@@ -526,10 +569,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@computed get listOfSelected() {
const list = this._selectedArray.map((doc: Doc, index: any) => {
- const activeItem = Cast(doc, Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc!, Doc, null);
+ const curDoc = Cast(doc, Doc, null);
+ const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
return (
- <div className="selectedList-items">{index + 1}. {targetDoc.title}</div>
+ <div className="selectedList-items">{index + 1}. {tagDoc.title}</div>
);
});
return list;
@@ -563,8 +606,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
this._selectedArray = [];
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- if (activeItem) {
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ if (this.activeItem) {
for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
this._selectedArray.push(this.childDocs[i]);
this._eleArray.push(ref);
@@ -575,8 +618,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
// Key for when the presentaiton is active
- @action
- keyEvents = (e: KeyboardEvent) => {
+ @undoBatch
+ keyEvents = action((e: KeyboardEvent) => {
+ if (e.target instanceof HTMLInputElement) return;
let handled = false;
const anchorNode = document.activeElement as HTMLDivElement;
if (anchorNode && anchorNode.className?.includes("lm_title")) return;
@@ -592,29 +636,44 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
handled = true;
}
} if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back
- this.back(); if (this._presTimer) clearTimeout(this._presTimer);
+ this.back();
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next
- this.next(); if (this._presTimer) clearTimeout(this._presTimer);
+ this.next();
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 32) { // spacebar to 'present' or autoplay
if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0);
else this.layoutDoc.presStatus = "manual"; if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
- }
- if (e.keyCode === 8) { // delete selected items
+ } if (e.keyCode === 8) { // delete selected items
if (this.layoutDoc.presStatus === "edit") {
- this._selectedArray.forEach((doc, i) => {
- this.removeDocument(doc);
- });
+ this._selectedArray.forEach((doc, i) => this.removeDocument(doc));
+ this._selectedArray = [];
+ this._eleArray = [];
+ this._dragArray = [];
handled = true;
}
- }
- if (handled) {
+ } if (handled) {
e.stopPropagation();
e.preventDefault();
+ } if ((e.keyCode === 37 || e.keyCode === 38) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
+ if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
+ const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
+ if ((index - 1) > 0) this._selectedArray.push(this.childDocs[index - 1]);
+ }
+ handled = true;
+ } if ((e.keyCode === 39 || e.keyCode === 40) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
+ if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
+ const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
+ if ((index - 1) > 0) {
+ this._selectedArray.push(this.childDocs[index - 1]);
+ }
+ }
+ handled = true;
}
- }
+ });
/**
*
@@ -636,12 +695,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get order() {
const order: JSX.Element[] = [];
this.childDocs.forEach((doc, index) => {
- const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc?.context, Doc, null);
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ const srcContext = Cast(tagDoc?.context, Doc, null);
// Case A: Document is contained within the colleciton
if (this.rootDoc.presCollection === srcContext) {
order.push(
- <div className="pathOrder" style={{ top: NumCast(targetDoc.y), left: NumCast(targetDoc.x) }}>
+ <div className="pathOrder" style={{ top: NumCast(tagDoc.y), left: NumCast(tagDoc.x) }}>
<div className="pathOrder-frame">{index + 1}</div>
</div>);
// Case B: Document is not inside of the collection
@@ -666,11 +725,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get paths() {
let pathPoints = "";
this.childDocs.forEach((doc, index) => {
- const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc?.context, Doc, null);
- if (targetDoc && this.rootDoc.presCollection === srcContext) {
- const n1x = NumCast(targetDoc.x) + (NumCast(targetDoc._width) / 2);
- const n1y = NumCast(targetDoc.y) + (NumCast(targetDoc._height) / 2);
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ const srcContext = Cast(tagDoc?.context, Doc, null);
+ if (tagDoc && this.rootDoc.presCollection === srcContext) {
+ const n1x = NumCast(tagDoc.x) + (NumCast(tagDoc._width) / 2);
+ const n1y = NumCast(tagDoc.y) + (NumCast(tagDoc._height) / 2);
if (index = 0) pathPoints = n1x + "," + n1y;
else pathPoints = pathPoints + " " + n1x + "," + n1y;
} else {
@@ -701,8 +760,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
activeItem.presFadeButton = !activeItem.presFadeButton;
if (!activeItem.presFadeButton) {
if (targetDoc) {
@@ -722,9 +781,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 10000) timeInMS = 10000;
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- if (targetDoc) targetDoc.presTransition = timeInMS;
+ if (this.targetDoc) this.targetDoc.presTransition = timeInMS;
}
// Converts seconds to ms and updates presDuration
@@ -733,15 +790,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 20000) timeInMS = 20000;
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- if (targetDoc) targetDoc.presDuration = timeInMS;
+ if (this.targetDoc) this.targetDoc.presDuration = timeInMS;
}
@computed get transitionDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
const transitionSpeed = targetDoc.presTransition ? NumCast(targetDoc.presTransition) / 1000 : 0.5;
let duration = targetDoc.presDuration ? NumCast(targetDoc.presDuration) / 1000 : 2;
@@ -753,21 +808,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Movement
<div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
- {activeItem.presMovement}
+ {this.setMovementName(activeItem.presMovement, activeItem)}
<FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'None' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('none')}>None</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('zoom')}>Pan and Zoom</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('pan')}>Pan</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Jump cut' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('jump')}>Jump cut</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === 'none' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('none', activeItem, targetDoc)}>None</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === 'zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('zoom', activeItem, targetDoc)}>Pan and Zoom</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === 'pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('pan', activeItem, targetDoc)}>Pan</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === 'jump' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement('jump', activeItem, targetDoc)}>Jump cut</div>
</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "inline-flex" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Transition Speed</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={transitionSpeed}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -779,8 +834,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
</div>
- <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
- <div className={`slider-headers ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`}>
+ <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
+ <div className={`slider-headers ${activeItem.presMovement === 'pan' || activeItem.presMovement === 'zoom' ? "" : "none"}`}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
<div className="slider-text">Slow</div>
@@ -797,7 +852,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={duration}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setDurationTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -858,10 +913,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get effectDirection(): string {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
let effect = '';
- switch (targetDoc.presEffectDirection) {
+ switch (this.targetDoc.presEffectDirection) {
case 'left': effect = "Enter from left"; break;
case 'right': effect = "Enter from right"; break;
case 'top': effect = "Enter from top"; break;
@@ -874,8 +927,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
applyTo = (array: Doc[]) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
array.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
@@ -885,14 +938,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tagDoc.presEffect = targetDoc.presEffect;
tagDoc.presEffectDirection = targetDoc.presEffectDirection;
curDoc.presMovement = activeItem.presMovement;
+ this.updateMovement(activeItem.presMovement, curDoc, tagDoc);
curDoc.presHideTillShownButton = activeItem.presHideTillShownButton;
curDoc.presHideAfterButton = activeItem.presHideAfterButton;
}
});
}
@computed get optionsDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
return (
<div>
@@ -909,6 +963,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-button" style={{ backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
onClick={() => {
activeItem.presPinView = !activeItem.presPinView;
+ targetDoc.presPinView = activeItem.presPinView;
if (activeItem.presPinView) {
const x = targetDoc._panX;
const y = targetDoc._panY;
@@ -926,7 +981,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewX)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
</div>
</div>
@@ -936,7 +991,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewY)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
</div>
</div>
@@ -946,7 +1001,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewScale)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
</div>
</div>
@@ -996,9 +1051,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Slide Title: <br></br>
<input className="ribbon-textInput" placeholder="..." type="text" name="fname"
- onFocus={() => {
- document.removeEventListener("keydown", this.keyEvents, true);
- }}
+ onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={(e) => {
e.stopPropagation();
e.preventDefault();
@@ -1052,21 +1105,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (layout) doc = this.createTemplate(layout);
if (freeform && layout) doc = this.createTemplate(layout, title);
if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title });
- const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
- const data = Cast(presCollection?.data, listSpec(Doc));
- const presData = Cast(this.rootDoc.data, listSpec(Doc));
- if (data && doc && presData) {
- data.push(doc);
- DockedFrameRenderer.PinDoc(doc, false);
- this.gotoDocument(this.childDocs.length, this.itemIndex);
- } else {
- this.props.addDocTab(doc as Doc, "onRight");
+ if (doc) {
+ const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
+ const data = Cast(presCollection?.data, listSpec(Doc));
+ const presData = Cast(this.rootDoc.data, listSpec(Doc));
+ if (data && presData) {
+ data.push(doc);
+ TabDocView.PinDoc(doc, false);
+ this.gotoDocument(this.childDocs.length, this.itemIndex);
+ } else {
+ this.props.addDocTab(doc, "add:right");
+ }
}
}
createTemplate = (layout: string, input?: string) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
let x = 0;
let y = 0;
if (activeItem && targetDoc) {
@@ -1120,9 +1175,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// Case in which the document has keyframes to navigate to next key frame
@undoBatch
@action
- nextKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
+ nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc.currentFrame, "number", null);
+ const currentFrame = Cast(tagDoc._currentFrame, "number", null);
if (currentFrame === undefined) {
tagDoc.currentFrame = 0;
CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
@@ -1130,37 +1185,37 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame);
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
- tagDoc.currentFrame = Math.max(0, (currentFrame || 0) + 1);
- tagDoc.lastFrame = Math.max(NumCast(tagDoc.currentFrame), NumCast(tagDoc.lastFrame));
- if (activeItem.zoomProgressivize) {
+ tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
+ tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
+ if (curDoc.zoomProgressivize) {
const resize = document.getElementById('resizable');
if (resize) {
- resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
+ resize.style.width = this.checkList(tagDoc, curDoc["viewfinder-width-indexed"]) + 'px';
+ resize.style.height = this.checkList(tagDoc, curDoc["viewfinder-height-indexed"]) + 'px';
+ resize.style.top = this.checkList(tagDoc, curDoc["viewfinder-top-indexed"]) + 'px';
+ resize.style.left = this.checkList(tagDoc, curDoc["viewfinder-left-indexed"]) + 'px';
}
}
}
@undoBatch
@action
- prevKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
+ prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc.currentFrame, "number", null);
+ const currentFrame = Cast(tagDoc._currentFrame, "number", null);
if (currentFrame === undefined) {
- tagDoc.currentFrame = 0;
+ tagDoc._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
}
CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
- tagDoc.currentFrame = Math.max(0, (currentFrame || 0) - 1);
- if (activeItem.zoomProgressivize) {
+ tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
+ if (actItem.zoomProgressivize) {
const resize = document.getElementById('resizable');
if (resize) {
- resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
+ resize.style.width = this.checkList(tagDoc, actItem["viewfinder-width-indexed"]) + 'px';
+ resize.style.height = this.checkList(tagDoc, actItem["viewfinder-height-indexed"]) + 'px';
+ resize.style.top = this.checkList(tagDoc, actItem["viewfinder-top-indexed"]) + 'px';
+ resize.style.left = this.checkList(tagDoc, actItem["viewfinder-left-indexed"]) + 'px';
}
}
}
@@ -1169,8 +1224,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* Returns the collection type as a string for headers
*/
@computed get stringType(): string {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
let type: string = '';
if (activeItem) {
switch (targetDoc.type) {
@@ -1193,11 +1248,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get progressivizeDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
- const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
+ const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
+ const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
return (
<div>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
@@ -1209,7 +1264,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Active text color</div>
- <div className="ribbon-property" style={{ backgroundColor: activeFontColor }} onClick={action(() => { console.log("hi"); this.openActiveColorPicker = !this.openActiveColorPicker; })}>
+ <div className="ribbon-property" style={{ backgroundColor: activeFontColor }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
</div>
</div>
{this.activeColorPicker}
@@ -1237,7 +1292,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.editing ? "white" : "black", backgroundColor: targetDoc.editing ? "#5B9FDD" : "#AEDDF8" }}
onClick={action(() => targetDoc.editing = !targetDoc.editing)} >
- {NumCast(targetDoc.currentFrame)}
+ {NumCast(targetDoc._currentFrame)}
</div>
<div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-right"} size={"lg"} />
@@ -1256,8 +1311,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
switchActive = (color: ColorState) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const val = String(color.hex);
targetDoc["pres-text-color"] = val;
return true;
@@ -1265,16 +1320,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
switchPresented = (color: ColorState) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const val = String(color.hex);
targetDoc["pres-text-viewed-color"] = val;
return true;
}
@computed get activeColorPicker() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
return !this.openActiveColorPicker ? (null) : <SketchPicker onChange={this.switchActive}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -1283,8 +1338,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get viewedColorPicker() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
return !this.openViewedColorPicker ? (null) : <SketchPicker onChange={this.switchPresented}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -1308,8 +1363,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Toggle whether the user edits or not
@action
editZoomProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (!targetDoc.editZoomProgressivize) {
if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; targetDoc.zoomProgressivize = true;
targetDoc.editZoomProgressivize = true;
@@ -1323,8 +1378,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Toggle whether the user edits or not
@action
editScrollProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (!targetDoc.editScrollProgressivize) {
if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; activeItem.scrollProgressivize = true; }
targetDoc.editScrollProgressivize = true;
@@ -1337,14 +1392,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeScroll = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const activeItem: Doc = this.activeItem;
activeItem.scrollProgressivize = !activeItem.scrollProgressivize;
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
- CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc.currentFrame), true);
+ CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
if (targetDoc.editScrollProgressivize) {
targetDoc.editScrollProgressivize = false;
- targetDoc.currentFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.lastFrame = 0;
}
}
@@ -1353,14 +1408,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeZoom = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const activeItem: Doc = this.activeItem;
activeItem.zoomProgressivize = !activeItem.zoomProgressivize;
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize;
- CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc, true);
+ CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc);
if (activeItem.editZoomProgressivize) {
activeItem.editZoomProgressivize = false;
- targetDoc.currentFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.lastFrame = 0;
}
}
@@ -1368,9 +1423,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Progressivize Child Docs
@action
editProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- targetDoc.currentFrame = targetDoc.lastFrame;
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ targetDoc._currentFrame = targetDoc.lastFrame;
if (!targetDoc.editProgressivize) {
if (!activeItem.presProgressivize) { activeItem.presProgressivize = true; targetDoc.presProgressivize = true; }
targetDoc.editProgressivize = true;
@@ -1382,21 +1437,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeChild = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
if (!activeItem.presProgressivize) {
targetDoc.editing = false;
activeItem.presProgressivize = true;
targetDoc.presProgressivize = true;
- targetDoc.currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
+ targetDoc._currentFrame = 0;
+ docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
targetDoc.lastFrame = docs.length - 1;
} else {
targetDoc.editProgressivize = false;
activeItem.presProgressivize = false;
targetDoc.presProgressivize = false;
- targetDoc.currentFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.lastFrame = 0;
targetDoc.editing = true;
}
@@ -1438,18 +1493,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
checkList = (doc: Doc, list: any): number => {
const x: List<number> = list;
- if (x && x.length >= NumCast(doc.currentFrame) + 1) {
- return x[NumCast(doc.currentFrame)];
+ if (x && x.length >= NumCast(doc._currentFrame) + 1) {
+ return x[NumCast(doc._currentFrame)];
} else if (x) {
- x.length = NumCast(doc.currentFrame) + 1;
- x[NumCast(doc.currentFrame)] = x[NumCast(doc.currentFrame) - 1];
- return x[NumCast(doc.currentFrame)];
+ x.length = NumCast(doc._currentFrame) + 1;
+ x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
+ return x[NumCast(doc._currentFrame)];
} else return 100;
}
@computed get progressivizeChildDocs() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
const tags: JSX.Element[] = [];
docs.forEach((doc, index) => {
@@ -1457,7 +1511,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
}
tags.push(
- <div className="progressivizeButton" onPointerLeave={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
+ <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
<div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
<div className="progressivizeButton-frame">{doc.appearFrame}</div>
<div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
@@ -1469,8 +1523,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
nextAppearFrame = (doc: Doc, i: number): void => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
const appearFrame = Cast(doc.appearFrame, "number", null);
if (appearFrame === undefined) {
doc.appearFrame = 0;
@@ -1482,8 +1536,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
prevAppearFrame = (doc: Doc, i: number): void => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
const appearFrame = Cast(doc.appearFrame, "number", null);
if (appearFrame === undefined) {
doc.appearFrame = 0;
@@ -1604,12 +1658,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get playButtonFrames() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
return (
<>
- {targetDoc ? <div className="miniPres-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
- <div>{targetDoc.currentFrame}</div>
+ {this.targetDoc ? <div className="miniPres-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
+ <div>{targetDoc._currentFrame}</div>
<div className="miniPres-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
<div>{targetDoc.lastFrame}</div>
</div> : null}
@@ -1620,8 +1673,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get playButtons() {
// Case 1: There are still other frames and should go through all frames before going to next slide
return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanel-divider"></div>
<div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="presPanel-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
<div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
<div className="presPanel-divider"></div>
<div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
@@ -1636,6 +1691,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>);
}
+ @action
+ startOrPause = () => {
+ if (this.layoutDoc.presStatus === "manual") this.startAutoPres(this.itemIndex);
+ else this.pauseAutoPres();
+ }
+
render() {
// calling this method for keyEvents
this.isPres;
@@ -1645,8 +1706,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return this.layoutDoc.inOverlay ?
<div className="miniPres" style={{ width: 250, height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
{<div className="miniPresOverlay">
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="miniPres-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="miniPres-divider"></div>
<div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="miniPres-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
<div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
<div className="miniPres-divider"></div>
<div className="miniPres-button-text">
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx
index bd5b3bff4..8876b4879 100644
--- a/src/client/views/nodes/RadialMenuItem.tsx
+++ b/src/client/views/nodes/RadialMenuItem.tsx
@@ -1,13 +1,9 @@
import React = require("react");
-import { observable, action } from "mobx";
-import { observer } from "mobx-react";
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { observer } from "mobx-react";
import { UndoManager } from "../../util/UndoManager";
-library.add(faAngleRight);
-
export interface RadialMenuProps {
description: string;
event: (stuff?: any) => void;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 1cd29d795..5d51c420b 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,12 +1,11 @@
import React = require("react");
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faVideo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from 'request-promise';
import { Doc } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
+import { InkTool } from "../../../fields/InkField";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { Cast, NumCast } from "../../../fields/Types";
import { VideoField } from "../../../fields/URLField";
@@ -18,14 +17,11 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
import "./ScreenshotBox.scss";
-import { InkTool } from "../../../fields/InkField";
const path = require('path');
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
const ScreenshotDocument = makeInterface(documentSchema);
-library.add(faVideo);
-
@observer
export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
private _reactionDisposer?: IReactionDisposer;
@@ -77,7 +73,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
const spt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
imageSummary.x = spt[0];
imageSummary.y = spt[1];
- Cast(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
+ Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
} else {
this.props.addDocument?.(imageSummary);
}
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 45cdfc5ad..d13680046 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -1,5 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit } from '@fortawesome/free-regular-svg-icons';
import { runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -16,9 +14,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
import './SliderBox.scss';
-
-library.add(faEdit as any);
-
const SliderSchema = createSchema({
_sliderMin: "number",
_sliderMax: "number",
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ee92e517c..d50a10bdd 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -23,7 +23,7 @@ import { SnappingManager } from "../../util/SnappingManager";
const path = require('path');
export const timeSchema = createSchema({
- currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
+ _currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
});
type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>;
const VideoDocument = makeInterface(documentSchema, timeSchema);
@@ -82,7 +82,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action public FullScreen() {
this._fullScreen = true;
this.player && this.player.requestFullscreen();
- this._youtubePlayer && this.props.addDocTab(this.rootDoc, "inTab");
+ this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
}
choosePath(url: string) {
@@ -109,14 +109,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
if (!this._videoRef) {
const b = Docs.Create.LabelDocument({
x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
- _width: 150, _height: 50, title: (this.layoutDoc.currentTimecode || 0).toString(),
+ _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
});
b.isLinkButton = true;
this.props.addDocument?.(b);
DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
Networking.PostToServer("/youtubeScreenshot", {
id: this.youtubeVideoId,
- timecode: this.layoutDoc.currentTimecode
+ timecode: this.layoutDoc._currentTimecode
}).then(response => {
const resolved = response?.accessPaths?.agnostic?.client;
if (resolved) {
@@ -128,7 +128,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
//convert to desired file format
const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
- const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc.currentTimecode || 0).toString().replace(/\./, "_")));
+ const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => {
if (returnedFilename) {
this.createRealSummaryLink(returnedFilename);
@@ -144,7 +144,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const imageSummary = Docs.Create.ImageDocument(url, {
_nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
- _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc.currentTimecode || 0) + " image-"
+ _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
});
Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
@@ -155,8 +155,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action
updateTimecode = () => {
- this.player && (this.layoutDoc.currentTimecode = this.player.currentTime);
- this._youtubePlayer && (this.layoutDoc.currentTimecode = this._youtubePlayer.getCurrentTime());
+ this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+ this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime());
}
componentDidMount() {
@@ -185,9 +185,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
this._videoRef = vref;
if (vref) {
this._videoRef!.ontimeupdate = this.updateTimecode;
+ // @ts-ignore
vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
- this._reactionDisposer && this._reactionDisposer();
- this._reactionDisposer = reaction(() => (this.layoutDoc.currentTimecode || 0),
+ this._reactionDisposer?.();
+ this._reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
@@ -272,21 +273,21 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const onYoutubePlayerReady = (event: any) => {
this._reactionDisposer?.();
this._youtubeReactionDisposer?.();
- this._reactionDisposer = reaction(() => this.layoutDoc.currentTimecode, () => !this._playing && this.Seek((this.layoutDoc.currentTimecode || 0)));
+ this._reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
this._youtubeReactionDisposer = reaction(
- () => Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+ () => !this.props.Document.isAnnotating && Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
(interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
};
this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
events: {
- 'onReady': onYoutubePlayerReady,
- 'onStateChange': onYoutubePlayerStateChange,
+ 'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+ 'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
}
});
}
private get uIButtons() {
- const curTime = (this.layoutDoc.currentTimecode || 0);
+ const curTime = (this.layoutDoc._currentTimecode || 0);
return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} >
<span>{"" + Math.round(curTime)}</span>
<span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
@@ -328,7 +329,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
onResetMove = (e: PointerEvent) => {
this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY);
- this.Seek(Math.max(0, (this.layoutDoc.currentTimecode || 0) + Math.sign(e.movementX) * 0.0333));
+ this.Seek(Math.max(0, (this.layoutDoc._currentTimecode || 0) + Math.sign(e.movementX) * 0.0333));
e.stopImmediatePropagation();
}
@@ -336,14 +337,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
onResetUp = (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onResetMove, true);
document.removeEventListener("pointerup", this.onResetUp, true);
- this._isResetClick < 10 && (this.layoutDoc.currentTimecode = 0);
+ this._isResetClick < 10 && (this.layoutDoc._currentTimecode = 0);
}
@computed get youtubeContent() {
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
- const start = untracked(() => Math.round((this.layoutDoc.currentTimecode || 0)));
+ const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
@@ -353,7 +354,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach(doc => {
- const curTime = (this.layoutDoc.currentTimecode || -1);
+ const curTime = (this.layoutDoc._currentTimecode || -1);
curTime !== -1 && (doc.displayTimecode = curTime);
});
return this.addDocument(doc);
@@ -384,6 +385,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 875142169..134860d0a 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -70,7 +70,6 @@
}
}
-
.webBox-overlay {
width: 100%;
height: 100%;
@@ -82,61 +81,24 @@
background:lightGray;
width: 100%;
}
- .webBox-freeze {
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 5px;
- width: 30px;
- }
-
- .webBox-urlEditor {
- position: relative;
- opacity: 0.9;
+ .webBox-annotationToggle {
z-index: 9001;
- transition: top .5s;
-
- .urlEditor {
- display: grid;
- grid-template-columns: 1fr auto;
- padding-bottom: 10px;
- overflow: hidden;
-
- .editorBase {
- display: flex;
-
- .editor-collapse {
- transition: all .5s, opacity 0.3s;
- position: absolute;
- width: 40px;
- transform-origin: top left;
- }
-
- .switchToText {
- color: $main-accent;
- }
-
- .switchToText:hover {
- color: $dark-color;
- }
- }
-
- button:hover {
- transform: scale(1);
- }
+ position: absolute;
+ top: 2;
+ left: 2;
+ box-shadow: black 0.3em 0.3em 1em;
+ border-radius: 5px;
+ display: flex;
+ opacity: 0.3;
+ width: 25px;
+ height: 25px;
+ align-items: center;
+ > svg {
+ margin: auto;
}
}
-
- .webpage-urlInput {
- padding: 12px 10px 11px 10px;
- border: 0px;
- color: grey;
- letter-spacing: 2px;
- outline-color: black;
- background: rgb(238, 238, 238);
- width: 100%;
- margin-right: 10px;
- height: 100%;
+ .webBox-annotationToggle:hover {
+ opacity: 1;
}
.touch-iframe-overlay {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1393e7868..c5d7c3c9f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -24,13 +24,14 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
-import Annotation from "../pdf/Annotation";
-import PDFMenu from "../pdf/PDFMenu";
+import { Annotation } from "../pdf/Annotation";
+import { PDFMenu } from "../pdf/PDFMenu";
import { PdfViewerMarquee } from "../pdf/PDFViewer";
import { FieldView, FieldViewProps } from './FieldView';
import "./WebBox.scss";
import "../pdf/PDFViewer.scss";
import React = require("react");
+import { Tooltip } from '@material-ui/core';
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -235,81 +236,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
e.stopPropagation();
}
- toggleAnnotationMode = () => {
- this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating;
- }
-
- urlEditor() {
- return (
- <div className="webBox-urlEditor"
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover} style={{ top: this._collapsed ? -70 : 0 }}>
- <div className="urlEditor">
- <div className="editorBase">
- <button className="editor-collapse"
- style={{
- top: this._collapsed ? 70 : 0,
- transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`,
- opacity: (this._collapsed && !this.props.isSelected()) ? 0 : 0.9,
- left: (this._collapsed ? 0 : "unset"),
- }}
- title="Collapse Url Editor" onClick={this.toggleCollapse}>
- <FontAwesomeIcon icon="caret-up" size="2x" />
- </button>
- <div className="webBox-buttons"
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover} style={{ display: this._collapsed ? "none" : "flex" }}>
- <div className="webBox-freeze" title={"Annotate"} style={{ background: this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
- <FontAwesomeIcon icon={faPen} size={"2x"} />
- </div>
- <div className="webBox-freeze" title={"Select"} style={{ background: !this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
- <FontAwesomeIcon icon={faMousePointer} size={"2x"} />
- </div>
- <input className="webpage-urlInput"
- placeholder="ENTER URL"
- value={this._url}
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover}
- onChange={this.onURLChange}
- onKeyDown={this.onValueKeyDown}
- onClick={(e) => {
- this._keyInput.current!.select();
- e.stopPropagation();
- }}
- ref={this._keyInput}
- />
- <div style={{
- display: "flex",
- flexDirection: "row",
- justifyContent: "space-between",
- maxWidth: "120px",
- }}>
- <button className="submitUrl" onClick={this.submitURL}
- onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
- GO
- </button>
- <button className="submitUrl" onClick={this.back}>
- <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
- </button>
- <button className="submitUrl" onClick={this.forward}>
- <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
- </button>
- </div>
- </div>
- </div>
- </div>
+ editToggleBtn() {
+ return <Tooltip title={<div className="dash-tooltip" >{`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}</div>}>
+ <div className="webBox-annotationToggle"
+ style={{ color: this.props.Document.isAnnotating ? "black" : "white", backgroundColor: this.props.Document.isAnnotating ? "white" : "black" }}
+ onClick={action(() => this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating)}>
+ <FontAwesomeIcon icon="edit" size="sm" />
</div>
- );
+ </Tooltip>;
}
-
- @action
- toggleCollapse = () => {
- this._collapsed = !this._collapsed;
- }
-
-
-
_ignore = 0;
onPreWheel = (e: React.WheelEvent) => {
this._ignore = e.timeStamp;
@@ -438,7 +374,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
const funcs: ContextMenuProps[] = [];
- funcs.push({ description: (this.layoutDoc.UseCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.UseCors = !this.layoutDoc.UseCors, icon: "snowflake" });
+ funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@@ -453,7 +389,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
if (field instanceof HtmlField) {
view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
+ const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href;
view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
// the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
@@ -474,16 +410,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
style={{ width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%" }}
onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
{view}
- </div>;
+ </div>
{!frozen ? (null) :
- <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+ <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc._isBackground ? undefined : "all" }}
onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
<div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
<div className="indicator" ref={this._iframeIndicatorRef}></div>
<div className="dragger" ref={this._iframeDragRef}></div>
</div>
</div>}
- {this.urlEditor()}
</>);
}
@@ -560,12 +495,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
clipDoc.rootDocument = targetDoc;
targetDoc.layoutKey = "layout";
- const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
+ const annotationDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
dragComplete: e => {
- if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
- DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
+ if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) {
+ e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
annotationDoc.isLinkButton = true;
}
}
@@ -682,7 +617,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
transform: `scale(${this.props.ContentScaling()})`,
width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}
onContextMenu={this.specificContextMenu}>
<base target="_blank" />
@@ -690,7 +625,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%",
- pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none"
+ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc._isBackground ? "all" : "none"
}}
onWheel={e => e.stopPropagation()}
onPointerDown={this.onMarqueeDown}
@@ -709,7 +644,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}}>
<div className={"webBox-innerContent"} style={{
height: NumCast(this.layoutDoc.scrollHeight),
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}>
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
@@ -733,6 +668,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
ScreenToLocalTransform={this.scrollXf}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
</CollectionFreeFormView>
</div>
@@ -740,6 +676,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{this.annotationLayer}
<PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
</div >
+
+ {this.props.isSelected() ? this.editToggleBtn() : null}
</div>);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 212da3f3d..3b77735a7 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,13 +5,14 @@ import { Id } from "../../../../fields/FieldSymbols";
import { ObjectField } from "../../../../fields/ObjectField";
import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import { Transform } from "../../../util/Transform";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
interface IDashDocView {
node: any;
@@ -114,7 +115,7 @@ export class DashDocView extends React.Component<IDashDocView> {
}
/*endregion*/
- componentWillMount = () => {
+ componentWillUnmount = () => {
this._reactionDisposer?.();
}
@@ -175,7 +176,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const outerStyle = {
position: "relative" as "relative",
textIndent: "0",
- border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+ border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")),
width: this.props.node.props.width,
height: this.props.node.props.height,
display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +203,7 @@ export class DashDocView extends React.Component<IDashDocView> {
({ dim, color }) => {
spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
- outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -254,7 +255,8 @@ export class DashDocView extends React.Component<IDashDocView> {
whenActiveChanged={returnFalse}
bringToFront={emptyFunction}
dontRegisterView={false}
- docFilters={this.props.tbox?.props.docFilters||returnEmptyFilter}
+ docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+ searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8ae71c035..f2658e77e 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -169,7 +169,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
onPointerDownEnumerables = async (e: any) => {
e.stopPropagation();
const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
- collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight");
+ collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right");
}
@@ -183,7 +183,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
if (container) {
const alias = Doc.MakeAlias(container.props.Document);
- alias.viewType = CollectionViewType.Time;
+ alias._viewType = CollectionViewType.Time;
let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
if (!list) {
alias._columnHeaders = list = new List<SchemaHeaderField>();
@@ -191,7 +191,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
- this.props.tbox.props.addDocTab(alias, "onRight");
+ this.props.tbox.props.addDocTab(alias, "add:right");
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index afdd8fea2..160f4ba72 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -32,8 +32,8 @@
.formattedTextBox-dictation {
height: 12px;
width: 10px;
- top: 0px;
- left: 0px;
+ bottom: 5px;
+ right: 8px;
position: absolute;
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2ded33346..311143ff7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,5 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEqual } from "lodash";
import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction, trace } from "mobx";
@@ -22,7 +20,7 @@ import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { createSchema, makeInterface } from "../../../../fields/Schema";
+import { makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
@@ -33,8 +31,8 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { DictationManager } from '../../../util/DictationManager';
import { DragManager } from "../../../util/DragManager";
import { makeTemplate } from '../../../util/DropConverter';
-import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer";
-import RichTextMenu from './RichTextMenu';
+import { buildKeymap, updateBullets } from "./ProsemirrorExampleTransfer";
+import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from "./RichTextRules";
//import { DashDocView } from "./DashDocView";
@@ -61,9 +59,6 @@ import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark }
import React = require("react");
import { DocumentManager } from '../../../util/DocumentManager';
-library.add(faEdit);
-library.add(faSmile, faTextHeight, faUpload);
-
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
@@ -99,8 +94,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _linkTime: number | null = null;
private _pause: boolean = false;
- @computed get _recording() { return this.dataDoc.audioState === "recording"; }
- set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
+ @computed get _recording() { return this.dataDoc?.audioState === "recording"; }
+ set _recording(value) {
+ this.dataDoc.audioState = value ? "recording" : undefined;
+ }
@observable private _entered = false;
@@ -225,7 +222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
- const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value });
+ const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value });
const mval = this._editorView.state.schema.marks.metadataVal.create();
const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -350,7 +347,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
updateTitle = () => {
if ((this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
- StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.rootDoc.customTitle) {
+ StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] &&
+ Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey) {
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
const str = node.textContent;
@@ -374,10 +372,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
}
}
- public highlightSearchTerms = (terms: string[], alt: boolean) => {
+ public highlightSearchTerms = (terms: string[], backward: boolean) => {
if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
-
-
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
@@ -385,31 +381,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
- if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) {
- this._searchIndex = 0;
- Doc.GetProto(this.dataDoc).resetSearch = undefined;
- }
- else {
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
- if (alt === true) {
- if (this._searchIndex > 1) {
- this._searchIndex += -2;
- }
- else if (this._searchIndex === 1) {
- this._searchIndex = length - 1;
- }
- else if (this._searchIndex === 0 && length !== 1) {
- this._searchIndex = length - 2;
- }
-
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ if (backward === true) {
+ if (this._searchIndex > 1) {
+ this._searchIndex += -2;
}
+ else if (this._searchIndex === 1) {
+ this._searchIndex = length - 1;
+ }
+ else if (this._searchIndex === 0 && length !== 1) {
+ this._searchIndex = length - 2;
+ }
+
}
const lastSel = Math.min(flattened.length - 1, this._searchIndex);
flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
flattened[lastSel] && this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
-
- console.log(this._searchIndex);
}
}
@@ -472,12 +460,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
} else if (de.complete.linkDragData) {
de.complete.linkDragData.linkDropCallback = this.linkDrop;
}
+ else if (de.complete.annoDragData) {
+ de.complete.annoDragData.linkDropCallback = this.linkDrop;
+ }
}
- linkDrop = (data: DragManager.LinkDragData) => {
+ linkDrop = (data: { linkDocument?: Doc }) => {
const linkDoc = data.linkDocument!;
const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-";
const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id);
+ this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id);
}
getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
@@ -511,10 +502,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (node.isTextblock) {
let index = 0, foundAt;
const ep = this.getNodeEndpoints(pm.state.doc, node);
- while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
- const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
- ret.push(sel);
- index = index + foundAt + find.length;
+ const regexp = new RegExp(find.replace("*", ""), "i");
+ if (regexp) {
+ while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+ ret.push(sel);
+ index = index + foundAt + find.length;
+ }
}
} else {
node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find)));
@@ -664,7 +658,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const newBullets: Doc[] = this.recursiveProgressivize(1, list)[0];
mainBulletList.push.apply(mainBulletList, newBullets);
}
- console.log(mainBulletList.length);
const title = Docs.Create.TextDocument(StrCast(this.rootDoc.title), { title: "Title", _width: 800, _height: 70, x: 20, y: -10, _fontSize: '20pt', backgroundColor: "rgba(0,0,0,0)", appearFrame: 0, _fontWeight: 700 });
mainBulletList.push(title);
const doc = Docs.Create.FreeformDocument(mainBulletList, {
@@ -719,7 +712,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
recordDictation = () => {
DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
+ interimHandler: this.setDictationContent,
continuous: { indefinite: false },
}).then(results => {
if (results && [DictationManager.Controls.Infringed].includes(results)) {
@@ -730,22 +723,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
stopDictation = (abort: boolean) => { DictationManager.Controls.stop(!abort); };
- recordBullet = async () => {
- const completedCue = "end session";
- const results = await DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
- continuous: { indefinite: false },
- terminators: [completedCue, "bullet", "next"]
- });
- if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) {
- DictationManager.Controls.stop();
- return;
- }
- this.nextBullet(this._editorView!.state.selection.to);
- setTimeout(this.recordBullet, 2000);
- }
-
- setCurrentBulletContent = (value: string) => {
+ setDictationContent = (value: string) => {
if (this._editorView) {
const state = this._editorView.state;
const now = Date.now();
@@ -760,33 +738,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
}
- const recordingStart = DateCast(this.props.Document.recordingStart).date.getTime();
- this._break = false;
- value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
const from = state.selection.from;
- const inserted = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
- this._editorView.dispatch(inserted.setSelection(TextSelection.create(inserted.doc, from, from + value.length + 1)));
- }
- }
-
- nextBullet = (pos: number) => {
- if (this._editorView) {
- const frag = Fragment.fromArray(this.newListItems(2));
- if (this._editorView.state.doc.resolve(pos).depth >= 2) {
- const slice = new Slice(frag, 2, 2);
- let state = this._editorView.state;
- this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
- pos += 4;
- state = this._editorView.state;
- this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos)));
+ this._break = false;
+ if (this.props.Document.recordingStart) {
+ const recordingStart = DateCast(this.props.Document.recordingStart)?.date.getTime();
+ value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
}
+ const tr = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
+ this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, from + value.length + 1)));
}
}
- private newListItems = (count: number) => {
- return numberRange(count).map(x => schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create()));
- }
-
_keymap: any = undefined;
_rules: RichTextRules | undefined;
@computed get config() {
@@ -852,13 +814,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (linkDoc) {
const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id);
+ this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
}
},
{ fireImmediately: true }
);
this._disposers.editorState = reaction(
() => {
+ if (!this.dataDoc || !this.layoutDoc) return undefined;
if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) {
return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data;
}
@@ -894,13 +857,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
);
this._disposers.autoHeight = reaction(
- () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
- () => setTimeout(() => this.tryUpdateHeight(), 0)
+ () => ({
+ width: NumCast(this.layoutDoc._width),
+ autoHeight: this.layoutDoc?._autoHeight
+ }),
+ ({ width, autoHeight }) => width !== undefined && setTimeout(() => this.tryUpdateHeight(), 0)
);
this._disposers.height = reaction(
- () => this.layoutDoc[HeightSym](),
+ () => NumCast(this.layoutDoc._height),
action(height => {
- if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
+ if (height !== undefined && height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
this.layoutDoc._delayAutoHeight = height;
}
})
@@ -908,36 +874,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.searchAlt = reaction(() => this.rootDoc.searchMatchAlt,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], false) : this.unhighlightSearchTerms(),
- { fireImmediately: true });
- this._disposers.search = reaction(() => this.rootDoc.searchMatch,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], true) : this.unhighlightSearchTerms(),
- { fireImmediately: this.rootDoc.searchMatch ? true : false });
-
- this._disposers.record = reaction(() => this._recording,
- () => {
- if (this._recording) {
- setTimeout(action(() => {
- this.stopDictation(true);
- setTimeout(() => this.recordDictation(), 500);
- }), 500);
- } else setTimeout(() => this.stopDictation(true), 0);
- }
- );
+ this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
+ search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
+
+ this._disposers.selected = reaction(() => this.props.isSelected(), action(() => this._recording = false));
+
+ if (!this.props.dontRegisterView) {
+ this._disposers.record = reaction(() => this._recording,
+ () => {
+ if (this._recording) {
+ setTimeout(action(() => {
+ this.stopDictation(true);
+ setTimeout(() => this.recordDictation(), 500);
+ }), 500);
+ } else setTimeout(() => this.stopDictation(true), 0);
+ }
+ );
+ }
this._disposers.scrollToRegion = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
const findLinkFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
+ let offset = 0;
frag.forEach((node, index) => {
const examinedNode = findLinkNode(node, editor);
if (examinedNode?.textContent) {
nodes.push(examinedNode);
- start += index;
+ offset = index;
}
});
- return { frag: Fragment.fromArray(nodes), start: start };
+ return { frag: Fragment.fromArray(nodes), start: start + offset };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
@@ -949,7 +917,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
};
- let start = 0;
+ const start = 0;
if (this._editorView && scrollToLinkID) {
const editor = this._editorView;
const ret = findLinkFrag(editor.state.doc.content, editor);
@@ -1036,7 +1004,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}, 0);
dataDoc.title = exportState.title;
- this.rootDoc.customTitle = true;
+ this.dataDoc["title-custom"] = true;
dataDoc.unchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -1131,7 +1099,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
- const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true });
+ const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
}
@@ -1278,10 +1246,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.Hide();
if (FormattedTextBoxComment.linkDoc) {
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
}
}
@@ -1307,7 +1275,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
// jump rich text menu to this textbox
const bounds = this._ref.current?.getBoundingClientRect();
- if (bounds && this.layoutDoc._chromeStatus !== "disabled") {
+ if (bounds && this.layoutDoc._chromeStatus !== "disabled" && RichTextMenu.Instance) {
const x = Math.min(Math.max(bounds.left, 0), window.innerWidth - RichTextMenu.Instance.width);
let y = Math.min(Math.max(0, bounds.top - RichTextMenu.Instance.height - 50), window.innerHeight - RichTextMenu.Instance.height);
if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) {
@@ -1411,11 +1379,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
+ menuPlugin: any;
+
richTextMenuPlugin() {
+ const self = this;
return new Plugin({
view(newView) {
- RichTextMenu.Instance?.changeView(newView);
- return RichTextMenu.Instance;
+ self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView);
+ return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
}
@@ -1432,13 +1403,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
return wasUndoing;
}
+ public static LiveTextUndo: UndoManager.Batch | undefined;
public static HadSelection: boolean = false;
onBlur = (e: any) => {
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
- //DictationManager.Controls.stop(false);
this.endUndoTypingBatch();
this.doLinkOnDeselect();
+ FormattedTextBox.LiveTextUndo?.end();
+ FormattedTextBox.LiveTextUndo = undefined;
// move the richtextmenu offscreen
//if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
@@ -1524,8 +1497,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
TraceMobx();
const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
- setTimeout(() => this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props), this.props.isSelected() ? 10 : 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
+ const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground;
+ this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
setTimeout(() => FormattedTextBoxComment.Hide(), 0);
}
@@ -1546,13 +1519,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
width: "100%",
height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
- opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
pointerEvents: interactive ? undefined : "none",
fontSize: Cast(this.layoutDoc._fontSize, "string", null),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
- transition: "opacity 1s"
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyPress}
@@ -1616,14 +1587,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
<div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} />
</div>}
{!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation"
- onPointerDown={e => {
- runInAction(() => this._recording = !this._recording);
- setTimeout(() => this._editorView!.focus(), 500);
- e.stopPropagation();
- }} >
+ <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
<FontAwesomeIcon className="formattedTextBox-audioFont"
- style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" />
+ style={{
+ color: this._recording ? "red" : "blue",
+ transitionDelay: "0.6s",
+ opacity: this._recording ? 1 : 0.25,
+ }}
+ icon={"microphone"} size="sm" />
</div>}
</div>
</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6f3984f39..b4f648273 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -4,7 +4,7 @@ import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { DocumentManager } from "../../../util/DocumentManager";
import { schema } from "./schema_rts";
@@ -103,7 +103,7 @@ export class FormattedTextBoxComment {
this.deleteLink();
} else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) {
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ?
Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc))
@@ -111,29 +111,29 @@ export class FormattedTextBoxComment {
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
if (FormattedTextBoxComment.linkDoc.follow) {
- if (FormattedTextBoxComment.linkDoc.follow === "Default") {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
+ if (FormattedTextBoxComment.linkDoc.follow === "default") {
+ DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false);
} else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") {
- if (target) { textBox.props.addDocTab(target, "onRight"); }
+ if (target) { textBox.props.addDocTab(target, "add:right"); }
} else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") {
- if (target) { textBox.props.addDocTab(target, "inTab"); }
+ if (target) { textBox.props.addDocTab(target, "add"); }
}
} else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
+ DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false);
}
}
} else {
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
- (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
}
}
}
} else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
- textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight");
+ textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, useCors: true }), "add:right");
}
keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
@@ -232,6 +232,16 @@ export class FormattedTextBoxComment {
const mark = child ? findLinkMark(child.marks) : undefined;
const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
+ FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltipText = document.createElement("div");
+ FormattedTextBoxComment.tooltipText.style.width = "100%";
+ FormattedTextBoxComment.tooltipText.style.height = "100%";
+ FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
+ FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+
FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
(FormattedTextBoxComment.tooltipText as any).href = href;
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
@@ -241,12 +251,9 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
}
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
FormattedTextBoxComment.tooltipText.textContent = "target not found...";
(FormattedTextBoxComment.tooltipText as any).href = "";
- const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- try {
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
- } catch (e) { }
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
@@ -302,6 +309,7 @@ export class FormattedTextBoxComment {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
renderDepth={0}
@@ -323,8 +331,8 @@ export class FormattedTextBoxComment {
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%";
- FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%";
+ FormattedTextBoxComment.tooltip.style.width = "100%";
+ FormattedTextBoxComment.tooltip.style.height = "100%";
}
}
});
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 8faf752b4..c6bacc1a8 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -33,7 +33,7 @@ export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?:
return tx2;
};
-export default function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
+export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
const keys: { [key: string]: any } = {};
function bind(key: string, cmd: any) {
@@ -104,7 +104,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//Command to create a new Tab with a PDF of all the command shortcuts
bind("Mod-/", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 });
- props.addDocTab(newDoc, "onRight");
+ props.addDocTab(newDoc, "add:right");
});
//Commands to modify BlockType
@@ -143,7 +143,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const layoutKey = StrCast(originalDoc.layoutKey);
const newDoc = Doc.MakeCopy(originalDoc, true);
newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+ newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];
}
@@ -168,7 +168,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const layoutKey = StrCast(originalDoc.layoutKey);
const newDoc = Doc.MakeCopy(originalDoc, true);
newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+ newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index b683fb25d..307238ea1 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,8 +1,8 @@
import React = require("react");
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, IReactionDisposer, reaction } from "mobx";
+import { Tooltip } from "@material-ui/core";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { lift, wrapIn } from "prosemirror-commands";
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
@@ -11,31 +11,28 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Doc } from "../../../../fields/Doc";
import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from "../../../../fields/util";
import { unimplementedFunction, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { LinkManager } from "../../../util/LinkManager";
import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu from "../../AntimodeMenu";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
import { FieldViewProps } from "../FieldView";
import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
import { updateBullets } from "./ProsemirrorExampleTransfer";
import "./RichTextMenu.scss";
import { schema } from "./schema_rts";
-import { TraceMobx } from "../../../../fields/util";
-import { UndoManager, undoBatch } from "../../../util/UndoManager";
-import { Tooltip } from "@material-ui/core";
const { toggleMark } = require("prosemirror-commands");
-library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
-
@observer
-export default class RichTextMenu extends AntimodeMenu {
+export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
- private view?: EditorView;
+ public view?: EditorView;
public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
public _brushMap: Map<string, Set<Mark>> = new Map();
@@ -78,7 +75,7 @@ export default class RichTextMenu extends AntimodeMenu {
RichTextMenu.Instance = this;
this._canFade = false;
//this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
- this.Pinned = true;
+ runInAction(() => this.Pinned = true);
this.fontSizeOptions = [
{ mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
@@ -156,22 +153,8 @@ export default class RichTextMenu extends AntimodeMenu {
public delayHide = () => this._delayHide = true;
@action
- changeView(view: EditorView) {
- if ((view as any)?.TextView?.props.isSelected(true)) {
- this.view = view;
- }
- }
-
- update(view: EditorView, lastState: EditorState | undefined) {
- RichTextMenu.Instance.updateFromDash(view, lastState, this.editorProps);
- }
-
- @action
- public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
- RichTextMenu.Instance.finalUpdateFromDash(view, lastState, props);
- }
- public async finalUpdateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
- if (!view || !(view as any).TextView?.props.isSelected(true)) {
+ public updateMenu(view: EditorView, lastState: EditorState | undefined, props: any) {
+ if (!view || !(view as any).TextView?.props.isSelected(true) || !view.hasFocus()) {
return;
}
this.view = view;
@@ -199,8 +182,7 @@ export default class RichTextMenu extends AntimodeMenu {
this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
// update link in current selection
- const targetTitle = await this.getTextLinkTargetTitle();
- this.setCurrentLink(targetTitle);
+ this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle));
}
setMark = (mark: Mark, state: EditorState<any>, dispatch: any, dontToggle: boolean = false) => {
@@ -268,7 +250,9 @@ export default class RichTextMenu extends AntimodeMenu {
const pos = this.view.state.selection.$from;
const ref_node = this.reference_node(pos);
if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
- ref_node.marks.forEach(m => {
+ const marks = Array.from(ref_node.marks);
+ marks.push(...(this.view.state.storedMarks as any));
+ marks.forEach(m => {
m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
@@ -443,14 +427,20 @@ export default class RichTextMenu extends AntimodeMenu {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
this.TextView.layoutDoc._fontSize = mark.attrs.fontSize;
}
- this.setMark(view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }), view.state, view.dispatch, true);
+ const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
+ this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
+ view.focus();
+ this.updateMenu(view, undefined, this.props);
}
changeFontFamily = (mark: Mark, view: EditorView) => {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
this.TextView.layoutDoc._fontFamily = mark.attrs.family;
}
- this.setMark(view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }), view.state, view.dispatch, true);
+ const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family });
+ this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
+ view.focus();
+ this.updateMenu(view, undefined, this.props);
}
// TODO: remove doesn't work
@@ -486,6 +476,8 @@ export default class RichTextMenu extends AntimodeMenu {
this.view.dispatch(tx3);
}
}
+ this.view.focus();
+ this.updateMenu(this.view, undefined, this.props);
}
insertSummarizer(state: EditorState<any>, dispatch: any) {
@@ -690,16 +682,22 @@ export default class RichTextMenu extends AntimodeMenu {
e.preventDefault();
e.stopPropagation();
self.TextView.endUndoTypingBatch();
- UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
- self.TextView.EditorView!.focus();
+ if (self.view) {
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.view.focus();
+ self.updateMenu(self.view, undefined, self.props);
+ }
}
function changeColor(e: React.PointerEvent, color: string) {
e.preventDefault();
e.stopPropagation();
self.setActiveColor(color);
self.TextView.endUndoTypingBatch();
- UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
- self.TextView.EditorView!.focus();
+ if (self.view) {
+ UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
+ self.view.focus();
+ self.updateMenu(self.view, undefined, self.props);
+ }
}
// onPointerDown={onColorClick}
@@ -812,7 +810,7 @@ export default class RichTextMenu extends AntimodeMenu {
<div className="dropdown link-menu">
<p>Linked to:</p>
<input value={link} placeholder="Enter URL" onChange={onLinkChange} />
- <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "onRight")}>Apply hyperlink</button>
+ <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
<div className="divider"></div>
<button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
</div>;
@@ -859,7 +857,7 @@ export default class RichTextMenu extends AntimodeMenu {
// TODO: should check for valid URL
@undoBatch
makeLinkToURL = (target: string, lcoation: string) => {
- ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target);
+ ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target);
}
@undoBatch
@@ -988,7 +986,7 @@ export default class RichTextMenu extends AntimodeMenu {
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => this.activeFontSize = val)),
this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => this.activeFontFamily = val)),
<div className="richTextMenu-divider" key="divider 4" />,
- this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", action((val: string) => this.activeListType = val)),
+ this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule),
@@ -1020,6 +1018,7 @@ interface ButtonDropdownProps {
dropdownContent: JSX.Element;
openDropdownOnButton?: boolean;
link?: boolean;
+ pdf?: boolean;
}
@observer
@@ -1059,15 +1058,33 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
}, 0);
}
+
render() {
return (
<div className="button-dropdown-wrapper" ref={node => this.ref = node}>
- <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
- {this.props.button}
- <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
- </div>
+ {!this.props.pdf ?
+ <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
+ {this.props.button}
+ <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
+ </div>
+ :
+ <>
+ {this.props.button}
+ <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
+ <FontAwesomeIcon icon="caret-down" size="sm" />
+ </button>
+ </>}
{this.showDropdown ? this.props.dropdownContent : (null)}
</div>
);
}
+}
+
+
+interface RichTextMenuPluginProps {
+ editorProps: any;
+}
+export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
+ render() { return null; }
+ update(view: EditorView, lastState: EditorState | undefined) { RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps); }
} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index dc1d8a2c8..5c0505909 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -3,13 +3,13 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
import { DataSym, Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
-import RichTextMenu from "./RichTextMenu";
+import { RichTextMenu } from "./RichTextMenu";
import { schema } from "./schema_rts";
import { List } from "../../../../fields/List";
@@ -92,7 +92,7 @@ export class RichTextRules {
const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
textDocInline.title = inlineFieldKey; // give the annotation its own title
- textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+ textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
@@ -279,7 +279,7 @@ export class RichTextRules {
DocUtils.Publish(target, docid, returnFalse, returnFalse);
DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
});
- const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
+ const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "add:right", title: docid, targetId: docid });
return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
return state.tr;
@@ -321,7 +321,11 @@ export class RichTextRules {
(state, match, start, end) => {
const tag = match[1];
if (!tag) return state.tr;
- this.Document[DataSym]["#" + tag] = ".";
+ this.Document[DataSym]["#" + tag] = "#" + tag;
+ const tags = StrCast(this.Document.tags, ":");
+ if (!tags.includes(`#${tag}:`)) {
+ this.Document[DataSym].tags = `"${tags + "#" + tag + ':'}"`;
+ }
const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
return state.tr.deleteRange(start, end).insert(start, fieldView);
}),
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 33a080fe4..f0bacb735 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
export class DashDocView {
@@ -43,7 +44,7 @@ export class DashDocView {
this._outer = document.createElement("span");
this._outer.style.position = "relative";
this._outer.style.textIndent = "0";
- this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
this._outer.style.width = node.attrs.width;
this._outer.style.height = node.attrs.height;
this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +127,7 @@ export class DashDocView {
this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
- this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
@@ -155,6 +156,7 @@ export class DashDocView {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this._textBox.props.docFilters}
+ searchFilterDocs={this._textBox.props.searchFilterDocs}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 1616500f6..64f7d27e5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = {
alt: { default: null },
title: { default: null },
float: { default: "left" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
docid: { default: "" }
},
group: "inline",
@@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = {
height: { default: 100 },
title: { default: null },
float: { default: "right" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
hidden: { default: false },
fieldKey: { default: "" },
docid: { default: "" },
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index d29b638e6..222a6cb0f 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -4,9 +4,9 @@ import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../fields/Types";
import { DocumentManager } from "../../util/DocumentManager";
-import PDFMenu from "./PDFMenu";
+import { PDFMenu } from "./PDFMenu";
import "./Annotation.scss";
interface IAnnotationProps {
@@ -19,7 +19,7 @@ interface IAnnotationProps {
}
@observer
-export default
+export
class Annotation extends React.Component<IAnnotationProps> {
render() {
return DocListCast(this.props.anno.annotations).map(a => (
@@ -86,7 +86,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
}
@action
- onPointerDown = async (e: React.PointerEvent) => {
+ onPointerDown = (e: React.PointerEvent) => {
if (e.button === 2 || e.ctrlKey) {
PDFMenu.Instance.Status = "annotation";
PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
@@ -97,11 +97,11 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
e.stopPropagation();
}
else if (e.button === 0) {
- const annoGroup = await Cast(this.props.document.group, Doc);
- if (annoGroup) {
- DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined);
- e.stopPropagation();
- }
+ e.persist();
+ e.stopPropagation();
+ PromiseValue(this.props.document.group).then(annoGroup => annoGroup instanceof Doc &&
+ DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined)
+ );
}
}
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index 7bea8d01b..32dd376ac 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -1,17 +1,17 @@
import React = require("react");
-import "./PDFMenu.scss";
-import { observable, action, computed, } from "mobx";
-import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { unimplementedFunction, returnFalse, Utils } from "../../../Utils";
-import AntimodeMenu from "../AntimodeMenu";
-import { Doc, Opt } from "../../../fields/Doc";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
import { ColorState } from "react-color";
+import { Doc, Opt } from "../../../fields/Doc";
+import { returnFalse, unimplementedFunction, Utils } from "../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu";
+import "./PDFMenu.scss";
@observer
-export default class PDFMenu extends AntimodeMenu {
+export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: PDFMenu;
private _commentCont = React.createRef<HTMLButtonElement>();
@@ -112,7 +112,7 @@ export default class PDFMenu extends AntimodeMenu {
</div>
</div>;
return (
- <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} />
+ <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
);
}
@@ -154,7 +154,7 @@ export default class PDFMenu extends AntimodeMenu {
const buttons = this.Status === "pdf" ?
[
this.highlighter,
- <button key="2" className="antimodeMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
+ <button key="2" className="antimodeMenu-button annotate" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: "grab" }}>
<FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
] : [
<button key="5" className="antimodeMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f9ae78778..18be9b679 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,42 +1,43 @@
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
-import { createSchema, makeInterface, listSpec } from "../../../fields/Schema";
-import { ScriptField, ComputedField } from "../../../fields/ScriptField";
+import { createSchema, makeInterface } from "../../../fields/Schema";
+import { ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import { PdfField } from "../../../fields/URLField";
-import { TraceMobx, GetEffectiveAcl } from "../../../fields/util";
+import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
+import { Networking } from "../../Network";
import { DragManager } from "../../util/DragManager";
import { CompiledScript, CompileScript } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
+import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CollectionView } from "../collections/CollectionView";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
-import Annotation from "./Annotation";
-import PDFMenu from "./PDFMenu";
+import { Annotation } from "./Annotation";
+import { PDFMenu } from "./PDFMenu";
import "./PDFViewer.scss";
+const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import React = require("react");
-import { SnappingManager } from "../../util/SnappingManager";
+import { LinkDocPreview } from "../nodes/LinkDocPreview";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
-import { Networking } from "../../Network";
export const pageSchema = createSchema({
- curPage: "number",
+ _curPage: "number",
rotation: "number",
scrollHeight: "number",
serachMatch: "boolean"
@@ -56,6 +57,7 @@ interface IViewerProps {
Document: Doc;
DataDoc?: Doc;
docFilters: () => string[];
+ searchFilterDocs: () => Doc[];
ContainingCollectionView: Opt<CollectionView>;
PanelWidth: () => number;
PanelHeight: () => number;
@@ -97,16 +99,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@observable private _zoomed = 1;
private _pdfViewer: any;
+ private _styleRule: any; // stylesheet rule for making hyperlinks clickable
private _retries = 0; // number of times tried to create the PDF viewer
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- private _reactionDisposer?: IReactionDisposer;
- private _selectionReactionDisposer?: IReactionDisposer;
- private _annotationReactionDisposer?: IReactionDisposer;
- private _scrollTopReactionDisposer?: IReactionDisposer;
- private _filterReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer?: IReactionDisposer;
- private _searchReactionDisposer2?: IReactionDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _selectionText: string = "";
@@ -134,7 +131,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
const coreFilename = pathComponents.pop()!.split(".")[0];
const params: any = {
coreFilename,
- pageNum: this.Document.curPage || 1,
+ pageNum: this.Document._curPage || 1,
};
if (pathComponents.length) {
params.subtree = `${pathComponents.join("/")}/`;
@@ -143,20 +140,20 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
} else {
const params: any = {
coreFilename: relative.split("/")[relative.split("/").length - 1],
- pageNum: this.Document.curPage || 1,
+ pageNum: this.Document._curPage || 1,
};
this._coverPath = "http://cs.brown.edu/~bcz/face.gif";//href.startsWith(window.location.origin) ? await Networking.PostToServer("/thumbnail", params) : { width: 100, height: 100, path: "" };
}
runInAction(() => this._showWaiting = this._showCover = true);
this.props.startupLive && this.setupPdfJsViewer();
this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0);
- this._searchReactionDisposer = reaction(() => this.Document.searchMatch,
+ this._disposers.searchMatch = reaction(() => Doc.IsSearchMatch(this.rootDoc),
m => {
- if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true);
+ if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0);
else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200);
}, { fireImmediately: true });
- this._selectionReactionDisposer = reaction(() => this.props.isSelected(),
+ this._disposers.selected = reaction(() => this.props.isSelected(),
selected => {
if (!selected) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
@@ -166,26 +163,26 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
(SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer();
},
{ fireImmediately: true });
- this._reactionDisposer = reaction(
+ this._disposers.scrollY = reaction(
() => this.Document._scrollY,
(scrollY) => {
if (scrollY !== undefined) {
(this._showCover || this._showWaiting) && this.setupPdfJsViewer();
- this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0));
+ (!LinkDocPreview.TargetDoc) && this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0));
setTimeout(() => this.Document._scrollY = undefined, 1000);
}
},
{ fireImmediately: true }
);
+ this._disposers.curPage = reaction(
+ () => this.Document._curPage,
+ (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
+ { fireImmediately: true }
+ );
}
componentWillUnmount = () => {
- this._reactionDisposer?.();
- this._scrollTopReactionDisposer?.();
- this._annotationReactionDisposer?.();
- this._filterReactionDisposer?.();
- this._selectionReactionDisposer?.();
- this._searchReactionDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
document.removeEventListener("copy", this.copy);
}
@@ -226,11 +223,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this.props.setPdfViewer(this);
await this.initialLoad();
- this._scrollTopReactionDisposer = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
+ this._disposers.scrollTop = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
(stop) => (stop !== undefined && this.layoutDoc._scrollY === undefined && this._mainCont.current) && (this._mainCont.current.scrollTop = stop),
{ fireImmediately: true });
- this._filterReactionDisposer = reaction(
+ this._disposers.filterScript = reaction(
() => Cast(this.Document.filterScript, ScriptField),
action(scriptField => {
const oldScript = this._script.originalScript;
@@ -245,8 +242,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
pagesinit = action(() => {
- this._pdfViewer.currentScaleValue = this._zoomed = 1;
- this.gotoPage(this.Document.curPage || 1);
+ if (this._pdfViewer._setDocumentViewerElement.offsetParent) {
+ this._pdfViewer.currentScaleValue = this._zoomed = 1;
+ this.gotoPage(this.Document._curPage || 1);
+ }
document.removeEventListener("pagesinit", this.pagesinit);
});
@@ -313,7 +312,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
annoDocs.push(annoDoc);
anno.remove();
(annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY));
- (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc.width), maxX));
+ (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc._width), maxX));
}));
mainAnnoDocProto.y = Math.max(minY, 0);
@@ -342,7 +341,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
gotoPage = (p: number) => {
- this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+ if (this._pdfViewer?._setDocumentViewerElement?.offsetParent) {
+ this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
+ }
}
@action
@@ -363,7 +364,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
onScroll = (e: React.UIEvent<HTMLElement>) => {
this.Document._scrollY === undefined && (this.layoutDoc._scrollTop = this._mainCont.current!.scrollTop);
- this._pdfViewer && (this.Document.curPage = this._pdfViewer.currentPageNumber);
+ this._pdfViewer && (this.Document._curPage = this._pdfViewer.currentPageNumber);
}
// get the page index that the vertical offset passed in is on
@@ -436,11 +437,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
// if alt+left click, drag and annotate
this._downX = e.clientX;
this._downY = e.clientY;
- addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
+ (e.target as any).tagName === "SPAN" && (this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" }));
if ((this.Document._viewScale || 1) !== 1) return;
if ((e.button !== 0 || e.altKey) && this.active(true)) {
this._setPreviewCursor?.(e.clientX, e.clientY, true);
- //e.stopPropagation();
}
this._marqueeing = false;
if (!e.altKey && e.button === 0 && this.active(true)) {
@@ -462,12 +462,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._marqueeHeight = this._marqueeWidth = 0;
this._marqueeing = true;
}
- document.removeEventListener("pointermove", this.onSelectMove);
document.addEventListener("pointermove", this.onSelectMove);
- document.removeEventListener("pointerup", this.onSelectEnd);
document.addEventListener("pointerup", this.onSelectEnd);
+ document.addEventListener("pointerup", this.removeStyle, true);
}
}
+ removeStyle = () => {
+ clearStyleSheetRules(PDFViewer._annotationStyle);
+ document.removeEventListener("pointerup", this.removeStyle);
+ }
@action
onSelectMove = (e: PointerEvent): void => {
@@ -602,15 +605,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
// Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy]();
// const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() });
// Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped");
- const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
+ const annotationDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
dragComplete: e => {
- if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
- const link = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
- annotationDoc.isLinkButton = true;
- if (link) link.followLinkLocation = "onRight";
+ if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) {
+ e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
+ annotationDoc.isLinkButton = true; // prevents link button fro showing up --- maybe not a good thing?
}
+ e.annoDragData && e.annoDragData.linkDocument && e.annoDragData?.linkDropCallback?.({ linkDocument: e.annoDragData.linkDocument });
}
});
}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index a25a8ee33..c33637d0f 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -20,6 +20,7 @@ import { DocumentType } from "../../documents/DocumentTypes";
import { Tooltip } from "@material-ui/core";
import { DragManager } from "../../util/DragManager";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { undoBatch } from "../../util/UndoManager";
export const presSchema = createSchema({
presentationTargetDoc: Doc,
@@ -102,6 +103,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
bringToFront={returnFalse}
opacity={returnOne}
docFilters={this.props.docFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
ContentScaling={returnOne}
@@ -205,6 +207,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
}
}
+ @undoBatch
+ removeItem = action((e: React.MouseEvent) => {
+ this.props.removeDocument?.(this.rootDoc);
+ if (PresBox.Instance._selectedArray.includes(this.rootDoc)) {
+ PresBox.Instance._selectedArray.splice(PresBox.Instance._selectedArray.indexOf(this.rootDoc), 1);
+ }
+ e.stopPropagation();
+ });
+
render() {
const className = "presElementBox-item" + (PresBox.Instance._selectedArray.includes(this.rootDoc) ? " presElementBox-active" : "");
const pbi = "presElementBox-interaction";
@@ -253,10 +264,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<Tooltip title={<><div className="dash-tooltip">{"Presentation pin view"}</div></>}><div className="presElementBox-time" style={{ fontWeight: 700, display: this.rootDoc.presPinView && PresBox.Instance.toolbarWidth > 300 ? "block" : "none" }}>V</div></Tooltip>
<Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
className="presElementBox-closeIcon"
- onClick={e => {
- this.props.removeDocument?.(this.rootDoc);
- e.stopPropagation();
- }}>
+ onClick={this.removeItem}>
<FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
</div></Tooltip>
<Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"presElementBox-expand" + (this.rootDoc.presExpandInlineButton ? "-selected" : "")} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx
index 9b7cf2fc6..f1dd106a7 100644
--- a/src/client/views/search/IconBar.tsx
+++ b/src/client/views/search/IconBar.tsx
@@ -1,28 +1,11 @@
-import * as React from 'react';
+import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import * as React from 'react';
+import { DocumentType } from "../../documents/DocumentTypes";
// import "./SearchBox.scss";
import "./IconBar.scss";
-import "./IconButton.scss";
-import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faTimesCircle, faCheckCircle } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { library } from '@fortawesome/fontawesome-svg-core';
-import * as _ from "lodash";
import { IconButton } from './IconButton';
-import { DocumentType } from "../../documents/DocumentTypes";
-
-
-library.add(faSearch);
-library.add(faObjectGroup);
-library.add(faImage);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm);
-library.add(faMusic);
-library.add(faLink);
-library.add(faChartBar);
-library.add(faGlobeAsia);
-library.add(faBan);
+import "./IconButton.scss";
export interface IconBarProps {
setIcons: (icons: string[]) => void;
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index 52641c543..349690b20 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -1,30 +1,14 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { observable, action, runInAction, IReactionDisposer, reaction } from 'mobx';
-import "./SearchBox.scss";
-import "./IconButton.scss";
-import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faVideo, faCaretDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { library, icon } from '@fortawesome/fontawesome-svg-core';
+import * as _ from "lodash";
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
import { DocumentType } from "../../documents/DocumentTypes";
import '../globalCssVariables.scss';
-import * as _ from "lodash";
import { IconBar } from './IconBar';
-import { props } from 'bluebird';
-import { Search } from '../../../server/Search';
-import { gravity } from 'sharp';
-
-library.add(faSearch);
-library.add(faObjectGroup);
-library.add(faImage);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm);
-library.add(faMusic);
-library.add(faLink);
-library.add(faChartBar);
-library.add(faGlobeAsia);
-library.add(faBan);
+import "./IconButton.scss";
+import "./SearchBox.scss";
+import { Font } from '@react-pdf/renderer';
interface IconButtonProps {
type: string;
@@ -47,59 +31,46 @@ export class IconButton extends React.Component<IconButtonProps>{
componentDidMount = () => {
this._resetReaction = reaction(
() => IconBar.Instance._resetClicked,
- () => {
+ action(() => {
if (IconBar.Instance._resetClicked) {
- runInAction(() => {
- this.reset();
- IconBar.Instance._reset++;
- if (IconBar.Instance._reset === 9) {
- IconBar.Instance._reset = 0;
- IconBar.Instance._resetClicked = false;
- }
- });
+ this._isSelected = false;
+ IconBar.Instance._reset++;
+ if (IconBar.Instance._reset === 9) {
+ IconBar.Instance._reset = 0;
+ IconBar.Instance._resetClicked = false;
+ }
}
- },
+ }),
);
+
this._selectAllReaction = reaction(
() => IconBar.Instance._selectAllClicked,
- () => {
+ action(() => {
if (IconBar.Instance._selectAllClicked) {
- runInAction(() => {
- this.select();
- IconBar.Instance._select++;
- if (IconBar.Instance._select === 9) {
- IconBar.Instance._select = 0;
- IconBar.Instance._selectAllClicked = false;
- }
- });
+ this._isSelected = true;
+ IconBar.Instance._select++;
+ if (IconBar.Instance._select === 9) {
+ IconBar.Instance._select = 0;
+ IconBar.Instance._selectAllClicked = false;
+ }
}
- },
+ }),
);
}
@action.bound
getIcon() {
switch (this.props.type) {
- case (DocumentType.NONE):
- return faBan;
- case (DocumentType.AUDIO):
- return faMusic;
- case (DocumentType.COL):
- return faObjectGroup;
- case (DocumentType.IMG):
- return faImage;
- case (DocumentType.LINK):
- return faLink;
- case (DocumentType.PDF):
- return faFilePdf;
- case (DocumentType.RTF):
- return faStickyNote;
- case (DocumentType.VID):
- return faVideo;
- case (DocumentType.WEB):
- return faGlobeAsia;
- default:
- return faCaretDown;
+ case (DocumentType.NONE): return "ban";
+ case (DocumentType.AUDIO): return "music";
+ case (DocumentType.COL): return "object-group";
+ case (DocumentType.IMG): return "image";
+ case (DocumentType.LINK): return "link";
+ case (DocumentType.PDF): return "file-pdf";
+ case (DocumentType.RTF): return "sticky-note";
+ case (DocumentType.VID): return "video";
+ case (DocumentType.WEB): return "globe-asia";
+ default: return "caret-down";
}
}
@@ -136,53 +107,16 @@ export class IconButton extends React.Component<IconButtonProps>{
//backgroundColor: "rgb(178, 206, 248)" //$darker-alt-accent
};
- @action.bound
- public reset() { this._isSelected = false; }
-
- @action.bound
- public select() { this._isSelected = true; }
-
- @action
- onMouseLeave = () => { this._hover = false; }
-
- @action
- onMouseEnter = () => { this._hover = true; }
-
- getFA = () => {
- switch (this.props.type) {
- case (DocumentType.NONE):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faBan} />);
- case (DocumentType.AUDIO):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faMusic} />);
- case (DocumentType.COL):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faObjectGroup} />);
- case (DocumentType.IMG):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faImage} />);
- case (DocumentType.LINK):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faLink} />);
- case (DocumentType.PDF):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faFilePdf} />);
- case (DocumentType.RTF):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faStickyNote} />);
- case (DocumentType.VID):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faVideo} />);
- case (DocumentType.WEB):
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faGlobeAsia} />);
- default:
- return (<FontAwesomeIcon className="fontawesome-icon" icon={faCaretDown} />);
- }
- }
-
render() {
return (
<div className="type-outer" id={this.props.type + "-filter"}
- onMouseEnter={this.onMouseEnter}
- onMouseLeave={this.onMouseLeave}
+ onMouseEnter={() => this._hover = true}
+ onMouseLeave={() => this._hover = false}
onClick={this.onClick}>
<div className="type-icon" id={this.props.type + "-icon"}
style={this._hover ? this.hoverStyle : this._isSelected ? this.selected : this.notSelected}
>
- {this.getFA()}
+ <FontAwesomeIcon className="fontawesome-icon" icon={this.getIcon()} />
</div>
{/* <div className="filter-description">{this.props.type}</div> */}
</div>
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 3f06ba7d3..13d4a6d5a 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -6,71 +6,132 @@
flex-direction: column;
width: 100%;
height: 100%;
- position: absolute;
+ position: relative;
font-size: 10px;
line-height: 1;
overflow-y: auto;
overflow-x: visible;
- background: lightgrey,
-}
+ background: lightgrey;
+ overflow: visible;
+ z-index: 10000;
-.searchBox-bar {
- height: 32px;
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: black;
- .searchBox-barChild {
+ .searchBox-bar {
+ height: $searchpanel-height;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: black;
- &.searchBox-collection {
- flex: 0 1 auto;
- margin-left: 2px;
- margin-right: 2px
- }
+ .searchBox-lozenges {
+ position: absolute;
+ left: 15;
+ display: flex;
- &.searchBox-input {
- margin:5px;
- border-radius:20px;
- border:black;
- display: block;
- width: 130px;
- -webkit-transition: width 0.4s;
- transition: width 0.4s;
- align-self: stretch;
- outline:none;
+ .searchBox-lozenge-user,
+ .searchBox-lozenge-dashboard,
+ .searchBox-lozenge {
+ background-color: #313131;
+ border-radius: 5px;
+ height: 18px;
+ padding: 4px;
+ box-shadow: lightgrey 0.15em 0.15em 0.1em;
+ margin: 2px;
+ margin-bottom: 4px;
+ border-top: dimgrey 1px solid;
+ border-left: dimgrey 1px solid;
+ display: flex;
+ .searchBox-logoff,
+ .searchBox-dashboards {
+ border-radius: 3px;
+ background: olivedrab;
+ color: white;
+ position: relative;
+ display: none;
+ margin-left: 3px;
+ padding-left: 2px;
+ padding-right: 2px;
+ padding-bottom: 11px;
+ cursor: default;
+ }
+ .searchBox-logoff {
+ background: red;
+ }
+
+ .searchBox-dashSelect{
+ background-color: black;
+ color: white;
+ font-size: 9;
+ margin-right: 6;
+ border-radius: 5px;
+ position: relative;
+ height: 15px;
+ transform: translate(0,-3px);
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+ .searchBox-lozenge-user:hover {
+ .searchBox-logoff {
+ display:inline-block;
+ }
+ }
+ .searchBox-lozenge-dashboard:hover {
+ .searchBox-dashboards {
+ display:inline-block;
+ }
+ }
}
-
- .searchBox-input:focus {
- width: 500px;
- outline:none;
+ .searchBox-query {
+ position: relative;
+ display: flex;
+ width: 450;
}
+ .searchBox-barChild {
- &.searchBox-filter {
- align-self: stretch;
- button{
- transform:none;
+ &.searchBox-collection {
+ flex: 0 1 auto;
+ margin-left: 2px;
+ margin-right: 2px
}
- button:hover{
- transform:none;
+
+ &.searchBox-input {
+ margin:5px;
+ border-radius:20px;
+ border:black;
+ display: block;
+ width: 130px;
+ -webkit-transition: width 0.4s;
+ transition: width 0.4s;
+ align-self: stretch;
+ outline:none;
+ &:focus {
+ width: 500px;
+ outline:none;
+ }
+ }
+ &.searchBox-filter {
+ align-self: stretch;
+ button{
+ transform:none;
+ &:hover {
+ transform: none;
+ }
+ }
}
- }
- &.searchBox-submit {
- margin-left: 2px;
- margin-right: 2px
- }
+ &.searchBox-submit {
+ margin-left: 2px;
+ margin-right: 2px
+ }
- &.searchBox-close {
- color: $light-color;
- max-height: 32px;
+ &.searchBox-close {
+ color: $light-color;
+ max-height: $searchpanel-height;
+ }
}
- }
-}
-
-.searchBox-quickFilter {
- width: 100%;
- height: 40px;
- margin-top: 10px;
+ }
}
.searchBox-results {
@@ -91,241 +152,4 @@
text-align: left;
font-weight: bold;
}
-}
-
-.filter-form {
- position: relative;
- background: #121721;
- flex-direction: column;
- transform-origin: top;
- transition: height 0.3s ease, display 0.6s ease, overflow 0.6s ease;
- height:0px;
- overflow:hidden;
-
-
- .filter-header {
- //display: flex;
- position: relative;
- //flex-wrap:wrap;
- right: 1px;
- color: grey;
- //flex-direction: row-reverse;
- transform-origin: top;
- //justify-content: space-evenly;
- margin-bottom: 5px;
- overflow:hidden;
- transition:height 0.3s ease-out;
-
-
-
- .filter-item {
- position: relative;
- border:1px solid grey;
- border-radius: 16px;
-
- }
- }
-
- .filter-body {
- position: relative;
- right: 1px;
- color: grey;
- transform-origin: top;
- border-top: 0px;
-
- overflow:hidden;
- transition:height 0.3s ease-out;
- height:0px;
-
- }
- .filter-key {
- position: relative;
- right: 1px;
- color: grey;
- transform-origin: top;
- border-top: 0px;
- overflow:hidden;
- transition:height 0.3s ease-out;
- height:0px;
-
- // .filter-keybar {
- // display: flex;
- // flex-wrap: wrap;
- // justify-content: space-evenly;
- // height: auto;
- // width: 100%;
- // flex-direction: row-reverse;
- // margin-top:5px;
-
- // .filter-item {
- // position: relative;
- // border:1px solid grey;
- // border-radius: 16px;
- // }
- // }
- }
-}
-
-// .top-filter-header {
-
-// #header {
-// text-transform: uppercase;
-// letter-spacing: 2px;
-// font-size: 13;
-// width: 80%;
-// }
-
-// .close-icon {
-// width: 20%;
-// opacity: .6;
-// position: relative;
-// display: block;
-
-// .line {
-// display: block;
-// background: $alt-accent;
-// width: 20;
-// height: 3;
-// position: absolute;
-// right: 0;
-// border-radius: ($height-line / 2);
-
-// &.line-1 {
-// transform: rotate(45deg);
-// top: 45%;
-// }
-
-// &.line-2 {
-// transform: rotate(-45deg);
-// top: 45%;
-// }
-// }
-// }
-
-// .close-icon:hover {
-// opacity: 1;
-// }
-
-// }
-
-// .filter-options {
-
-// .filter-div {
-// margin-top: 10px;
-// margin-bottom: 10px;
-// display: inline-block;
-// width: 100%;
-// border-color: rgba(178, 206, 248, .2); // $darker-alt-accent
-// border-top-style: solid;
-
-// .filter-header {
-// display: flex;
-// align-items: center;
-// margin-bottom: 10px;
-// letter-spacing: 2px;
-
-// .filter-title {
-// font-size: 13;
-// text-transform: uppercase;
-// margin-top: 10px;
-// margin-bottom: 10px;
-// -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;
-// }
-// }
-
-// .filter-header:hover .filter-title {
-// transform: scale(1.05);
-// }
-
-// .filter-panel {
-// max-height: 0px;
-// width: 100%;
-// overflow: hidden;
-// opacity: 0;
-// transform-origin: top;
-// -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;
-// text-align: center;
-// }
-// }
-// }
-
-// .filter-buttons {
-// border-color: rgba(178, 206, 248, .2); // $darker-alt-accent
-// 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;
-
- .save-filter,
- .reset-filter,
- .all-filter {
- background-color: gray;
- }
-
- .save-filter:hover,
- .reset-filter:hover,
- .all-filter:hover {
- background-color: $darker-alt-accent;
- }
- }
} \ No newline at end of file
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 770a03cb1..b37ae02c3 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,183 +1,99 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import * as rp from 'request-promise';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { returnFalse, Utils } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { SetupDrag } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
-import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
-import { CollectionView, CollectionViewType } from '../collections/CollectionView';
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
+import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
-import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
+import { undoBatch } from "../../util/UndoManager";
-export const searchSchema = createSchema({
- id: "string",
- Document: Doc,
- searchQuery: "string",
-});
-
-export enum Keys {
- TITLE = "title",
- AUTHOR = "author",
- DATA = "data",
- TEXT = "text"
-}
+export const searchSchema = createSchema({ Document: Doc });
type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
-//React.Component<SearchProps>
@observer
export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+ public static Instance: SearchBox;
- get _searchString() { return this.layoutDoc.searchQuery; }
- @computed set _searchString(value) { this.layoutDoc.searchQuery = (value); }
- @observable private _resultsOpen: boolean = false;
- @observable _searchbarOpen: boolean = false;
- @observable private _results: [Doc, string[], string[]][] = [];
- @observable private _openNoResults: boolean = false;
- @observable private _visibleElements: JSX.Element[] = [];
- @observable private _visibleDocuments: Doc[] = [];
-
- static NUM_SEARCH_RESULTS_PER_PAGE = 25;
-
- private _resultsSet = new Map<Doc, number>();
- private _resultsRef = React.createRef<HTMLDivElement>();
- public inputRef = React.createRef<HTMLInputElement>();
-
- private _isSearch: ("search" | "placeholder" | undefined)[] = [];
- private _isSorted: ("sorted" | "placeholder" | undefined)[] = [];
-
+ private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
+ private _numResultsPerPage = 500;
private _numTotalResults = -1;
private _endIndex = -1;
-
- static Instance: SearchBox;
-
+ private _lockPromise?: Promise<void>;
+ private _resultsSet = new Map<Doc, number>();
+ private _inputRef = React.createRef<HTMLInputElement>();
private _maxSearchIndex: number = 0;
private _curRequest?: Promise<any> = undefined;
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+ private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
- private new_buckets: { [characterName: string]: number } = {};
- //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 = false;
- @observable private _nodeStatus: boolean = false;
- @observable private _keyStatus: boolean = false;
+ private docsforfilter: Doc[] | undefined = [];
+ private realTotalResults: number = 0;
+ private newsearchstring = "";
+ private collectionRef = React.createRef<HTMLDivElement>();
- @observable private newAssign: boolean = true;
+ @observable _icons: string[] = this._allIcons;
+ @observable _results: [Doc, string[], string[]][] = [];
+ @observable _visibleElements: JSX.Element[] = [];
+ @observable _visibleDocuments: Doc[] = [];
+ @observable _deletedDocsStatus: boolean = false;
+ @observable _onlyAliases: boolean = true;
+ @observable _searchbarOpen = false;
+ @observable _searchFullDB = "DB";
+ @observable _noResults = "";
+ @observable _pageStart = 0;
+ @observable open = false;
+ @observable children = 0;
+ @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
constructor(props: any) {
super(props);
SearchBox.Instance = this;
- this.resultsScrolled = this.resultsScrolled.bind(this);
-
}
- @observable setupButtons = false;
- private _disposers: { [name: string]: IReactionDisposer } = {};
-
- componentDidMount = () => {
- this._disposers.filters = reaction(() => Cast(this.props.Document._docFilters, listSpec("string")), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
- newFilters => {
- if (this.searchFullDB) {
- runInAction(() => this._pageStart = 0);
- this.submitSearch();
- // newFilters?.forEach(f => {
- // console.log(f);
- // })
- }
- });
- if (this.setupButtons === false) {
- runInAction(() => this.setupButtons = true);
- }
- if (this.inputRef.current) {
- this.inputRef.current.focus();
- runInAction(() => { this._searchbarOpen = true; });
- }
- if (this.rootDoc.searchQuery && this.newAssign) {
- const sq = this.rootDoc.searchQuery;
- runInAction(() => {
-
- // this._deletedDocsStatus=this.props.filterQuery!.deletedDocsStatus;
- // this._authorFieldStatus=this.props.filterQuery!.authorFieldStatus
- // this._titleFieldStatus=this.props.filterQuery!.titleFieldStatus;
- // this._basicWordStatus=this.props.filterQuery!.basicWordStatus;
- // this._icons=this.props.filterQuery!.icons;
- this.newAssign = false;
- });
- runInAction(() => {
- this.layoutDoc._searchString = StrCast(sq);
- this.submitSearch();
- });
+ componentDidMount = action(() => {
+ if (this._inputRef.current) {
+ this._inputRef.current.focus();
}
- }
+ this._disposers.filters = reaction(() => this.props.Document._docFilters,
+ (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter));
+ });
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
}
+ @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
- @action
- getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc)
-
-
- @observable newsearchstring: string = "";
- @action.bound
- onChange(e: React.ChangeEvent<HTMLInputElement>) {
- this.layoutDoc._searchString = e.target.value;
+ onChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
this.newsearchstring = e.target.value;
if (e.target.value === "") {
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
+ console.log("Reset start");
+ this.docsforfilter = undefined;
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
+ this.resetSearch(false);
- if (this.currentSelectedCollection !== undefined) {
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection = undefined;
- this.props.Document.selectedDoc = undefined;
-
- }
- runInAction(() => { this.open = false; });
- this._openNoResults = false;
+ this.open = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -186,61 +102,16 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._curRequest = undefined;
this._maxSearchIndex = 0;
}
- }
+ });
- enter = (e: React.KeyboardEvent) => {
- if (e.key === "Enter") {
+ enter = action((e: React.KeyboardEvent | undefined) => {
+ if (!e || e.key === "Enter") {
this.layoutDoc._searchString = this.newsearchstring;
- runInAction(() => this._pageStart = 0);
-
- if (StrCast(this.layoutDoc._searchString) !== "" || !this.searchFullDB) {
- runInAction(() => this.open = true);
- }
- else {
- runInAction(() => this.open = false);
-
- }
+ this._pageStart = 0;
+ this.open = StrCast(this.layoutDoc._searchString) !== "" || this._searchFullDB !== "DB";
this.submitSearch();
}
- }
-
- @observable open: boolean = false;
-
-
- public static async convertDataUri(imageUri: string, returnedFilename: string) {
- try {
- const posting = Utils.prepend("/uploadURI");
- const returnedUri = await rp.post(posting, {
- body: {
- uri: imageUri,
- name: returnedFilename
- },
- json: true,
- });
- return returnedUri;
-
- } catch (e) {
- console.log("SearchBox:" + e);
- }
- }
-
- public _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.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 _filterOpen: boolean = false;
- //if icons = all icons, then no icon filter is applied
- // get _icons() { return this.props.searchFileTypes; }
- // set _icons(value) {
- // this.props.setSearchFileTypes(value);
- // }
- @observable _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;
- //this also serves as an indicator if the collection status filter is applied
- @observable public _deletedDocsStatus: boolean = false;
- @observable private _collectionStatus = false;
-
+ });
getFinalQuery(query: string): string {
//alters the query so it looks in the correct fields
@@ -248,8 +119,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
//TODO: data
const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
- const type: string[] = [];
-
const filters: string[] = [];
for (let i = 0; i < initialfilters.length; i = i + 3) {
@@ -277,9 +146,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[0].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
query = `(${query}) AND (${newWords.join(" ")})`;
}
else {
@@ -287,194 +154,74 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[i].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
const v = "(" + newWords.join(" ") + ")";
if (i === 0) {
- query = `(${query}) AND (${v}`;
- if (values.length === 1) {
- query = query + ")";
- }
- }
- else if (i === values.length - 1) {
- query = query + " OR " + v + ")";
- }
- else {
- query = query + " OR " + v;
+ query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : "");
}
+ else query = query + " OR " + v + (i === values.length - 1 ? ")" : "");
}
}
-
}
-
- // let limit = typepos.length
- // typepos.forEach(i => {
- // if (i === 0) {
- // if (i + 1 === limit) {
- // query = query + " && " + filters[i] + "_t:" + filters;
- // }
- // else if (filters[i] === filters[i + 3]) {
- // query = query + " && (" + filters[i] + "_t:" + filters;
- // }
- // else {
- // query = query + " && " + filters[i] + "_t:" + filters;
- // }
-
- // }
- // else if (i + 3 > filters.length) {
-
- // }
- // else {
-
- // }
-
- // });
-
- // query = this.applyBasicFieldFilters(query);
-
-
-
- query = query.replace(/-\s+/g, '');
- query = query.replace(/-/g, "");
- return query;
- }
-
- basicRequireWords(query: string): string {
- return query.split(" ").join(" + ").replace(/ + /, "");
+ return query.replace(/-\s+/g, '');
}
@action
filterDocsByType(docs: Doc[]) {
const finalDocs: Doc[] = [];
- const blockedTypes: string[] = [DocumentType.PRESELEMENT, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
docs.forEach(doc => {
- const layoutresult = Cast(doc.type, "string");
- if (layoutresult && !blockedTypes.includes(layoutresult)) {
- if (layoutresult && this._icons.includes(layoutresult)) {
- finalDocs.push(doc);
- }
+ const layoutresult = StrCast(doc.type, "string") as DocumentType;
+ if (layoutresult && !this._blockedTypes.includes(layoutresult) && this._icons.includes(layoutresult)) {
+ finalDocs.push(doc);
}
});
return finalDocs;
}
- addCollectionFilter(query: string): string {
- const collections: Doc[] = this.getCurCollections();
- const oldWords = query.split(" ");
-
- const collectionString: string[] = [];
- collections.forEach(doc => {
- const proto = doc.proto;
- const protoId = (proto || doc)[Id];
- const colString: string = "{!join from=data_l to=id}id:" + protoId + " ";
- collectionString.push(colString);
- });
-
- let finalColString = collectionString.join(" ");
- finalColString = finalColString.trim();
- return "+(" + finalColString + ")" + query;
- }
-
- get filterTypes() {
- return this._icons.length === this._allIcons.length ? undefined : this._icons;
- }
-
- //TODO: basically all of this
- //gets all of the collections of all the docviews that are selected
- //if a collection is the only thing selected, search only in that collection (not its container)
- getCurCollections(): Doc[] {
- const selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
- const collections: Doc[] = [];
- selectedDocs.forEach(async element => {
- const layout: string = StrCast(element.props.Document.layout);
- //checks if selected view (element) is a collection. if it is, adds to list to search through
- if (layout.indexOf("Collection") > -1) {
- //makes sure collections aren't added more than once
- if (!collections.includes(element.props.Document)) {
- collections.push(element.props.Document);
- }
- }
- //makes sure collections aren't added more than once
- if (element.props.ContainingCollectionDoc && !collections.includes(element.props.ContainingCollectionDoc)) {
- collections.push(element.props.ContainingCollectionDoc);
- }
- });
-
- return collections;
+ static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
+ let newarray: Doc[] = [];
+ while (docs.length > 0) {
+ newarray = [];
+ docs.forEach(d => {
+ const fieldKey = Doc.LayoutFieldKey(d);
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+ const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ data && newarray.push(...DocListCast(data));
+ func(d);
+ });
+ docs = newarray;
+ }
}
-
- currentSelectedCollection: DocumentView | undefined = undefined;
- docsforfilter: Doc[] = [];
-
+ @action
searchCollection(query: string) {
- const selectedCollection: DocumentView = SelectionManager.SelectedDocuments()[0];
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
query = query.toLowerCase();
if (selectedCollection !== undefined) {
- this.currentSelectedCollection = selectedCollection;
- if (this.filter === true) {
- this.props.Document.selectedDoc = selectedCollection.props.Document;
- }
- let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
+ // this._currentSelectedCollection = selectedCollection;
+ const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
const found: [Doc, string[], string[]][] = [];
- const docsforFilter: Doc[] = [];
- let newarray: Doc[] = [];
+ SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
+ const hlights = new Set<string>();
+ SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+ Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
+ });
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- d ? console.log(Cast(d.context, Doc)) : null;
- if (d.data !== undefined) {
- newarray.push(...DocListCast(d.data));
- }
- const hlights: string[] = [];
- const protos = Doc.GetAllPrototypes(d);
- protos.forEach(proto => {
- Object.keys(proto).forEach(key => {
- if (StrCast(d[key]).toLowerCase().includes(query) && !hlights.includes(key)) {
- hlights.push(key);
- }
- });
- });
- if (hlights.length > 0) {
- found.push([d, hlights, []]);
- docsforFilter.push(d);
- }
- });
- docs = newarray;
- }
this._results = found;
- this.docsforfilter = docsforFilter;
- if (this.filter === true) {
- selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter);
- docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>(docsforFilter);
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- }
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
this._numTotalResults = found.length;
+ this.realTotalResults = found.length;
}
else {
- this.noresults = "No collection selected :(";
+ this._noResults = "No collection selected :(";
}
}
-
- documentKeys(doc: Doc) {
+ static documentKeys(doc: Doc) {
const keys: { [key: string]: boolean } = {};
// bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
// then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
@@ -482,151 +229,72 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
// then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
// is displayed (unlikely) it won't show up until something else changes.
//TODO Types
- Doc.GetAllPrototypes(doc).map
- (proto => Object.keys(proto).forEach(key => keys[key] = false));
+ Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
return Array.from(Object.keys(keys));
}
- applyBasicFieldFilters(query: string) {
- let finalQuery = "";
-
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.TITLE);
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.AUTHOR);
-
- if (this._deletedDocsStatus) {
- finalQuery = finalQuery + this.basicFieldFilters(query, Keys.TEXT);
- }
- return finalQuery;
- }
-
- basicFieldFilters(query: string, type: string): string {
- let mod = "";
- switch (type) {
- case Keys.AUTHOR: mod = " author_t:"; break;
- case Keys.TITLE: mod = " title_t:"; break;
- case Keys.TEXT: mod = " text_t:"; break;
- }
-
- const newWords: string[] = [];
- const oldWords = query.split(" ");
- oldWords.forEach(word => newWords.push(mod + word));
-
- query = newWords.join(" ");
-
- return query;
- }
-
- get fieldFiltersApplied() { return !(this._authorFieldStatus && this._titleFieldStatus); }
-
@action
- submitSearch = async (reset?: boolean) => {
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- //d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
+ submitSearch = async () => {
+ this.resetSearch(false);
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- if (reset) {
- this.layoutDoc._searchString = "";
- }
//this.props.Document._docFilters = new List();
- this.noresults = "";
+ this._noResults = "";
this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.headercount = 0;
this.children = 0;
- this.buckets = [];
- this.new_buckets = {};
let query = StrCast(this.layoutDoc._searchString);
Doc.SetSearchQuery(query);
- this.searchFullDB ? query = this.getFinalQuery(query) : console.log("local");
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- result[0].searchMatch = undefined;
- });
+ this._searchFullDB && (query = this.getFinalQuery(query));
this._results = [];
this._resultsSet.clear();
- this._isSearch = [];
- this._isSorted = [];
this._visibleElements = [];
this._visibleDocuments = [];
- if (StrCast(this.props.Document.searchQuery)) {
- if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; }
- this._timeout = setTimeout(() => {
- console.log("Resubmitting search");
- }, 60000);
- }
- if (query !== "") {
+ if (query || this._searchFullDB === "My Stuff") {
this._endIndex = 12;
this._maxSearchIndex = 0;
this._numTotalResults = -1;
- this.searchFullDB ? await this.getResults(query) : this.searchCollection(query);
+ this._searchFullDB ? await this.searchDatabase(query) : this.searchCollection(query);
runInAction(() => {
- this._resultsOpen = true;
- this._searchbarOpen = true;
- this._openNoResults = true;
+ this.open = this._searchbarOpen = true;
this.resultsScrolled();
-
});
}
}
- @observable searchFullDB = true;
-
- @observable _timeout: any = undefined;
-
- @observable firststring: string = "";
- @observable secondstring: string = "";
-
- @observable bucketcount: number[] = [];
- @observable buckets: Doc[] | undefined;
-
getAllResults = async (query: string) => {
return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 });
}
private get filterQuery() {
- const types = ["preselement", "docholder", "search", "searchitem", "script", "fonticonbox", "button", "label"]; // this.filterTypes;
- const baseExpr = "NOT baseProto_b:true AND NOT system_b:true";
- const includeDeleted = this.getDataStatus() ? "" : " NOT deleted_b:true";
- const includeIcons = this.getDataStatus() ? "" : " NOT type_t:fonticonbox";
- const typeExpr = !types ? "" : ` ${types.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
+ const baseExpr = "NOT system_b:true";
+ const authorExpr = this._searchFullDB === "My Stuff" ? ` author_t:${Doc.CurrentUserEmail}` : undefined;
+ const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true";
+ const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
// fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello
- const query = [baseExpr, includeDeleted, includeIcons, typeExpr].join(" AND ").replace(/AND $/, "");
- return query;
+ return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
}
- getDataStatus() { return this._deletedDocsStatus; }
+ @computed get primarySort() {
+ const suffixMap = (type: ColumnType) => {
+ switch (type) {
+ case ColumnType.Date: return "_d";
+ case ColumnType.String: return "_t";
+ case ColumnType.Boolean: return "_b";
+ case ColumnType.Number: return "_n";
+ }
+ };
+ const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
+ return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined);
+ }
- private NumResults = 50;
- private lockPromise?: Promise<void>;
- getResults = async (query: string) => {
- if (this.lockPromise) {
- await this.lockPromise;
- }
- this.lockPromise = new Promise(async res => {
+ searchDatabase = async (query: string) => {
+ this._lockPromise && (await this._lockPromise);
+ this._lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, true, { fq: this.filterQuery, start: this._maxSearchIndex, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: "on", "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
- this.realTotalResults = res.numFound;
+ this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound;
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
this._numTotalResults = res.numFound;
}
@@ -634,55 +302,49 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
const lines = new Map<string, string[]>();
res.docs.map((doc, i) => lines.set(doc[Id], res.lines[i]));
- const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
+ const docs = res.docs;
const highlights: typeof res.highlighting = {};
docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
const filteredDocs = this.filterDocsByType(docs);
- runInAction(() => {
- filteredDocs.forEach((doc, i) => {
- const index = this._resultsSet.get(doc);
- const highlight = highlights[doc[Id]];
- const line = lines.get(doc[Id]) || [];
- const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
- // if (this.findCommonElements(hlights)) {
- // }
- if (index === undefined) {
- this._resultsSet.set(doc, this._results.length);
- this._results.push([doc, hlights, line]);
- } else {
- this._results[index][1].push(...hlights);
- this._results[index][2].push(...line);
- }
+ runInAction(() => filteredDocs.forEach((doc, i) => {
+ const index = this._resultsSet.get(doc);
+ const highlight = highlights[doc[Id]];
+ const line = lines.get(doc[Id]) || [];
+ const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
+ // if (this.findCommonElements(hlights)) {
+ // }
+ if (index === undefined) {
+ this._resultsSet.set(doc, this._results.length);
+ this._results.push([doc, hlights, line]);
+ } else {
+ this._results[index][1].push(...hlights);
+ this._results[index][2].push(...line);
+ }
- });
- });
+ }));
this._curRequest = undefined;
}));
- this._maxSearchIndex += this.NumResults;
+ this._maxSearchIndex += this._numResultsPerPage;
await this._curRequest;
}
this.resultsScrolled();
+
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined);
res();
});
- return this.lockPromise;
+ return this._lockPromise;
}
- @observable noresults = "";
- collectionRef = React.createRef<HTMLSpanElement>();
+
startDragCollection = async () => {
const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString)));
const filtered = this.filterDocsByType(res.docs);
- const docs = filtered.map(doc => {
- const isProto = Doc.GetT(doc, "isPrototype", "boolean", true);
- if (isProto) {
- return Doc.MakeDelegate(doc);
- } else {
- return Doc.MakeAlias(doc);
- }
- });
+ const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc));
let x = 0;
let y = 0;
for (const doc of docs.map(d => Doc.Layout(d))) {
@@ -706,28 +368,26 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
y += 300;
}
}
- return Docs.Create.SchemaDocument(Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []), DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
+ const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; });
+ return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
}
@action.bound
openSearch(e: React.SyntheticEvent) {
e.stopPropagation();
- this._openNoResults = false;
- this._resultsOpen = true;
- this._searchbarOpen = true;
+ this._results.forEach(result => Doc.BrushDoc(result[0]));
}
- realTotalResults: number = 0;
-
- @action.bound
- closeSearch = () => {
- //this.closeResults();
- this._searchbarOpen = false;
- }
+ resetSearch = action((close: boolean) => {
+ this._results.forEach(result => {
+ Doc.UnBrushDoc(result[0]);
+ Doc.ClearSearchMatches();
+ });
+ close && (this.open = this._searchbarOpen = false);
+ });
@action.bound
closeResults() {
- this._resultsOpen = false;
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
@@ -737,21 +397,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._curRequest = undefined;
}
- @observable _pageStart: number = 0;
- @observable _pageCount: number = SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
-
- @observable children: number = 0;
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
- if (!this._resultsRef.current) return;
this._endIndex = 30;
const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]);
- // if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) {
- // if (this.noresults === "") {
- // this.noresults = "No search results :(";
- // }
- // return;
- // }
if (this._numTotalResults <= this._maxSearchIndex) {
this._numTotalResults = this._results.length;
@@ -763,301 +412,170 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
// undefined until a searchitem is put in there
this._visibleElements = Array<JSX.Element>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- // indicates if things are placeholders
- this._isSearch = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- this._isSorted = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
-
}
- let max = this.NumResults;
+ let max = this._numResultsPerPage;
max > this._results.length ? max = this._results.length : console.log("");
for (let i = this._pageStart; i < max; i++) {
//if the index is out of the window then put a placeholder in
//should ones that have already been found get set to placeholders?
- if (this._isSearch[i] !== "search") {
- let result: [Doc, string[], string[]] | undefined = undefined;
-
- result = this._results[i];
- if (result) {
- const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- const lines = new List<string>(result[2]);
- highlights.forEach((item) => headers.add(item));
- result[0].lines = lines;
- result[0].highlighting = highlights.join(", ");
- result[0].searchMatch = true;
- if (i < this._visibleDocuments.length) {
- this._visibleDocuments[i] = result[0];
- this._isSearch[i] = "search";
- Doc.BrushDoc(result[0]);
- Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
- this.children++;
- }
-
+ let result: [Doc, string[], string[]] | undefined = undefined;
+
+ result = this._results[i];
+ if (result) {
+ const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
+ const lines = new List<string>(result[2]);
+ highlights.forEach((item) => headers.add(item));
+ Doc.SetSearchMatch(result[0], { searchMatch: 1 });
+ if (i < this._visibleDocuments.length) {
+ this._visibleDocuments[i] = result[0];
+ Doc.BrushDoc(result[0]);
+ Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
+ this.children++;
}
-
}
}
- this.headerscale = headers.size;
- if (Cast(this.props.Document._docFilters, listSpec("string"), []).length === 0) {
- const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
- if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
- const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- headers.forEach(header => {
- if (oldSchemaHeaders.includes(header) === false) {
- newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb"));
- }
- });
- this.headercount = newSchemaHeaders.length;
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
- }
+ if (this.props.Document._schemaHeaders === undefined) {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
}
if (this._maxSearchIndex >= this._numTotalResults) {
this._visibleElements.length = this._results.length;
this._visibleDocuments.length = this._results.length;
- this._isSearch.length = this._results.length;
}
}
- @observable headercount: number = 0;
- @observable headerscale: number = 0;
-
- findCommonElements(arr2: string[]) {
- const arr1 = ["layout", "data"];
- return arr1.some(item => arr2.includes(item));
- }
-
- @computed
- get resFull() { return this._numTotalResults <= 8; }
-
- @computed
- get resultHeight() { return this._numTotalResults * 70; }
-
- addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
- remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc);
- moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
- @computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); }
+ getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
+ panelHeight = () => this.props.PanelHeight();
+ selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ };
+ returnHeight = () => NumCast(this.layoutDoc._height);
+ returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
- @computed get viewspec() { return Cast(this.props.Document._docFilters, listSpec("string"), []); }
-
- getTransform = () => {
- return this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
- }
- panelHeight = () => {
- return this.props.PanelHeight();
- }
- selectElement = (doc: Doc) => {
- //this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex));
- }
-
- addDocument = (doc: Doc) => {
- return null;
+ @action
+ changeSearchScope = (scope: string) => {
+ this.docsforfilter = undefined;
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
+ this._searchFullDB = scope;
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ this.submitSearch();
+ }
+
+ @computed get scopeButtons() {
+ return <div style={{ height: 25, paddingLeft: "4px", paddingRight: "4px", border: "1px solid gray", borderRadius: "0.3em", borderBottom: !this.open ? "1px solid" : "none", }}>
+ <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
+ <div style={{ display: "contents" }}>
+ <div className="radio" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }} >
+ <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this._searchFullDB} onChange={() => this.changeSearchScope("")} />
+ Dashboard
+ </label>
+ </div>
+ <div className="radio" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }} >
+ <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={this._searchFullDB?.length ? true : false} onChange={() => this.changeSearchScope("DB")} />
+ DB
+ <span onClick={action(() => this._searchFullDB = this._searchFullDB === "My Stuff" ? "DB" : "My Stuff")}>
+ {this._searchFullDB === "My Stuff" ? "(me)" : "(full)"}
+ </span>
+ </label>
+ </div>
+ </div>
+ </form>
+ </div>;
}
- @observable filter = false;
+ setSearchFilter = action((collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => {
+ if (collectionView) {
+ const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
+ collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
+ collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
+ }
+ });
- @action newpage() {
- this._pageStart += SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.resultsScrolled();
- }
render() {
- this.props.Document._chromeStatus === "disabled";
- this.props.Document._searchDoc = true;
- const cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length;
- let length = 0;
- length = cols * 205 + 51;
- let height = 0;
- const rows = this.children;
- height = 31 + 31 * 6;
+ const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
return (
<div style={{ pointerEvents: "all" }} className="searchBox-container">
<div className="searchBox-bar">
- <div style={{ position: "absolute", left: 15 }}>{Doc.CurrentUserEmail}</div>
- <div style={{ display: "flex", alignItems: "center" }}>
- <Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>} ><div>
- <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
- style={{ cursor: "hand", color: "black", padding: 1, left: 35, position: "relative" }} />
- </div></Tooltip>
- <div style={{
- position: "relative",
- left: 245,
- zIndex: 9000,
- color: "grey",
- background: "white",
- }}> {`${this._results.length}` + " of " + `${this.realTotalResults}`}</div>
- <div style={{ cursor: "default", left: 250, position: "relative", }}>
- <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} ><div>
- <FontAwesomeIcon icon={"filter"} size="lg"
- style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
- onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined); }}
- onClick={action(() => {
- ///DONT Change without emailing andy r first.
- this.filter = !this.filter && !this.searchFullDB;
- if (this.filter === true && this.currentSelectedCollection !== undefined) {
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter);
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>(this.docsforfilter);
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._docFilters = new List<string>(this.viewspec);
- this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document;
- }
- else if (this.filter === false && this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
-
- this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.props.Document.selectedDoc = undefined;
- }
- }
- )} />
- </div></Tooltip></div>
- <input value={this.newsearchstring} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
- className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch}
- style={{ padding: 1, paddingLeft: 20, paddingRight: 20, color: "black", height: 20, width: 250 }} />
- <div style={{
- height: 25,
- paddingLeft: "4px",
- paddingRight: "4px",
- border: "1px solid gray",
- borderRadius: "0.3em",
- borderBottom: this.open === false ? "1px solid" : "none",
- }}>
- <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
- <div style={{ display: "contents" }}>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this.searchFullDB} onChange={() => {
- runInAction(() => {
- this.searchFullDB = !this.searchFullDB;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.currentSelectedCollection.props.Document._searchDocs = undefined;
- this.currentSelectedCollection = undefined;
- }
- this.submitSearch();
- });
- }} />
- Collection
- </label>
- </div>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input style={{ marginLeft: -16, marginTop: -1 }} type="radio" checked={this.searchFullDB} onChange={() => {
- runInAction(() => {
- this.searchFullDB = !this.searchFullDB;
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.filter = false;
- if (this.currentSelectedCollection !== undefined) {
- let newarray: Doc[] = [];
- let docs: Doc[] = [];
- docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]);
- while (docs.length > 0) {
- newarray = [];
- docs.forEach((d) => {
- if (d.data !== undefined) {
- d._searchDocs = new List<Doc>();
- d._docFilters = new List();
- const newdocs = DocListCast(d.data);
- newdocs.forEach((newdoc) => {
- newarray.push(newdoc);
- });
- }
- });
- docs = newarray;
- }
- this.currentSelectedCollection.props.Document._docFilters = new List();
- this.currentSelectedCollection.props.Document._searchDocs = undefined;
- this.currentSelectedCollection = undefined;
- }
- this.submitSearch();
- });
- }} />
- DB
- </label>
- </div>
- </div>
- </form>
+ <div className="searchBox-lozenges" >
+ <div className="searchBox-lozenge-user">
+ {`${Doc.CurrentUserEmail}`}
+ <div className="searchBox-logoff" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
+ Logoff
+ </div>
+ </div>
+ <div className="searchBox-lozenge">
+ {`UI project`}
+ </div>
+ <div className="searchBox-lozenge-dashboard" >
+ <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
+ value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}>
+ {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
+ </select>
+ <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
+ New
+ </div>
+ <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}>
+ Snapshot
+ </div>
</div>
</div>
-
- </div>
- <div style={{ zIndex: 20000, color: "black" }}>
- {this._searchbarOpen === true ?
+ <div className="searchBox-query" >
+ <input defaultValue={""} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef}
+ className="searchBox-barChild searchBox-input" onKeyPress={this.enter}
+ style={{ padding: 1, paddingLeft: 20, paddingRight: 60, color: "black", height: 20, width: 250 }} />
+ <div style={{ display: "flex", alignItems: "center" }}>
+ <div style={{ position: "absolute", left: 10 }}>
+ <Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>}>
+ <div ref={this.collectionRef}><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
+ style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
+ </Tooltip>
+ </div>
+ <div style={{ position: "absolute", left: 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
+ {`${this._results.length}` + " of " + `${this.realTotalResults}`}
+ </div>
+ <div style={{ cursor: "default", left: 235, position: "absolute", }}>
+ <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} >
+ <div>
+ <FontAwesomeIcon icon={"filter"} size="lg"
+ style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
+ onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
+ onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
+ </div>
+ </Tooltip>
+ </div>
+ {this.scopeButtons}
+ </div>
+ </div >
+ </div >
+ {!this._searchbarOpen ? (null) :
+ <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}>
<div style={{ display: "flex", justifyContent: "center", }}>
- {this.noresults === "" ? <div style={{ display: this.open === true ? "flex" : "none", overflow: "auto", }}>
- <CollectionView {...this.props}
+ <div style={{ display: this.open ? "flex" : "none", overflow: "auto", position: "absolute" }}>
+ <CollectionSchemaView {...this.props}
+ CollectionView={undefined}
+ annotationsKey={""}
+ addDocument={returnFalse}
Document={this.props.Document}
moveDocument={returnFalse}
removeDocument={returnFalse}
- PanelHeight={this.open === true ? () => height : () => 0}
- PanelWidth={this.open === true ? () => length : () => 0}
- overflow={length > window.innerWidth || rows > 6 ? true : false}
+ PanelHeight={this.open ? this.returnHeight : returnZero}
+ PanelWidth={this.open ? this.returnLength : returnZero}
+ overflow={length > window.innerWidth || this.children > 6 ? true : false}
focus={this.selectElement}
ScreenToLocalTransform={Transform.Identity}
/>
- </div> :
- <div style={{ display: "flex", justifyContent: "center" }}><div style={{ height: 200, top: 54, minWidth: 400, position: "absolute", backgroundColor: "rgb(241, 239, 235)", display: "flex", justifyContent: "center", alignItems: "center", border: "black 1px solid", }}>
- <div>{this.noresults}</div>
- </div></div>}
- </div> : undefined}
- </div>
-
- <div className="searchBox-results" onScroll={this.resultsScrolled} style={{
- display: this._resultsOpen ? "flex" : "none",
- height: this.resFull ? "auto" : this.resultHeight,
- overflow: "visibile" // this.resFull ? "auto" : "visible"
- }} ref={this._resultsRef}>
- </div>
+ <div style={{ position: "absolute", right: 5, bottom: 7, width: 15, height: 15, }}
+ onPointerDown={e => setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
+ this.props.Document._height = NumCast(this.props.Document._height) + delta[1];
+ return false;
+ }, returnFalse, emptyFunction)}
+ >
+ <FontAwesomeIcon icon="grip-lines" size="lg" />
+ </div>
+ </div>
+ </div>
+ </div>
+ }
</div >
);
}
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 647e1ce6f..82c0e19c8 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -1,20 +1,16 @@
-import { observer } from "mobx-react";
-import React = require("react");
-import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView";
-import { FieldViewProps, FieldView } from "../nodes/FieldView";
-import { observable, action } from "mobx";
-import { DocumentDecorations } from "../DocumentDecorations";
-import "../../views/nodes/WebBox.scss";
-import "./DashWebRTCVideo.scss";
-import { initialize, hangup, refreshVideos } from "./WebCamLogic";
+import { faPhoneSlash, faSync } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faSync, faPhoneSlash } from "@fortawesome/free-solid-svg-icons";
+import { action, observable } from "mobx";
+import { observer } from "mobx-react";
import { Doc } from "../../../fields/Doc";
import { InkTool } from "../../../fields/InkField";
-
-library.add(faSync);
-library.add(faPhoneSlash);
+import "../../views/nodes/WebBox.scss";
+import { DocumentDecorations } from "../DocumentDecorations";
+import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import "./DashWebRTCVideo.scss";
+import { hangup, initialize, refreshVideos } from "./WebCamLogic";
+import React = require("react");
/**