aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/documents/Documents.ts80
-rw-r--r--src/client/util/DragManager.ts1
-rw-r--r--src/client/util/RichTextRules.ts3
-rw-r--r--src/client/util/RichTextSchema.tsx2
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.scss3
-rw-r--r--src/client/views/DocumentDecorations.tsx15
-rw-r--r--src/client/views/EditableView.tsx4
-rw-r--r--src/client/views/MainView.tsx34
-rw-r--r--src/client/views/TemplateMenu.tsx14
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss1
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx18
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx3
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx10
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx3
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx5
-rw-r--r--src/client/views/collections/CollectionSubView.tsx114
-rw-r--r--src/client/views/collections/CollectionTimeView.scss7
-rw-r--r--src/client/views/collections/CollectionTreeView.scss9
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx77
-rw-r--r--src/client/views/collections/CollectionView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx14
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx3
-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/nodes/CollectionFreeFormDocumentView.tsx9
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx29
-rw-r--r--src/client/views/nodes/DocumentView.tsx102
-rw-r--r--src/client/views/nodes/FieldView.tsx6
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx14
-rw-r--r--src/client/views/nodes/ImageBox.tsx9
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.scss49
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx57
-rw-r--r--src/client/views/webcam/WebCamLogic.js14
35 files changed, 405 insertions, 337 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 49d1820f5..53671707e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -22,7 +22,6 @@ import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField }
import { HtmlField } from "../../new_fields/HtmlField";
import { List } from "../../new_fields/List";
import { Cast, NumCast } from "../../new_fields/Types";
-import { IconField } from "../../new_fields/IconField";
import { listSpec } from "../../new_fields/Schema";
import { DocServer } from "../DocServer";
import { dropActionType } from "../util/DragManager";
@@ -53,8 +52,8 @@ import { InkingStroke } from "../views/InkingStroke";
import { InkField } from "../../new_fields/InkField";
import { InkingControl } from "../views/InkingControl";
import { RichTextField } from "../../new_fields/RichTextField";
-import { Networking } from "../Network";
import { extname } from "path";
+import { MessageStore } from "../../server/Message";
const requestImageSize = require('../util/request-image-size');
const path = require('path');
@@ -69,7 +68,9 @@ export interface DocumentOptions {
_fitWidth?: boolean;
_fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
_LODdisable?: boolean;
- dropAction?: dropActionType;
+ _showTitleHover?: string; //
+ _showTitle?: string; // which field to display in the title area. leave empty to have no title
+ _showCaption?: string; // which field to display in the caption area. leave empty to have no caption
_chromeStatus?: string;
_viewType?: number;
_gridGap?: number; // gap between items in masonry view
@@ -81,6 +82,7 @@ export interface DocumentOptions {
x?: number;
y?: number;
z?: number;
+ dropAction?: dropActionType;
layoutKey?: string;
type?: string;
title?: string;
@@ -88,7 +90,6 @@ export interface DocumentOptions {
scale?: number;
isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents
forceActive?: boolean;
- preventTreeViewOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expande/collapse state to be independent of other views of the same document in the tree view
layout?: string | Doc;
hideHeadings?: boolean; // whether stacking view column headings should be hidden
isTemplateForField?: string; // the field key for which the containing document is a rendering template
@@ -105,10 +106,8 @@ export interface DocumentOptions {
curPage?: number;
currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds
displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video)
- documentText?: string;
borderRounding?: string;
boxShadow?: string;
- showTitle?: string;
sectionFilter?: string; // field key used to determine headings for sections in stacking and masonry views
schemaColumns?: List<SchemaHeaderField>;
dockingConfig?: string;
@@ -121,15 +120,16 @@ export interface DocumentOptions {
onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked
onPointerDown?: ScriptField;
onPointerUp?: ScriptField;
+ dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
- clipboard?: Doc; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop
+ clipboard?: Doc;
icon?: string;
sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
- dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
strokeWidth?: number;
color?: string;
+ treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
treeViewHideTitle?: boolean; // whether to hide the title of a tree view
treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
treeViewOpen?: boolean; // whether this document is expanded in a tree view
@@ -138,9 +138,9 @@ export interface DocumentOptions {
limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
- isExpanded?: boolean; // is linear view expanded
- textTransform?: string; // is linear view expanded
- letterSpacing?: string; // is linear view expanded
+ linearViewIsExpanded?: boolean; // is linear view expanded
+ textTransform?: string;
+ letterSpacing?: string;
}
class EmptyBox {
@@ -168,7 +168,7 @@ export namespace Docs {
const TemplateMap: TemplateMap = new Map([
[DocumentType.TEXT, {
layout: { view: FormattedTextBox, dataField: data },
- options: { _height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" }
+ options: { _height: 150 }
}],
[DocumentType.HIST, {
layout: { view: HistogramBox, dataField: data },
@@ -346,6 +346,7 @@ export namespace Docs {
export namespace Create {
export function Buxton() {
+ let responded = false;
const loading = new Doc;
loading.title = "Please wait for the import script...";
const parent = TreeDocument([loading], {
@@ -354,41 +355,48 @@ export namespace Docs {
_height: 400,
_LODdisable: true
});
- Networking.FetchFromServer("/buxton").then(response => {
- const devices = JSON.parse(response);
- if (!Array.isArray(devices)) {
- if ("error" in devices) {
- loading.title = devices.error;
- } else {
- console.log(devices);
- alert("The importer returned an unexpected import format. Check the console.");
- }
- return;
+ const parentProto = Doc.GetProto(parent);
+ const { _socket } = DocServer;
+ Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, errors }) => {
+ if (!responded) {
+ responded = true;
+ parentProto.data = new List<Doc>();
}
- const parentProto = Doc.GetProto(parent);
- parentProto.data = new List<Doc>();
- devices.forEach(device => {
+ if (device) {
const { __images } = device;
delete device.__images;
const { ImageDocument, StackingDocument } = Docs.Create;
- if (Array.isArray(__images)) {
- const constructed = __images.map(relative => Utils.prepend(relative));
- const deviceImages = constructed.map((url, i) => ImageDocument(url, { title: `image${i}.${extname(url)}` }));
- const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true });
- const deviceProto = Doc.GetProto(doc);
- deviceProto.hero = new ImageField(constructed[0]);
- Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto);
- Doc.AddDocToList(parentProto, "data", doc);
- }
- });
+ const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight }));
+ const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => ImageDocument(url, {
+ title: `image${i}.${extname(url)}`,
+ _nativeWidth: nativeWidth,
+ _nativeHeight: nativeHeight
+ }));
+ const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true });
+ const deviceProto = Doc.GetProto(doc);
+ deviceProto.hero = new ImageField(constructed[0].url);
+ Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto);
+ Doc.AddDocToList(parentProto, "data", doc);
+ } else if (errors) {
+ console.log(errors);
+ } else {
+ alert("A Buxton document import was completely empty (??)");
+ }
+ });
+ Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => {
+ _socket.off(MessageStore.BuxtonDocumentResult.Message);
+ _socket.off(MessageStore.BuxtonImportComplete.Message);
+ alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`);
});
+ const startTime = Date.now();
+ Utils.Emit(_socket, MessageStore.BeginBuxtonImport, "");
return parent;
}
Scripting.addGlobal(Buxton);
const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "_annotationOn",
- "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "showTitle"];
+ "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover"];
/**
* This function receives the relevant document prototype and uses
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e572f0fcb..2877d5fd7 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -134,7 +134,6 @@ export namespace DragManager {
embedDoc?: boolean;
moveDocument?: MoveFunction;
isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts
- applyAsTemplate?: boolean;
}
export class LinkDragData {
constructor(linkSourceDoc: Doc) {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 6b8762a5e..de0f46202 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -121,8 +121,7 @@ export class RichTextRules {
new InputRule(
new RegExp(/##$/),
(state, match, start, end) => {
- const schemaDoc = Doc.GetDataDoc(this.Document);
- const textDoc = Doc.GetProto(Cast(schemaDoc[DataSym], Doc, null)!);
+ const textDoc = this.Document[DataSym];
const numInlines = NumCast(textDoc.inlineTextCount);
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3cf0561dc..80bd75771 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1136,7 +1136,7 @@ const fromJson = schema.nodeFromJSON;
schema.nodeFromJSON = (json: any) => {
const node = fromJson(json);
- if (json.type === schema.marks.summarize.name) {
+ if (json.type === schema.nodes.summary.name) {
node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
}
return node;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ee850922a..2201fe710 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -301,7 +301,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
const view0 = this.view0;
const templates: Map<Template, boolean> = new Map();
Array.from(Object.values(Templates.TemplateList)).map(template =>
- templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean)));
+ templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean)));
return !view0 ? (null) : <div title="Customize layout" className="documentButtonBar-linkFlyout" ref={this._dragRef}>
<Flyout anchorPoint={anchorPoints.LEFT_TOP}
content={<TemplateMenu docViews={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}>
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index d57b1456a..455e53a79 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -165,11 +165,12 @@ $linkGap : 3px;
.link-button-container {
margin-top: $linkGap;
- grid-column: 1/4;
width: max-content;
height: auto;
display: flex;
flex-direction: row;
+ z-index: 5;
+ position: absolute;
}
.linkButtonWrapper {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3dfe34234..65c02591c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -266,11 +266,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const layoutKey = Cast(dv.props.Document.layoutKey, "string", null);
const collapse = layoutKey !== "layout_icon";
if (collapse) {
- dv.setCustomView(collapse, "icon");
+ dv.switchViews(collapse, "icon");
if (layoutKey && layoutKey !== "layout") dv.props.Document.deiconifyLayout = layoutKey.replace("layout_", "");
} else {
const deiconifyLayout = Cast(dv.props.Document.deiconifyLayout, "string", null);
- dv.setCustomView(deiconifyLayout ? true : false, deiconifyLayout);
+ dv.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
dv.props.Document.deiconifyLayout = undefined;
}
});
@@ -509,11 +509,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
top: bounds.y - this._resizeBorderWidth / 2,
pointerEvents: this.Interacting ? "none" : "all",
zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
- }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
+ }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
</div>
<div className="documentDecorations-container" ref={this.setTextBar} style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight + 3) + "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,
opacity: this._opacity
@@ -545,10 +545,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<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 id="documentDecorations-borderRadius" className="documentDecorations-radius" onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}><span className="borderRadiusTooltip" title="Drag Corner Radius"></span></div>
- <div className="link-button-container">
- <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
- </div>
+
</div >
+ <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2, top: bounds.b + this._resizeBorderWidth / 2 }}>
+ <DocumentButtonBar views={SelectionManager.SelectedDocuments()} />
+ </div>
</div>
);
}
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 84c6b0dfd..4a27425e8 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -7,6 +7,7 @@ import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField';
import { ContextMenu } from './ContextMenu';
import { ContextMenuProps } from './ContextMenuItem';
import "./EditableView.scss";
+import { CollectionTreeView } from './collections/CollectionTreeView';
export interface EditableProps {
/**
@@ -60,12 +61,14 @@ export interface EditableProps {
*/
@observer
export class EditableView extends React.Component<EditableProps> {
+ public static loadId = "";
@observable _editing: boolean = false;
@observable _headingsHack: number = 1;
constructor(props: EditableProps) {
super(props);
this._editing = this.props.editing ? true : false;
+ EditableView.loadId = "";
}
@action
@@ -75,6 +78,7 @@ export class EditableView extends React.Component<EditableProps> {
// to false. this will no longer do so -syip
if (nextProps.editing && nextProps.editing !== this._editing) {
this._editing = nextProps.editing;
+ EditableView.loadId = "";
}
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 401a4b15c..ba49a2b53 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc'
import { Id } from '../../new_fields/FieldSymbols';
import { List } from '../../new_fields/List';
import { listSpec } from '../../new_fields/Schema';
-import { Cast, FieldValue, StrCast } from '../../new_fields/Types';
+import { Cast, FieldValue, StrCast, BoolCast } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils, emptyPath } from '../../Utils';
import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
@@ -43,6 +43,7 @@ import SettingsManager from '../util/SettingsManager';
import { TraceMobx } from '../../new_fields/util';
import { RadialMenu } from './nodes/RadialMenu';
import RichTextMenu from '../util/RichTextMenu';
+import { DocumentType } from '../documents/DocumentTypes';
@observer
export class MainView extends React.Component {
@@ -56,8 +57,9 @@ export class MainView extends React.Component {
@observable private _panelHeight: number = 0;
@observable private _flyoutTranslate: boolean = true;
@observable public flyoutWidth: number = 250;
+ private get darkScheme() { return BoolCast(Cast(this.userDoc.activeWorkspace, Doc, null)?.darkScheme); }
- @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
+ @computed private get userDoc() { return Doc.UserDoc(); }
@computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
@computed public get sidebarButtonsDoc() { return Cast(CurrentUserUtils.UserDocument.sidebarButtons, Doc) as Doc; }
@@ -207,7 +209,6 @@ export class MainView extends React.Component {
_width: this._panelWidth * .7,
_height: this._panelHeight,
title: "Collection " + workspaceCount,
- backgroundColor: "white"
};
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
Doc.AddDocToList(Doc.GetProto(CurrentUserUtils.UserDocument.documents as Doc), "data", freeformDoc);
@@ -273,6 +274,16 @@ export class MainView extends React.Component {
getPHeight = () => this._panelHeight;
getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
+ childBackgroundColor = (doc: Doc) => {
+ if (this.darkScheme) {
+ return doc.type === DocumentType.TEXT ? "#112423" : "black";
+ }
+ return doc.type === DocumentType.TEXT ? "#f1efeb" :
+ doc.type === DocumentType.COL && doc._viewType === CollectionViewType.Tree ? "lightgray" : "white";
+ }
+ sidebarBackgroundColor = (doc: Doc) => {
+ return this.childBackgroundColor(doc);
+ }
@computed get mainDocView() {
return <DocumentView Document={this.mainContainer!}
DataDoc={undefined}
@@ -281,13 +292,13 @@ export class MainView extends React.Component {
addDocTab={this.addDocTabFunc}
pinToPres={emptyFunction}
onClick={undefined}
+ backgroundColor={this.childBackgroundColor}
removeDocument={undefined}
ScreenToLocalTransform={Transform.Identity}
ContentScaling={returnOne}
PanelWidth={this.getPWidth}
PanelHeight={this.getPHeight}
renderDepth={0}
- backgroundColor={returnEmptyString}
focus={emptyFunction}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
@@ -366,7 +377,7 @@ export class MainView extends React.Component {
mainContainerXf = () => new Transform(0, -this._buttonBarHeight, 1);
@computed get flyout() {
- const sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+ const sidebarContent = this.userDoc?.sidebarContainer;
if (!(sidebarContent instanceof Doc)) {
return (null);
}
@@ -388,7 +399,7 @@ export class MainView extends React.Component {
PanelHeight={this.getPHeight}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ backgroundColor={this.sidebarBackgroundColor}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -414,7 +425,7 @@ export class MainView extends React.Component {
PanelHeight={this.getContentsHeight}
renderDepth={0}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ backgroundColor={this.sidebarBackgroundColor}
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
bringToFront={emptyFunction}
@@ -437,7 +448,7 @@ export class MainView extends React.Component {
@computed get mainContent() {
const sidebar = this.userDoc && this.userDoc.sidebarContainer;
return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
- <div className="mainView-mainContent" >
+ <div className="mainView-mainContent" style={{ color: this.darkScheme ? "lightGray" : "black" }} >
<div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
<div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
style={{ backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} >
@@ -482,12 +493,13 @@ export class MainView extends React.Component {
return new Transform(-translateX, -translateY, 1 / scale);
}
@computed get docButtons() {
- if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) {
+ const expandingBtns = Doc.UserDoc()?.expandingButtons;
+ if (expandingBtns instanceof Doc) {
return <div className="mainView-docButtons" ref={this._docBtnRef}
- style={{ height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} >
+ style={{ height: !expandingBtns.linearViewIsExpanded ? "42px" : undefined }} >
<MainViewNotifs />
<CollectionLinearView
- Document={CurrentUserUtils.UserDocument.expandingButtons}
+ Document={expandingBtns}
DataDoc={undefined}
LibraryPath={emptyPath}
fieldKey={"data"}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index c9c6af054..595c3817e 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -50,7 +50,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
@observable private _hidden: boolean = true;
toggleLayout = (e: React.ChangeEvent<HTMLInputElement>, layout: string): void => {
- this.props.docViews.map(dv => dv.setCustomView(e.target.checked, layout));
+ this.props.docViews.map(dv => dv.switchViews(e.target.checked, layout));//.setCustomView(e.target.checked, layout));
}
toggleFloat = (e: React.ChangeEvent<HTMLInputElement>): void => {
@@ -64,11 +64,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
@undoBatch
@action
toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
- if (event.target.checked) {
- this.props.docViews.map(d => d.Document["show" + template.Name] = template.Name.toLowerCase());
- } else {
- this.props.docViews.map(d => d.Document["show" + template.Name] = "");
- }
+ this.props.docViews.forEach(d => Doc.Layout(d.Document)["_show" + template.Name] = event.target.checked ? template.Name.toLowerCase() : "");
}
@action
@@ -79,10 +75,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
@undoBatch
@action
toggleChrome = (): void => {
- this.props.docViews.map(dv => {
- const layout = Doc.Layout(dv.Document);
- layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : StrCast(layout._replacedChrome, "enabled"));
- });
+ this.props.docViews.map(dv => Doc.Layout(dv.Document)).forEach(layout =>
+ layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : StrCast(layout._replacedChrome, "enabled")));
}
// todo: add brushes to brushMap to save with a style name
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index ad369bbff..fd1296286 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,6 +1,7 @@
.collectionCarouselView-outer {
background: gray;
+ height : 100%;
.collectionCarouselView-caption {
margin-left: 10%;
margin-right: 10%;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 00edf71dd..226a1c813 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -12,6 +12,8 @@ import { CollectionSubView } from './CollectionSubView';
import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
import { Doc } from '../../../new_fields/Doc';
import { FormattedTextBox } from '../nodes/FormattedTextBox';
+import { ContextMenu } from '../ContextMenu';
+import { ObjectField } from '../../../new_fields/ObjectField';
type CarouselDocument = makeInterface<[typeof documentSchema,]>;
const CarouselDocument = makeInterface(documentSchema);
@@ -50,6 +52,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
<div>
<div className="collectionCarouselView-image">
<ContentFittingDocumentView {...this.props}
+ backgroundColor={this.props.backgroundColor}
Document={this.childLayoutPairs[index].layout}
DataDocument={this.childLayoutPairs[index].data}
PanelHeight={this.panelHeight}
@@ -70,8 +73,21 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
</div>
</>;
}
+
+
+ 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()) {
+ ContextMenu.Instance.addItem({
+ description: "Make Hero Image", event: () => {
+ const index = NumCast(this.layoutDoc._itemIndex);
+ (this.dataDoc || Doc.GetProto(this.props.Document)).hero = ObjectField.MakeCopy(this.childLayoutPairs[index].layout.data as ObjectField);
+ }, icon: "plus"
+ });
+ }
+ }
render() {
- return <div className="collectionCarouselView-outer" ref={this.createDashEventsTarget}>
+ return <div className="collectionCarouselView-outer" ref={this.createDashEventsTarget} onContextMenu={this.onContextMenu}>
{this.content}
{this.buttons}
</div>;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cb413b3e3..0b7dbea7c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -599,6 +599,7 @@ interface DockedFrameProps {
dataDocumentId: FieldId;
glContainer: any;
libraryPath: (FieldId[]);
+ backgroundColor?: (doc:Doc) => string| undefined;
//collectionDockingView: CollectionDockingView
}
@observer
@@ -756,7 +757,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
parentActive={returnTrue}
whenActiveChanged={emptyFunction}
focus={emptyFunction}
- backgroundColor={returnEmptyString}
+ backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 67062ae41..7eb316cf0 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -38,8 +38,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
componentDidMount() {
// is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported).
- this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0),
- () => this.props.Document._width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
+ this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.linearViewIsExpanded ? 1 : 0),
+ () => this.props.Document._width = 5 + (this.props.Document.linearViewIsExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
{ fireImmediately: true }
);
@@ -84,8 +84,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
const guid = Utils.GenerateGuid();
return <div className="collectionLinearView-outer">
<div className="collectionLinearView" ref={this.createDashEventsTarget} >
- <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
- onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
+ <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+ onChange={action((e: any) => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
<label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
<div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25) }}>
@@ -97,7 +97,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
return <div className={`collectionLinearView-docBtn` + (pair.layout.onClick || pair.layout.onDragStart ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
style={{
width: nested ? pair.layout[WidthSym]() : this.dimension() - deltaSize,
- height: nested && pair.layout.isExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
+ height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension() - deltaSize,
}} >
<DocumentView
Document={pair.layout}
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index caffa7eb1..a3b1b5ec0 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -37,7 +37,8 @@ export interface CellProps {
renderDepth: number;
addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean;
pinToPres: (document: Doc) => void;
- moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+ moveDocument: (document: Doc, targetCollection: Doc | undefined,
+ addDocument: (document: Doc) => boolean) => boolean;
isFocused: boolean;
changeFocusedCellByIndex: (row: number, col: number) => void;
setIsEditing: (isEditing: boolean) => void;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d21ae32bc..055035b3e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -40,7 +40,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
@computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
+ @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
@computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@@ -159,6 +159,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return <ContentFittingDocumentView
Document={doc}
DataDocument={dataDoc}
+ backgroundColor={this.props.backgroundColor}
LayoutDoc={this.props.childLayoutTemplate}
LibraryPath={this.props.LibraryPath}
renderDepth={this.props.renderDepth + 1}
@@ -369,8 +370,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
if (!e.isPropagationStopped()) {
const subItems: ContextMenuProps[] = [];
subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" });
- subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" });
- subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" });
ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" });
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 32480ad4e..0963e1ea6 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -159,7 +159,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const docDragData = de.complete.docDragData;
(this.props.Document.dropConverter instanceof ScriptField) &&
this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this
- if (docDragData && !docDragData.applyAsTemplate) {
+ if (docDragData) {
if (de.altKey && docDragData.draggedDocuments.length) {
this.childDocs.map(doc => {
doc.layout_fromParent = docDragData.draggedDocuments[0];
@@ -253,7 +253,8 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}
});
} else {
- const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300, documentText: text });
+ const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 });
+ Doc.GetProto(htmlDoc)["data-text"] = text;
this.props.addDocument(htmlDoc);
}
return;
@@ -283,63 +284,74 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const albumId = matches[3];
const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId);
console.log(mediaItems);
+ return;
}
- const batch = UndoManager.StartBatch("collection view drop");
- const promises: Promise<void>[] = [];
- // tslint:disable-next-line:prefer-for-of
- for (let i = 0; i < e.dataTransfer.items.length; i++) {
- const item = e.dataTransfer.items[i];
- if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
- let str: string;
- const prom = new Promise<string>(resolve => e.dataTransfer.items[i].getAsString(resolve))
- .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
- .then(result => {
- const type = result["content-type"];
- if (type) {
- Docs.Get.DocumentFromType(type, str, options)
- .then(doc => doc && this.props.addDocument(doc));
- }
- });
- promises.push(prom);
- }
- const type = item.type;
- if (item.kind === "file") {
- const file = item.getAsFile();
- const formData = new FormData();
-
- if (!file || !file.type) {
- continue;
+ const { items } = e.dataTransfer;
+ const { length } = items;
+ if (length) {
+ const batch = UndoManager.StartBatch("collection view drop");
+ const promises: Promise<void>[] = [];
+ // tslint:disable-next-line:prefer-for-of
+ for (let i = 0; i < length; i++) {
+ const item = e.dataTransfer.items[i];
+ if (item.kind === "string" && item.type.indexOf("uri") !== -1) {
+ let str: string;
+ const prom = new Promise<string>(resolve => item.getAsString(resolve))
+ .then(action((s: string) => rp.head(Utils.CorsProxy(str = s))))
+ .then(result => {
+ const type = result["content-type"];
+ if (type) {
+ Docs.Get.DocumentFromType(type, str, options)
+ .then(doc => doc && this.props.addDocument(doc));
+ }
+ });
+ promises.push(prom);
}
+ const type = item.type;
+ if (item.kind === "file") {
+ const file = item.getAsFile();
+ const formData = new FormData();
+
+ if (!file || !file.type) {
+ continue;
+ }
- formData.append('file', file);
- const dropFileName = file ? file.name : "-empty-";
- promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => {
- results.map(action((result: any) => {
- const { accessPaths, nativeWidth, nativeHeight, contentSize } = result;
- const full = { ...options, _width: 300, title: dropFileName };
- const pathname = Utils.prepend(accessPaths.agnostic.client);
- Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
- if (doc) {
- const proto = Doc.GetProto(doc);
- proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
- nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
- nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
- contentSize && (proto.contentSize = contentSize);
- this.props?.addDocument(doc);
+ formData.append('file', file);
+ const dropFileName = file ? file.name : "-empty-";
+ promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => {
+ results.map(action((result: any) => {
+ const { accessPaths, nativeWidth, nativeHeight, contentSize } = result;
+ if (Object.keys(accessPaths).length) {
+ const full = { ...options, _width: 300, title: dropFileName };
+ const pathname = Utils.prepend(accessPaths.agnostic.client);
+ Docs.Get.DocumentFromType(type, pathname, full).then(doc => {
+ if (doc) {
+ const proto = Doc.GetProto(doc);
+ proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
+ nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
+ contentSize && (proto.contentSize = contentSize);
+ this.props?.addDocument(doc);
+ }
+ });
+ } else {
+ alert("Upload failed...");
}
- });
+ }));
}));
- }));
+ }
}
- }
- if (promises.length) {
- Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
- } else {
- if (text && !text.includes("https://")) {
- this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+ if (promises.length) {
+ Promise.all(promises).finally(() => { completed && completed(); batch.end(); });
+ } else {
+ if (text && !text.includes("https://")) {
+ this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+ }
+ batch.end();
}
- batch.end();
+ } else {
+ alert("No uploadable content found.");
}
}
}
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index 2dffb3ea0..6ea5e6908 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -67,6 +67,7 @@
pointer-events: all;
padding: 5px;
border: 1px solid black;
+ display:none;
}
.collectionTimeView-treeView {
@@ -131,4 +132,10 @@
.collectionFreeform-customText {
text-align: center;
}
+}
+
+.collectionTimeView:hover, .collectionTimeView-pivot:hover {
+ .pivotKeyEntry {
+ display:unset;
+ }
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2fa6813d7..6ebe81545 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -63,7 +63,9 @@
font-size: 8pt;
margin-left: 3px;
display: none;
- background: lightgray;
+}
+.collectionTreeView-keyHeader:hover {
+ background: #797777;
}
.collectionTreeView-subtitle {
@@ -84,9 +86,11 @@
.treeViewItem-openRight {
display: none;
height: 17px;
- background: gray;
width: 15px;
}
+.treeViewItem-openRight:hover {
+ background: #797777;
+}
.treeViewItem-border {
display: inherit;
@@ -101,7 +105,6 @@
.treeViewItem-openRight {
display: inline-block;
height: 17px;
- background: #a8a7a7;
width: 15px;
// display: inline;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index be2947dff..e2b3cc425 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,7 +3,7 @@ import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc';
+import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { Document, listSpec } from '../../../new_fields/Schema';
@@ -56,12 +56,13 @@ export interface TreeViewProps {
indentDocument?: () => void;
outdentDocument?: () => void;
ScreenToLocalTransform: () => Transform;
+ backgroundColor?: (doc: Doc) => string | undefined;
outerXf: () => { translateX: number, translateY: number };
treeViewId: Doc;
parentKey: string;
active: (outsideReaction?: boolean) => boolean;
treeViewHideHeaderFields: () => boolean;
- preventTreeViewOpen: boolean;
+ treeViewPreventOpen: boolean;
renderedIds: string[];
onCheckedClick?: ScriptField;
}
@@ -84,11 +85,10 @@ library.add(faPlus, faMinus);
*
* special fields:
* treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
- * preventTreeViewOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
+ * treeViewPreventOpen : ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document)
* treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
*/
class TreeView extends React.Component<TreeViewProps> {
- static loadId = "";
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _treedropDisposer?: DragManager.DragDropDisposer;
private _dref = React.createRef<HTMLDivElement>();
@@ -97,8 +97,8 @@ class TreeView extends React.Component<TreeViewProps> {
get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
- set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
- @computed get treeViewOpen() { return (!this.props.preventTreeViewOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
+ set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
+ @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.document.maxEmbedHeight, 300); }
@computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; }
@@ -171,7 +171,7 @@ class TreeView extends React.Component<TreeViewProps> {
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline-block"}
- editing={this.dataDoc[Id] === TreeView.loadId}
+ editing={this.dataDoc[Id] === EditableView.loadId}
contents={StrCast(this.props.document[key])}
height={12}
fontStyle={style}
@@ -180,18 +180,17 @@ class TreeView extends React.Component<TreeViewProps> {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.props.document, key, value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.props.document, key, value, false);
- const layoutDoc = this.props.document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layout_custom)) : undefined;
- const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- TreeView.loadId = doc[Id];
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ EditableView.loadId = doc[Id];
return this.props.addDocument(doc);
})}
OnTab={undoBatch((shift?: boolean) => {
- TreeView.loadId = this.dataDoc[Id];
+ EditableView.loadId = this.dataDoc[Id];
shift ? this.props.outdentDocument?.() : this.props.indentDocument?.();
setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document).
Doc.UnBrushDoc(this.props.document);
Doc.BrushDoc(this.props.document);
- TreeView.loadId = "";
+ EditableView.loadId = "";
}, 0);
})}
/>)
@@ -291,8 +290,8 @@ class TreeView extends React.Component<TreeViewProps> {
const addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] :
DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
- this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
+ this.props.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);
} else {
contentElement = <EditableView
@@ -334,8 +333,8 @@ class TreeView extends React.Component<TreeViewProps> {
{!docs ? (null) :
TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document),
this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
- this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.preventTreeViewOpen,
+ this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
+ this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen,
[...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
@@ -350,6 +349,7 @@ class TreeView extends React.Component<TreeViewProps> {
DataDocument={this.templateDataDoc}
LibraryPath={emptyPath}
renderDepth={this.props.renderDepth + 1}
+ backgroundColor={this.props.backgroundColor}
fitToBox={this.boundsOfCollectionDocument !== undefined}
PanelWidth={this.docWidth}
PanelHeight={this.docHeight}
@@ -386,7 +386,7 @@ class TreeView extends React.Component<TreeViewProps> {
@computed
get renderBullet() {
const checked = this.props.document.type === DocumentType.COL ? undefined : this.props.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined;
- return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "black"), opacity: 0.4 }}>
+ return <div className="bullet" title="view inline" onClick={this.bulletClick} style={{ color: StrCast(this.props.document.color, checked === "unchecked" ? "white" : "inherit"), opacity: 0.4 }}>
{<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />}
</div>;
}
@@ -417,7 +417,7 @@ class TreeView extends React.Component<TreeViewProps> {
return <>
<div className="docContainer" title="click to edit title" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
style={{
- color: this.props.document.isMinimized ? "red" : "black",
+ color: this.props.document.isMinimized ? "red" : "inherit",
background: Doc.IsHighlighted(this.props.document) ? "orange" : Doc.IsBrushed(this.props.document) ? "#06121212" : "0",
fontWeight: this.props.document.searchMatch ? "bold" : undefined,
outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
@@ -458,6 +458,7 @@ class TreeView extends React.Component<TreeViewProps> {
dropAction: dropActionType,
addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean,
pinToPres: (document: Doc) => void,
+ backgroundColor: undefined | ((document: Doc) => string | undefined),
screenToLocalXf: () => Transform,
outerXf: () => { translateX: number, translateY: number },
active: (outsideReaction?: boolean) => boolean,
@@ -465,7 +466,7 @@ class TreeView extends React.Component<TreeViewProps> {
ChromeHeight: undefined | (() => number),
renderDepth: number,
treeViewHideHeaderFields: () => boolean,
- preventTreeViewOpen: boolean,
+ treeViewPreventOpen: boolean,
renderedIds: string[],
libraryPath: Doc[] | undefined,
onCheckedClick: ScriptField | undefined
@@ -563,6 +564,7 @@ class TreeView extends React.Component<TreeViewProps> {
renderDepth={renderDepth}
deleteDoc={remove}
addDocument={addDocument}
+ backgroundColor={backgroundColor}
panelWidth={rowWidth}
panelHeight={rowHeight}
ChromeHeight={ChromeHeight}
@@ -575,7 +577,7 @@ class TreeView extends React.Component<TreeViewProps> {
parentKey={key}
active={active}
treeViewHideHeaderFields={treeViewHideHeaderFields}
- preventTreeViewOpen={preventTreeViewOpen}
+ treeViewPreventOpen={treeViewPreventOpen}
renderedIds={renderedIds} />;
});
}
@@ -602,13 +604,24 @@ export class CollectionTreeView extends CollectionSubView(Document) {
@action
remove = (document: Document): boolean => {
- const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const children = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
if (children.indexOf(document) !== -1) {
children.splice(children.indexOf(document), 1);
return true;
}
return false;
}
+ @action
+ addDoc = (doc: Document, relativeTo: Opt<Doc>, before?: boolean): boolean => {
+ const doAddDoc = () =>
+ Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ if (this.props.Document.resolvedDataDoc instanceof Promise) {
+ this.props.Document.resolvedDataDoc.then(resolved => doAddDoc());
+ } else {
+ doAddDoc();
+ }
+ return true;
+ }
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
@@ -624,7 +637,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
} else {
const layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: (this.props.Document.preventTreeViewOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.preventTreeViewOpen = !this.props.Document.preventTreeViewOpen, icon: "paint-brush" });
+ layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" });
layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Treeview Options ...", subitems: layoutItems, icon: "eye" });
@@ -656,8 +669,8 @@ export class CollectionTreeView extends CollectionSubView(Document) {
const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
heroView.proto!.layout = ImageBox.LayoutString("hero");
- heroView.showTitle = "title";
- heroView.showTitleHover = "titlehover";
+ heroView._showTitle = "title";
+ heroView._showTitleHover = "titlehover";
Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data",
Docs.Create.FontIconDocument({
@@ -701,17 +714,18 @@ export class CollectionTreeView extends CollectionSubView(Document) {
render() {
const dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
- const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false);
+ const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
return !this.childDocs ? (null) : (
<div className="collectionTreeView-dropTarget" id="body"
- style={{ background: StrCast(this.props.Document.backgroundColor, "lightgray"), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
+ style={{ background: this.props.backgroundColor?.(this.props.Document), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
onContextMenu={this.onContextMenu}
onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
{(this.props.Document.treeViewHideTitle ? (null) : <EditableView
contents={this.dataDoc.title}
+ editing={false}
display={"block"}
maxHeight={72}
height={"auto"}
@@ -719,18 +733,17 @@ export class CollectionTreeView extends CollectionSubView(Document) {
SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)}
OnFillDown={undoBatch((value: string) => {
Doc.SetInPlace(this.dataDoc, "title", value, false);
- const layoutDoc = this.props.Document.layout_custom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layout_custom)) : undefined;
- const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- TreeView.loadId = doc[Id];
- Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false);
+ const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
+ EditableView.loadId = doc[Id];
+ this.addDoc(doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
})} />)}
{this.props.Document.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
TreeView.GetChildElements(this.childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove,
- moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.ScreenToLocalTransform,
+ moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform,
this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields),
- BoolCast(this.props.Document.preventTreeViewOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
+ BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick))
}
</ul>
</div >
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index de3e9737f..be971eda6 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -38,6 +38,7 @@ import { CollectionViewBaseChrome } from './CollectionViewChromes';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { List } from '../../../new_fields/List';
+import { SubCollectionViewProps } from './CollectionSubView';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
@@ -133,7 +134,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
addDocument(doc: Doc): boolean {
- const targetDataDoc = this.props.Document.resolvedDataDoc && !this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document[DataSym]);
+ const targetDataDoc = this.props.Document[DataSym];
targetDataDoc[this.props.fieldKey] = new List<Doc>([...DocListCast(targetDataDoc[this.props.fieldKey]), doc]); // DocAddToList may write to targetdataDoc's parent ... we don't want this. should really change GetProto to GetDataDoc and test for resolvedDataDoc there
// Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
@@ -145,8 +146,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
removeDocument(doc: Doc): boolean {
const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- const targetDataDoc = this.props.Document.resolvedDataDoc ? this.props.Document : this.props.Document[DataSym];
- const value = Cast(targetDataDoc[this.props.fieldKey], listSpec(Doc), []);
+ const value = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
@@ -178,7 +178,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
}
private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
- const props = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+ const props: SubCollectionViewProps = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
switch (type) {
case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 050ca8347..d363770bf 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -44,7 +44,9 @@ export interface ViewDefResult {
}
function toLabel(target: FieldResult<Field>) {
if (typeof target === "number" || Number(target)) {
- return Number(target).toFixed(2).toString();
+ const truncated = Number(Number(target).toFixed(0));
+ const precise = Number(Number(target).toFixed(2));
+ return truncated === precise ? Number(target).toFixed(0) : Number(target).toFixed(2);
}
if (target instanceof ObjectField || target instanceof RefField) {
return target[ToString]();
@@ -287,7 +289,7 @@ export function computeTimelineLayout(
groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1, payload: undefined };
+ const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childDocs.filter(c => !filterDocs.includes(c)));
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -303,7 +305,7 @@ export function computeTimelineLayout(
docMap.set(doc, {
type: "doc",
x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
- zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt, payload: undefined
+ zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt / (Math.max(stack, 1)), payload: undefined
});
stacking[stack] = x + pivotAxisWidth;
});
@@ -340,17 +342,17 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 }));
return {
- elements: viewDefsToJSX(extras.concat(groupNames.map(gname => ({
+ elements: viewDefsToJSX(extras.concat(groupNames).map(gname => ({
type: gname.type,
text: gname.text,
x: gname.x * scale,
y: gname.y * scale,
color: gname.color,
width: gname.width === undefined ? undefined : gname.width * scale,
- height: Math.max(fontHeight, (gname.height || 0) * scale),
+ height: gname.height === -1 ? 1 : Math.max(fontHeight, (gname.height || 0) * scale),
fontSize: gname.fontSize,
payload: gname.payload
- }))))
+ })))
};
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 07a5a2c7b..969d6b3c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,13 +4,13 @@ import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrows
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkTool } from "../../../../new_fields/InkField";
import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, BoolCast, StrCast } from "../../../../new_fields/Types";
import { TraceMobx } from "../../../../new_fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -206,6 +206,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
@undoBatch
+ @action
updateClusters(useClusters: boolean) {
this.props.Document.useClusters = useClusters;
this._clusterSets.length = 0;
@@ -243,7 +244,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
}
childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
- childLayouts.map(child => Doc.GetProto(child).clusterStr = child.cluster?.toString());
}
}
@@ -279,16 +279,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
getClusterColor = (doc: Doc) => {
- let clusterColor = "";
+ let clusterColor = this.props.backgroundColor?.(doc);
const cluster = NumCast(doc.cluster);
if (this.Document.useClusters) {
if (this._clusterSets.length <= cluster) {
setTimeout(() => this.updateCluster(doc), 0);
} else {
// choose a cluster color from a palette
- const colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"];
+ const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"];
clusterColor = colors[cluster % colors.length];
- const set = this._clusterSets[cluster] && this._clusterSets[cluster].filter(s => s.backgroundColor && (s.backgroundColor !== s.defaultBackgroundColor));
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
// override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
set && set.filter(s => !s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
set && set.filter(s => s.isBackground).map(s => clusterColor = StrCast(s.backgroundColor));
@@ -697,7 +697,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
- setScaleToZoom = (doc: Doc, scale: number = 0.5) => {
+ setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
this.Document.scale = scale * Math.min(this.props.PanelWidth() / NumCast(doc._width), this.props.PanelHeight() / NumCast(doc._height));
}
@@ -709,6 +709,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; }
@computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+ backgroundHalo = () => BoolCast(this.Document.useClusters);
getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
return {
@@ -728,6 +729,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
ContainingCollectionDoc: this.props.Document,
focus: this.focusDocument,
backgroundColor: this.getClusterColor,
+ backgroundHalo: this.backgroundHalo,
parentActive: this.props.active,
bringToFront: this.bringToFront,
zoomToScale: this.zoomToScale,
@@ -867,6 +869,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
});
return rangeFilteredDocs;
}
+ childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null) as Doc;
get doLayoutComputation() {
const { newPool, computedElementData } = this.doInternalLayoutComputation;
runInAction(() =>
@@ -883,6 +886,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
computedElementData.elements.push({
ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} {...this.getChildDocumentViewProps(pair.layout, pair.data)}
dataProvider={this.childDataProvider}
+ LayoutDoc={this.childLayoutDocFunc}
jitterRotation={NumCast(this.props.Document.jitterRotation)}
fitToBox={this.props.fitToBox || this.props.layoutEngine !== undefined} />,
bounds: this.childDataProvider(pair.layout)
@@ -1072,7 +1076,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
// if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey.
// otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document
- // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale;
return <div className={"collectionfreeformview-container"}
ref={this.createDashEventsTarget}
onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
@@ -1084,7 +1087,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
width: this.contentScaling ? `${100 / this.contentScaling}%` : "",
height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
- {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ?
+ {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
</div>;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 4b0855635..85cda4ecb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -308,8 +308,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
y: bounds.top,
_panX: 0,
_panY: 0,
- backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
- defaultBackgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
+ backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : undefined,
_width: bounds.width,
_height: bounds.height,
_LODdisable: true,
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 7d8de0db4..db5673573 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -207,6 +207,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
{...this.props}
Document={layout}
DataDocument={layout.resolvedDataDoc as Doc}
+ backgroundColor={this.props.backgroundColor}
CollectionDoc={this.props.Document}
PanelWidth={width}
PanelHeight={height}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index ec05443e5..630a178cf 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -208,6 +208,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
{...this.props}
Document={layout}
DataDocument={layout.resolvedDataDoc as Doc}
+ backgroundColor={this.props.backgroundColor}
CollectionDoc={this.props.Document}
PanelWidth={width}
PanelHeight={height}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3bceec45f..53d17b580 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -61,15 +61,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect && !this.props.fitToBox ? this.width / this.nativeWidth : 1;
- clusterColorFunc = (doc: Doc) => this.clusterColor;
panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth());
panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight());
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
.scale(1 / this.contentScaling())
- @computed
- get clusterColor() { return this.props.backgroundColor(this.props.Document); }
focusDoc = (doc: Doc) => this.props.focus(doc, false);
render() {
TraceMobx();
@@ -78,13 +75,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
boxShadow:
this.layoutDoc.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.clusterColor ? (`${this.clusterColor} ${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.props.backgroundHalo?.() ? (`${this.props.backgroundColor?.(this.props.Document)} ${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" : "",
transform: this.transform,
- transition: this.Document.isAnimating ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
+ transition: this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
zIndex: this.ZInd,
@@ -96,7 +93,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
dragDivName={"collectionFreeFormDocumentView-container"}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
- backgroundColor={this.clusterColorFunc}
+ backgroundColor={this.props.backgroundColor}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
/> : <ContentFittingDocumentView {...this.props}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 671f5b96e..387da88f5 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -1,19 +1,17 @@
import React = require("react");
-import { action, computed } from "mobx";
+import { computed } from "mobx";
import { observer } from "mobx-react";
import "react-table/react-table.css";
import { Doc, Opt } from "../../../new_fields/Doc";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
+import { ScriptField } from "../../../new_fields/ScriptField";
import { NumCast, StrCast } from "../../../new_fields/Types";
-import { emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
-import { DragManager } from "../../util/DragManager";
+import { TraceMobx } from "../../../new_fields/util";
+import { emptyFunction, returnOne } from "../../../Utils";
import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
+import { CollectionView } from "../collections/CollectionView";
import '../DocumentDecorations.scss';
import { DocumentView } from "../nodes/DocumentView";
import "./ContentFittingDocumentView.scss";
-import { CollectionView } from "../collections/CollectionView";
-import { TraceMobx } from "../../../new_fields/util";
interface ContentFittingDocumentViewProps {
Document?: Doc;
@@ -29,6 +27,7 @@ interface ContentFittingDocumentViewProps {
CollectionView?: CollectionView;
CollectionDoc?: Doc;
onClick?: ScriptField;
+ backgroundColor?: (doc: Doc) => string | undefined;
getTransform: () => Transform;
addDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
@@ -55,20 +54,6 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
}
private contentScaling = () => this.scaling;
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- const docDragData = de.complete.docDragData;
- if (docDragData) {
- this.props.childDocs && this.props.childDocs.map(otherdoc => {
- const target = Doc.GetProto(otherdoc);
- target.layout = ComputedField.MakeFunction("this.image_data[0]");
- target.layout_custom = Doc.MakeDelegate(docDragData.draggedDocuments[0]);
- });
- e.stopPropagation();
- }
- return true;
- }
private PanelWidth = () => this.panelWidth;
private PanelHeight = () => this.panelHeight;;
@@ -102,6 +87,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
LibraryPath={this.props.LibraryPath}
fitToBox={this.props.fitToBox}
onClick={this.props.onClick}
+ backgroundColor={this.props.backgroundColor}
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
@@ -117,7 +103,6 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
focus={this.props.focus || emptyFunction}
- backgroundColor={returnEmptyString}
bringToFront={emptyFunction}
dontRegisterView={this.props.dontRegisterView}
zoomToScale={emptyFunction}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 95858af4c..52928e8cb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, runInAction } from "mobx";
+import { action, computed, runInAction, observable } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
@@ -77,7 +77,8 @@ export interface DocumentViewProps {
addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
zoomToScale: (scale: number) => void;
- backgroundColor: (doc: Doc) => string | undefined;
+ backgroundHalo?: () => boolean;
+ backgroundColor?: (doc: Doc) => string | undefined;
getScale: () => number;
animateBetweenIcon?: (maximize: boolean, target: number[]) => void;
ChromeHeight?: () => number;
@@ -92,7 +93,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _downY: number = 0;
private _lastTap: number = 0;
private _doubleTap = false;
- private _hitTemplateDrag = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _dropDisposer?: DragManager.DragDropDisposer;
private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
@@ -196,14 +196,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
!this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1);
}
- startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) {
+ startDragging(x: number, y: number, dropAction: dropActionType) {
if (this._mainCont.current) {
const dragData = new DragManager.DocumentDragData([this.props.Document]);
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument;
- dragData.applyAsTemplate = applyAsTemplate;
dragData.dragDivName = this.props.dragDivName;
this.props.Document.sourceContext = this.props.ContainingCollectionDoc; // bcz: !! shouldn't need this ... use search find the document's context dynamically
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
@@ -232,7 +231,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.stopPropagation();
e.preventDefault();
if (e.key === "†" || e.key === "t") {
- if (!StrCast(this.layoutDoc.showTitle)) this.layoutDoc.showTitle = "title";
+ if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = "title";
if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
{
@@ -257,8 +256,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let preventDefault = true;
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
const fullScreenAlias = Doc.MakeAlias(this.props.Document);
- if (StrCast(fullScreenAlias.layoutKey) !== "layout_custom" && fullScreenAlias.layout_custom !== undefined) {
- fullScreenAlias.layoutKey = "layout_custom";
+ if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
+ fullScreenAlias.layoutKey = "layout_fullScreen";
}
this.props.addDocTab(fullScreenAlias, undefined, "inTab");
SelectionManager.DeselectAll();
@@ -297,12 +296,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._downX = touch.clientX;
this._downY = touch.clientY;
if (!e.nativeEvent.cancelBubble) {
- this._hitTemplateDrag = false;
- for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
- if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
- this._hitTemplateDrag = true;
- }
- }
if ((this.active || this.Document.onDragStart || this.Document.onClick) && !e.ctrlKey && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation();
this.removeMoveListeners();
this.addMoveListeners();
@@ -323,7 +316,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) {
if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) {
this.cleanUpInteractions();
- this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
+ this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -434,14 +427,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) {
this._downX = e.clientX;
this._downY = e.clientY;
- this._hitTemplateDrag = false;
- // this whole section needs to move somewhere else. We're trying to initiate a special "template" drag where
- // this document is the template and we apply it to whatever we drop it on.
- for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
- if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
- this._hitTemplateDrag = true;
- }
- }
if ((this.active || this.Document.onDragStart || this.onClickHandler) &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
@@ -470,7 +455,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- this.startDragging(this._downX, this._downY, this.props.ContainingCollectionDoc?.childDropAction ? this.props.ContainingCollectionDoc?.childDropAction : this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
+ this.startDragging(this._downX, this._downY, this.props.ContainingCollectionDoc?.childDropAction ? this.props.ContainingCollectionDoc?.childDropAction : this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
}
}
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -557,17 +542,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@undoBatch
- makeSelBtnClicked = (): void => {
- if (this.Document.isButton || this.Document.onClick || this.Document.ignoreClick) {
- this.Document.isButton = false;
- this.Document.ignoreClick = false;
- this.Document.onClick = undefined;
- } else {
- this.props.Document.isButton = "Selector";
- }
- }
-
- @undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
if (de.complete.annoDragData) {
@@ -578,10 +552,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc },
`Link from ${StrCast(de.complete.annoDragData.annotationDocument.title)}`);
}
- if (de.complete.docDragData && de.complete.docDragData.applyAsTemplate) {
- Doc.ApplyTemplateTo(de.complete.docDragData.draggedDocuments[0], this.props.Document, "layout_custom", undefined);
- e.stopPropagation();
- }
if (de.complete.linkDragData) {
e.stopPropagation();
// const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
@@ -694,10 +664,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const existingOnClick = ContextMenu.Instance.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(this)"), icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
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.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" });
- onClicks.push({ description: this.props.Document.isButton ? "Remove Select Link Behavior" : "Select Link", event: this.makeSelBtnClicked, icon: "concierge-bell" });
onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
!existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
@@ -804,19 +773,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
- // does Document set a layout prop
- setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)] && this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)];
- // get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise.
- getLayoutPropStr = (prop: string) => StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
- getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]);
-
isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
chromeHeight = () => {
- const showTitle = StrCast(this.layoutDoc.showTitle);
- const showTitleHover = StrCast(this.layoutDoc.showTitleHover);
- return (showTitle && !showTitleHover ? 0 : 0) + 1;
+ return 1;
}
@computed get finalLayoutKey() {
@@ -874,9 +835,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get innards() {
TraceMobx();
- const showTitle = StrCast(this.getLayoutPropStr("showTitle"));
- const showTitleHover = StrCast(this.getLayoutPropStr("showTitleHover"));
- const showCaption = this.getLayoutPropStr("showCaption");
+ const showTitle = StrCast(this.layoutDoc._showTitle);
+ const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
+ const showCaption = StrCast(this.layoutDoc._showCaption);
const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
const searchHighlight = (!this.Document.searchFields ? (null) :
<div className="documentView-searchHighlight">
@@ -931,16 +892,25 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return (this.Document.isBackground && !this.isSelected()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
}
+ @observable _animate = 0;
+ switchViews = action((custom: boolean, view: string) => {
+ SelectionManager.SetIsDragging(true);
+ this._animate = 0.1;
+ setTimeout(action(() => {
+ this.setCustomView(custom, view);
+ this._animate = 1;
+ setTimeout(action(() => {
+ this._animate = 0;
+ SelectionManager.SetIsDragging(false);
+ }), 400);
+ }), 400);
+ });
+
render() {
if (!(this.props.Document instanceof Doc)) return (null);
- const colorSet = this.setsLayoutProp("backgroundColor");
- const clusterCol = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.clusterOverridesDefaultBackground;
- const backgroundColor = (clusterCol && !colorSet) ?
- this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) :
- StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document);
-
+ const backgroundColor = StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor?.(this.Document);
const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
- const borderRounding = this.getLayoutPropStr("borderRounding");
+ const borderRounding = this.layoutDoc.borderRounding;
const localScale = fullDegree;
const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
@@ -951,9 +921,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)}
style={{
- transition: this.Document.isAnimating ? ".5s linear" : StrCast(this.Document.transition),
+ transformOrigin: this._animate ? "center center" : undefined,
+ transform: this._animate ? `scale(${this._animate})` : undefined,
+ transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
pointerEvents: this.ignorePointerEvents ? "none" : "all",
- color: StrCast(this.Document.color),
+ color: StrCast(this.Document.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
@@ -971,4 +943,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
-Scripting.addGlobal(function toggleDetail(doc: any) { doc.layoutKey = StrCast(doc.layoutKey, "layout") === "layout" ? "layout_custom" : "layout"; }); \ No newline at end of file
+Scripting.addGlobal(function toggleDetail(doc: any, layoutKey: string) {
+ const dv = DocumentManager.Instance.getDocumentView(doc);
+ if (dv?.props.Document.layoutKey === layoutKey) dv?.switchViews(false, "");
+ else dv?.switchViews(true, layoutKey.replace("layout_", ""));
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 8250f41f3..00f00dd52 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -3,14 +3,13 @@ import { computed } from "mobx";
import { observer } from "mobx-react";
import { DateField } from "../../../new_fields/DateField";
import { Doc, FieldResult, Opt } from "../../../new_fields/Doc";
-import { IconField } from "../../../new_fields/IconField";
import { List } from "../../../new_fields/List";
-import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { AudioField, VideoField } from "../../../new_fields/URLField";
import { Transform } from "../../util/Transform";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
import { VideoBox } from "./VideoBox";
-import { ScriptField } from "../../../new_fields/ScriptField";
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -34,6 +33,7 @@ export interface FieldViewProps {
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc) => boolean;
moveDocument?: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
+ backgroundColor?: (document: Doc) => string | undefined;
ScreenToLocalTransform: () => Transform;
active: (outsideReaction?: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 7fbee8881..3c64b3974 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -186,7 +186,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000)));
this._applyingChange = true;
if (!this.props.Document._textTemplate || Doc.GetProto(this.props.Document) === this.dataDoc) {
- this.dataDoc[this.props.fieldKey + "-lastModified"] && (this.dataDoc[this.props.fieldKey + "-backgroundColor"] = "lightGray");
this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), state.doc.textBetween(0, state.doc.content.size, "\n\n"));
}
@@ -250,17 +249,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
e.stopPropagation();
}
- // apply as template when dragging with Meta
- } else if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.metaKey) {
- draggedDoc.isTemplateDoc = true;
- let newLayout = Doc.Layout(draggedDoc);
- if (typeof (draggedDoc.layout) === "string") {
- newLayout = Doc.MakeDelegate(draggedDoc);
- newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={'${this.props.fieldKey}'}`);
- }
- this.Document.layout_custom = newLayout;
- this.Document.layoutKey = "layout_custom";
- e.stopPropagation();
// embed document when dragging with a userDropAction or an embedDoc flag set
} else if (de.complete.docDragData.userDropAction || de.complete.docDragData.embedDoc) {
const target = de.complete.docDragData.droppedDocuments[0];
@@ -1086,7 +1074,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
<div className={`formattedTextBox-cont`} ref={this._ref}
style={{
height: this.layoutDoc._autoHeight ? "max-content" : undefined,
- background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : undefined,
+ background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"]),
opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.hideOnLeave ? "white" : "inherit",
pointerEvents: interactive ? "none" : "all",
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index c0e102195..364bce7a8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -92,10 +92,11 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
} else if (de.altKey || !this.dataDoc[this.props.fieldKey]) {
const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
const targetField = Doc.LayoutFieldKey(layoutDoc);
- if (layoutDoc?.[DataSym][targetField] instanceof ImageField) {
- this.dataDoc[this.props.fieldKey] = ObjectField.MakeCopy(layoutDoc[DataSym][targetField] as ImageField);
- this.dataDoc[this.props.fieldKey + "-nativeWidth"] = NumCast(layoutDoc[DataSym][targetField + "-nativeWidth"]);
- this.dataDoc[this.props.fieldKey + "-nativeHeight"] = NumCast(layoutDoc[DataSym][targetField + "-nativeHeight"]);
+ const targetDoc = layoutDoc[DataSym];
+ if (targetDoc[targetField] instanceof ImageField) {
+ this.dataDoc[this.props.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
+ this.dataDoc[this.props.fieldKey + "-nativeWidth"] = NumCast(targetDoc[targetField + "-nativeWidth"]);
+ this.dataDoc[this.props.fieldKey + "-nativeHeight"] = NumCast(targeDoc[targetField + "-nativeHeight"]);
e.stopPropagation();
}
}
diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss
index 2f35eeca2..41307a808 100644
--- a/src/client/views/webcam/DashWebRTCVideo.scss
+++ b/src/client/views/webcam/DashWebRTCVideo.scss
@@ -4,7 +4,7 @@
background: whitesmoke;
color: grey;
border-radius: 15px;
- box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw;
+ box-shadow: #9c9396 0.2vw 0.2vw 0.4vw;
border: solid #BBBBBBBB 5px;
pointer-events: all;
display: flex;
@@ -18,29 +18,66 @@
letter-spacing: 2px;
font-size: 16px;
width: 100%;
+ margin-top: 20px;
+ }
+
+ .videoContainer {
+ position: relative;
+ width: calc(100% - 20px);
+ height: 100%;
+ /* border: 10px solid red; */
+ margin-left: 10px;
+ }
+
+ .buttonContainer {
+ display: flex;
+ width: calc(100% - 20px);
+ height: 50px;
+ justify-content: center;
+ text-align: center;
+ /* border: 1px solid black; */
+ margin-left: 10px;
+ margin-top: 0;
+ margin-bottom: 15px;
}
#roomName {
outline: none;
border-radius: inherit;
border: 1px solid #BBBBBBBB;
+ margin: 10px;
+ padding: 10px;
}
.side {
width: 25%;
height: 20%;
position: absolute;
- top: 65%;
+ /* top: 65%; */
z-index: 2;
- right: 5%;
+ right: 0px;
+ bottom: 18px;
}
.main {
position: absolute;
- width: 95%;
- height: 75%;
- top: 20%;
+ width: 100%;
+ height: 100%;
+ /* top: 20%; */
+ align-self: center;
+ }
+
+ .videoButtons {
+ border-radius: 50%;
+ height: 30px;
+ width: 30px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ justify-self: center;
align-self: center;
+ margin: 5px;
+ border: 1px solid black;
}
} \ No newline at end of file
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index cbf75f708..9c339e986 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -8,7 +8,14 @@ import { InkingControl } from "../InkingControl";
import "../../views/nodes/WebBox.scss";
import "./DashWebRTCVideo.scss";
import adapter from 'webrtc-adapter';
-import { initialize, hangup } from "./WebCamLogic";
+import { initialize, hangup, refreshVideos } from "./WebCamLogic";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
+import { faSync, faPhoneSlash } from "@fortawesome/free-solid-svg-icons";
+
+library.add(faSync);
+library.add(faPhoneSlash);
+
/**
* This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
@@ -19,14 +26,6 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
private roomText: HTMLInputElement | undefined;
@observable remoteVideoAdded: boolean = false;
- componentDidMount() {
- DocumentDecorations.Instance.addCloseCall(this.closeConnection);
- }
-
- closeConnection: CloseCall = () => {
- hangup();
- }
-
@action
changeUILook = () => {
this.remoteVideoAdded = true;
@@ -47,34 +46,30 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DashWebRTCVideo, fieldKey); }
- _ignore = 0;
- onPreWheel = (e: React.WheelEvent) => {
- this._ignore = e.timeStamp;
- }
- onPrePointer = (e: React.PointerEvent) => {
- this._ignore = e.timeStamp;
- }
- onPostPointer = (e: React.PointerEvent) => {
- if (this._ignore !== e.timeStamp) {
- e.stopPropagation();
- }
+ @action
+ onClickRefresh = () => {
+ refreshVideos();
}
- onPostWheel = (e: React.WheelEvent) => {
- if (this._ignore !== e.timeStamp) {
- e.stopPropagation();
- }
+
+ onClickHangUp = () => {
+ hangup();
}
render() {
let content =
- <div className="webcam-cont" style={{ width: "100%", height: "100%" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
+ <div className="webcam-cont" style={{ width: "100%", height: "100%" }}>
<div className="webcam-header">DashWebRTC</div>
<input id="roomName" type="text" placeholder="Enter room name" ref={(e) => this.roomText = e!} onKeyDown={this.onEnterKeyDown} />
- <video id="localVideo" className={"RTCVideo" + (this.remoteVideoAdded ? " side" : " main")} autoPlay playsInline muted ref={(e) => {
- }}></video>
- <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={(e) => {
- }}></video>
-
+ <div className="videoContainer">
+ <video id="localVideo" className={"RTCVideo" + (this.remoteVideoAdded ? " side" : " main")} autoPlay playsInline muted ref={(e) => {
+ }}></video>
+ <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={(e) => {
+ }}></video>
+ </div>
+ <div className="buttonContainer">
+ <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash} color="white" /></div>
+ <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync} color="white" /></div>
+ </div>
</div >;
let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
@@ -86,7 +81,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
<div className={classname} >
{content}
</div>
- {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+ {!frozen ? (null) : <div className="webBox-overlay" />}
</>);
}
diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js
index 3dfb82465..f542fb983 100644
--- a/src/client/views/webcam/WebCamLogic.js
+++ b/src/client/views/webcam/WebCamLogic.js
@@ -277,4 +277,16 @@ function handleRemoteHangup() {
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message, room);
-}; \ No newline at end of file
+};
+
+export function refreshVideos() {
+ var localVideo = document.querySelector('#localVideo');
+ var remoteVideo = document.querySelector('#remoteVideo');
+ if (localVideo) {
+ localVideo.srcObject = localStream;
+ }
+ if (remoteVideo) {
+ remoteVideo.srcObject = remoteStream;
+ }
+
+} \ No newline at end of file