aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgeireann <geireann.lindfield@gmail.com>2021-09-12 10:03:00 -0400
committergeireann <geireann.lindfield@gmail.com>2021-09-12 10:03:00 -0400
commit939c7d092635e47e081aa8f047b73af36058f3b7 (patch)
tree7b5d4a9dce0c66ce3e2bd62755f536d41db48181
parentf20dfa857e1651ced1eb2303ea8a8462483fbc4c (diff)
parentd9419c89ccdac7435c87b27a55486b7a5980ae29 (diff)
Merge branch 'menu_updates_geireann' of https://github.com/brown-dash/Dash-Web into menu_updates_geireann
-rw-r--r--src/client/documents/Documents.ts21
-rw-r--r--src/client/util/CurrentUserUtils.ts35
-rw-r--r--src/client/util/DocumentManager.ts51
-rw-r--r--src/client/util/HypothesisUtils.ts2
-rw-r--r--src/client/util/Scripting.ts8
-rw-r--r--src/client/util/SelectionManager.ts12
-rw-r--r--src/client/views/DocComponent.tsx2
-rw-r--r--src/client/views/GlobalKeyHandler.ts4
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx2
-rw-r--r--src/client/views/SidebarAnnos.tsx25
-rw-r--r--src/client/views/StyleProvider.tsx9
-rw-r--r--src/client/views/collections/CollectionMenu.tsx33
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx15
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx8
-rw-r--r--src/client/views/collections/CollectionView.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx21
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx5
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss (renamed from src/client/views/collections/collectionLinearView/CollectionLinearView.scss)0
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx (renamed from src/client/views/collections/collectionLinearView/CollectionLinearView.tsx)119
-rw-r--r--src/client/views/collections/collectionLinear/index.ts (renamed from src/client/views/collections/collectionLinearView/index.ts)0
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.tsx66
-rw-r--r--src/client/views/nodes/FilterBox.tsx11
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx1
-rw-r--r--src/client/views/nodes/PDFBox.tsx20
-rw-r--r--src/client/views/nodes/WebBox.tsx14
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx92
-rw-r--r--src/client/views/nodes/button/textButton/TextButton.tsx2
-rw-r--r--src/client/views/nodes/button/textButton/index.ts2
-rw-r--r--src/client/views/nodes/button/toggleButton/index.ts2
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss17
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx63
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.scss1
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx20
-rw-r--r--src/client/views/pdf/AnchorMenu.scss29
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx13
-rw-r--r--src/client/views/pdf/PDFViewer.tsx5
-rw-r--r--src/fields/Doc.ts2
43 files changed, 438 insertions, 327 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bd247bd01..f50f306a3 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -157,6 +157,7 @@ export class DocumentOptions {
z?: number; // whether document is in overlay (1) or not (0 or undefined)
author?: string;
_layoutKey?: string;
+ unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
type?: string;
title?: string;
"acl-Public"?: string; // public permissions
@@ -277,7 +278,7 @@ export class DocumentOptions {
// Action Button
buttonMenu?: boolean; // whether a action button should be displayed
buttonMenuDoc?: Doc;
- explainer?:string;
+ explainer?: string;
treeViewOpenIsTransient?: boolean; // ignores the treeViewOpen Doc flag, allowing a treeViewItem's expand/collapse state to be independent of other views of the same document in the same or any other tree view
_treeViewOpen?: boolean; // whether this document is expanded in a tree view (note: need _ and regular versions since this can be specified for both proto and layout docs)
@@ -958,12 +959,16 @@ export namespace DocUtils {
// facets that have a check next to them
const checks = Object.keys(facet).filter(value => facet[value] === "check");
+ // metadata facets that exist
+ const exists = Object.keys(facet).filter(value => facet[value] === "exists");
+
// facets that have an x next to them
const xs = Object.keys(facet).filter(value => facet[value] === "x");
- if (!xs.length && !checks.length && !matches.length) return true;
+ if (!exists.length && !xs.length && !checks.length && !matches.length) return true;
const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
+ const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
const satisfiesMatchFacets = !matches.length ? true : matches.some(value => {
if (facetKey.startsWith("*")) { // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc
const allKeys = Array.from(Object.keys(d));
@@ -975,11 +980,11 @@ export namespace DocUtils {
});
// if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") {
- if (satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+ if (satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
}
// if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
else {
- if (!satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+ if (!satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
}
}
@@ -1188,7 +1193,7 @@ export namespace DocUtils {
})) as ContextMenuProps[],
icon: "sticky-note"
});
- const math:ContextMenuProps = ({
+ const math: ContextMenuProps = ({
description: ":Math", event: () => {
const created = Docs.Create.EquationDocument();
if (created) {
@@ -1202,7 +1207,7 @@ export namespace DocUtils {
}
}, icon: "calculator"
});
- const documentList:ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
+ const documentList: ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
description: ":" + StrCast(dragDoc.title),
event: undoBatch((args: { x: number, y: number }) => {
const newDoc = Doc.copyDragFactory(dragDoc);
@@ -1215,8 +1220,8 @@ export namespace DocUtils {
}
}),
icon: Doc.toIcon(dragDoc),
- })) as ContextMenuProps[];
- documentList.push(math)
+ })) as ContextMenuProps[];
+ documentList.push(math);
ContextMenu.Instance.addItem({
description: "Create document",
subitems: documentList,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 760949b57..e3db75c42 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -22,6 +22,7 @@ import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMu
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
import { Colors } from "../views/global/globalEnums";
import { MainView } from "../views/MainView";
+import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { LabelBox } from "../views/nodes/LabelBox";
import { OverlayView } from "../views/OverlayView";
@@ -37,8 +38,6 @@ import { ColorScheme } from "./SettingsManager";
import { SharingManager } from "./SharingManager";
import { SnappingManager } from "./SnappingManager";
import { UndoManager } from "./UndoManager";
-import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
-import { IconName } from "@fortawesome/fontawesome-svg-core";
interface Button {
title?: string;
@@ -447,16 +446,16 @@ export class CurrentUserUtils {
storedMarks: []
};
const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
- title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all",
+ title: "text", version: headerViewVersion, _height: 70, _headerPointerEvents: "all",
_headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true,
cloneFieldFilter: new List<string>(["system"])
}, "header");
const headerBtnHgt = 10;
headerTemplate[DataSym].layout =
"<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
- ` <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight}px)'/>` +
- " <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize}px' height='{(this._headerHeight||1)}px' background='{this._headerColor ||this.target.mySharedDocs.userColor||`lightGray`}' />" +
- ` <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(1,this._height-30),this._headerHeight===1?50:1)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’} >Metadata</HTMLdiv>` +
+ ` <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
+ " <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' background='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
+ ` <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’} >Metadata</HTMLdiv>` +
"</HTMLdiv>";
// "<div style={'height:100%'}>" +
@@ -562,7 +561,7 @@ export class CurrentUserUtils {
if (dragCreatorSet === undefined) {
doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
- _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true,
+ _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
}));
} else {
@@ -834,7 +833,7 @@ export class CurrentUserUtils {
await doc.myTrails;
if (doc.myTrails === undefined) {
const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
- const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
+ const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
title: "My Trails", _showTitle: "title", _height: 100,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -854,10 +853,10 @@ export class CurrentUserUtils {
if (doc.myFilesystem === undefined) {
doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
// doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
- const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
- const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "Create new folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+ const newFolder = ScriptField.MakeFunction(`doc.makeFolder()`, { doc: doc.myFilesystem })!;
+ const newFolderButton: Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "Create new folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
- title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+ title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
@@ -874,7 +873,7 @@ export class CurrentUserUtils {
static setupRecentlyClosedDocs(doc: Doc) {
if (doc.myRecentlyClosedDocs === undefined) {
const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
- const clearDocsButton:Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
+ const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -1053,6 +1052,10 @@ export class CurrentUserUtils {
title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
script: "setBackgroundColor", hidden: 'selectedDocumentType()'
}, // Only when a document is selected
+ {
+ title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
+ script: "setHeaderColor", hidden: 'selectedDocumentType()',
+ }, // Only when a document is selected
{ title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
// { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected
{ title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
@@ -1138,9 +1141,9 @@ export class CurrentUserUtils {
icon,
toolTip,
script,
- btnType: btnType,
+ btnType,
btnList: new List<string>(list),
- ignoreClick: ignoreClick,
+ ignoreClick,
_stayInCollection: true,
_hideContextMenu: true,
_lockedPosition: true,
@@ -1553,6 +1556,8 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
view && SelectionManager.SelectView(view, false);
}
});
+Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); },
+ "document containing all shared Docs");
Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
"is Dash in novice mode");
Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
@@ -1616,7 +1621,7 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
return [];
});
Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
- let selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
if (selected && checkParent) {
const parentDoc: Doc = Cast(selected.context, Doc, null);
selected = parentDoc;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index cb0ee411c..9e190ad02 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,19 +1,21 @@
import { action, observable, runInAction } from 'mobx';
import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
import { LightboxView } from '../views/LightboxView';
import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
+import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
import { Scripting } from './Scripting';
export class DocumentManager {
//global holds all of the nodes (regardless of which collection they're in)
@observable public DocumentViews: DocumentView[] = [];
+ @observable public LinkAnchorBoxViews: DocumentView[] = [];
@observable public RecordingEvent = 0;
@observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
@@ -25,24 +27,41 @@ export class DocumentManager {
@action
public AddView = (view: DocumentView) => {
- DocListCast(view.rootDoc.links).forEach(link => {
- const whichOtherAnchor = view.props.LayoutTemplateString?.includes("anchor2") ? "anchor1" : "anchor2";
- const otherDoc = link && (link[whichOtherAnchor] as Doc);
- const otherDocAnno = DocumentType.MARKER === otherDoc?.type ? otherDoc.annotationOn as Doc : undefined;
- otherDoc && DocumentManager.Instance.DocumentViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, otherDoc) || Doc.AreProtosEqual(dv.rootDoc, otherDocAnno)).
- forEach(otherView => {
- if (otherView.rootDoc.type !== DocumentType.LINK || otherView.props.LayoutTemplateString !== view.props.LayoutTemplateString) {
- this.LinkedDocumentViews.push({ a: whichOtherAnchor === "anchor1" ? otherView : view, b: whichOtherAnchor === "anchor1" ? view : otherView, l: link });
- }
- });
- });
- this.DocumentViews.push(view);
+ //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
+ if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+ const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1";
+ DocListCast(view.rootDoc.links).forEach(link => {
+ this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).
+ forEach(otherView => this.LinkedDocumentViews.push(
+ {
+ a: viewAnchorIndex === "anchor2" ? otherView : view,
+ b: viewAnchorIndex === "anchor2" ? view : otherView,
+ l: link
+ })
+ );
+ });
+ this.LinkAnchorBoxViews.push(view);
+ // this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
+ // view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString));
+ } else {
+ this.DocumentViews.push(view);
+ }
}
public RemoveView = action((view: DocumentView) => {
- const index = this.DocumentViews.indexOf(view);
- index !== -1 && this.DocumentViews.splice(index, 1);
+ this.LinkedDocumentViews.slice().forEach(action(pair => {
+ if (pair.a === view || pair.b === view) {
+ const li = this.LinkedDocumentViews.indexOf(pair);
+ li !== -1 && this.LinkedDocumentViews.splice(li, 1);
+ }
+ }));
- this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null));
+ if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+ const index = this.LinkAnchorBoxViews.indexOf(view);
+ this.LinkAnchorBoxViews.splice(index, 1);
+ } else {
+ const index = this.DocumentViews.indexOf(view);
+ index !== -1 && this.DocumentViews.splice(index, 1);
+ }
});
//gets all views
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 635673025..e910a9118 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -29,7 +29,7 @@ export namespace Hypothesis {
* Search for a WebDocument whose url field matches the given uri, return undefined if not found
*/
export const findWebDoc = async (uri: string) => {
- const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
+ const currentDoc = SelectionManager.Docs().lastElement();
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index efee37419..40b94024e 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -171,7 +171,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
if (!options.editable) {
batch = Doc.MakeReadOnly();
}
-
+
const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
if (batch) {
batch.end();
@@ -316,9 +316,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
}
const paramString = paramList.join(", ");
- const funcScript = `(function(${paramString})${requiredType ? `: ${requiredType}` : ''} {
- ${addReturn ? `return ${script};` : `return ${script};`}
- })`;
+ const body = addReturn ? `return ${script};` : `return ${script};`;
+ const reqTypes = requiredType ? `: ${requiredType}` : '';
+ const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
host.writeFile("file.ts", funcScript);
if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0cfaebbf2..bac13373c 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -11,7 +11,7 @@ export namespace SelectionManager {
class Manager {
@observable IsDragging: boolean = false;
- SelectedViews: ObservableMap<DocumentView, boolean> = new ObservableMap();
+ SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
@observable SelectedSchemaDocument: Doc | undefined;
@observable SelectedSchemaCollection: CollectionSchemaView | undefined;
@@ -28,19 +28,18 @@ export namespace SelectionManager {
this.DeselectAll();
}
- manager.SelectedViews.set(docView, true);
+ manager.SelectedViews.set(docView, docView.rootDoc);
docView.props.whenChildContentsActiveChanged(true);
} else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) {
Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
manager.SelectedSchemaDocument = undefined;
manager.SelectedSchemaCollection = undefined;
manager.SelectedViews.clear();
- manager.SelectedViews.set(docView, true);
+ manager.SelectedViews.set(docView, docView.rootDoc);
}
}
@action
DeselectView(docView: DocumentView): void {
-
if (manager.SelectedViews.get(docView)) {
manager.SelectedViews.delete(docView);
docView.props.whenChildContentsActiveChanged(false);
@@ -92,7 +91,7 @@ export namespace SelectionManager {
}
export function Views(): Array<DocumentView> {
- return Array.from(manager.SelectedViews.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
+ return Array.from(manager.SelectedViews.keys()).filter(dv => manager.SelectedViews.get(dv)?._viewType !== CollectionViewType.Docking);
}
export function SelectedSchemaDoc(): Doc | undefined {
return manager.SelectedSchemaDocument;
@@ -100,4 +99,7 @@ export namespace SelectionManager {
export function SelectedSchemaCollection(): CollectionSchemaView | undefined {
return manager.SelectedSchemaCollection;
}
+ export function Docs(): Doc[] {
+ return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
+ }
} \ No newline at end of file
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 33dff9da5..cb36f4270 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -115,7 +115,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
const style: { [key: string]: any } = {};
const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value
- return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || "";
+ return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? "";
};
divKeys.map((prop: string) => {
const p = (this.props as any)[prop];
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 0127d3080..f66c9c788 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -229,8 +229,8 @@ export class KeyManager {
}
break;
case "o":
- const target = SelectionManager.Views()[0];
- target && CollectionDockingView.OpenFullScreen(target.props.Document);
+ const target = SelectionManager.Docs().lastElement();
+ target && CollectionDockingView.OpenFullScreen(target);
break;
case "r":
preventDefault = false;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 809c63a45..68ec97523 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,7 +32,7 @@ import { Transform } from '../util/Transform';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
-import { CollectionLinearView } from './collections/CollectionLinearView';
+import { CollectionLinearView } from './collections/collectionLinear';
import { CollectionMenu } from './collections/CollectionMenu';
import { CollectionViewType } from './collections/CollectionView';
import "./collections/TreeView.scss";
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 9fe2122e6..dd737dc9d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -156,7 +156,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
["toggleDetail", "Toggle Detail"],
["linkInPlace", "Follow Link"],
["linkOnRight", "Open Link on Right"]
- ]
+ ];
const currentSelection = this.selectedDoc.onClickBehavior;
// Get items to place into the list
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 09e12443b..ab9022a84 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -366,7 +366,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
<div className="expansion-button" onPointerDown={() => {
if (this.selectedDocumentView || this.selectedDoc) {
- SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc);
+ SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
}
}}>
<FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 01b79ffd2..659eb86d1 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -38,10 +38,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
// this.props.dataDoc[this.sidebarKey] = new List<Doc>(); // bcz: can't do this here. it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared
}
_stackRef = React.createRef<CollectionStackingView>();
- @computed get allHashtags() {
+ @computed get allMetadata() {
const keys = new Set<string>();
DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys.keys()).filter(key => key[0]).filter(key => !key.startsWith("_") && (key[0] === "#" || key[0] === key[0].toUpperCase())).sort();
+ return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] !== "_" && (key[0] === key[0].toUpperCase())).sort();
}
@computed get allUsers() {
const keys = new Set<string>();
@@ -59,7 +59,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
});
FormattedTextBox.SelectOnLoad = target[Id];
FormattedTextBox.DontSelectInitialText = true;
- this.allHashtags.map(tag => target[tag] = tag);
+ this.allMetadata.map(tag => target[tag] = tag);
DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup", "annotation");
this.addDocument(target);
this._stackRef.current?.focusDocument(target);
@@ -83,13 +83,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-
- sidebarStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
- if (property === StyleProp.ShowTitle) {
- return doc === this.props.rootDoc ? undefined : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title");
- }
- return this.props.styleProvider?.(doc, props, property);
- }
+ showTitle = () => "title";
setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
render() {
const renderTag = (tag: string) => {
@@ -99,6 +93,13 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
{tag}
</div>;
};
+ const renderMeta = (tag: string) => {
+ const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:exists`);
+ return <div key={tag} className={`sidebarAnnos-filterTag${active ? "-active" : ""}`}
+ onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "exists", true, this.sidebarKey, e.shiftKey)}>
+ {tag}
+ </div>;
+ };
const renderUsers = (user: string) => {
const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`author:${user}:check`);
return <div key={user} className={`sidebarAnnos-filterUser${active ? "-active" : ""}`}
@@ -117,7 +118,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
<div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }}
onWheel={e => e.stopPropagation()}>
{this.allUsers.map(renderUsers)}
- {this.allHashtags.map(renderTag)}
+ {this.allMetadata.map(renderMeta)}
</div>
<div style={{ width: "100%", height: this.panelHeight(), position: "relative" }}>
<CollectionStackingView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} ref={this._stackRef}
@@ -125,13 +126,13 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
NativeHeight={returnZero}
PanelHeight={this.panelHeight}
PanelWidth={this.panelWidth}
- styleProvider={this.sidebarStyleProvider}
docFilters={this.docFilters}
scaleField={this.sidebarKey + "-scale"}
setHeight={this.setHeightCallback}
isAnnotationOverlay={false}
select={emptyFunction}
scaling={returnOne}
+ childShowTitle={this.showTitle}
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
childHideDecorationTitle={returnTrue}
removeDocument={this.removeDocument}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e528e84e3..3c88a4830 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -93,9 +93,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
case StyleProp.FontSize: return StrCast(doc?.[fieldKey + "fontSize"]);
- case StyleProp.ShowTitle: return doc && !doc.presentationTargetDoc && StrCast(doc._showTitle,
- !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
- (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
+ case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
+ StrCast(doc._showTitle,
+ props?.showTitle?.() ||
+ (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
+ (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
+ "author;creationDate") : "")) || "");
case StyleProp.Color:
if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 3d37a9df8..aefa1ec3d 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
-import { action, computed, Lambda, observable, reaction, runInAction } from "mobx";
+import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { ColorState } from "react-color";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
@@ -36,7 +36,7 @@ import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
import { PresBox } from "../nodes/trails/PresBox";
import { DefaultStyleProvider } from "../StyleProvider";
import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionLinearView } from "./CollectionLinearView";
+import { CollectionLinearView } from "./collectionLinear";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
@@ -62,7 +62,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
- (doc) => doc && this.SetSelection(doc));
+ view => view && this.SetSelection(view));
}
@action
@@ -93,7 +93,11 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
return new Transform(-translateX, -translateY, 1 / scale);
}
+ panelWidth100 = () => 100;
+ panelHeight35 = () => 35;
+
@computed get contMenuButtons() {
+ trace();
const selDoc = Doc.UserDoc().contextMenuBtns;
return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: "35px" }} >
<CollectionLinearView
@@ -118,8 +122,8 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
pinToPres={emptyFunction}
removeDocument={returnFalse}
ScreenToLocalTransform={this.buttonBarXf}
- PanelWidth={() => 100}
- PanelHeight={() => 35}
+ PanelWidth={this.panelWidth100}
+ PanelHeight={this.panelHeight35}
renderDepth={0}
focus={emptyFunction}
whenChildContentsActiveChanged={emptyFunction}
@@ -437,10 +441,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
</div>);
}
- @computed get selectedDocumentView() {
- return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- }
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+ @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
@computed get notACollection() {
if (this.selectedDoc) {
const layoutField = Doc.LayoutField(this.selectedDoc);
@@ -548,8 +550,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
@undoBatch
onAliasButtonMoved = (e: PointerEvent) => {
const contentDiv = this.selectedDocumentView?.ContentDiv;
- if (contentDiv) {
- const dragData = new DragManager.DocumentDragData([this.selectedDocumentView!.props.Document]);
+ if (contentDiv && this.selectedDoc) {
+ const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
dragData.defaultDropAction = "alias";
dragData.canEmbed = true;
@@ -644,11 +646,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
}
@computed get childDocs() { return DocListCast(this.dataField); }
- @computed get selectedDocumentView() { return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; }
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
- @computed get isText() {
- return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
- }
+ @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+ @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
+ @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }
@undoBatch
@action
@@ -822,7 +822,6 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>;
}
- @observable viewType = this.selectedDoc?._viewType;
render() {
return !this.props.docView.layoutDoc ? (null) :
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index c92b259d0..540bfd1ef 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -236,6 +236,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
rootSelected={this.rootSelected}
+ showTitle={this.props.childShowTitle}
dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
onClick={this.onChildClickHandler}
onDoubleClick={this.onChildDoubleClickHandler}
@@ -517,14 +518,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
}
@computed get buttonMenu() {
- const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+ const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
// TODO:glr Allow support for multiple buttons
- if (menuDoc){
+ if (menuDoc) {
const width: number = NumCast(menuDoc._width, 30);
const height: number = NumCast(menuDoc._height, 30);
console.log(menuDoc.title, width, height);
return (<div className="buttonMenu-docBtn"
- style = {{width: width, height: height}}>
+ style={{ width: width, height: height }}>
<DocumentView
Document={menuDoc}
DataDoc={menuDoc}
@@ -550,7 +551,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
docRangeFilters={this.props.docRangeFilters}
searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
+ ContainingCollectionDoc={undefined}
/>
</div>
);
@@ -579,7 +580,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
<>
{buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
{buttonMenu ? this.buttonMenu : null}
- {Doc.UserDoc().noviceMode && noviceExplainer ?
+ {Doc.UserDoc().noviceMode && noviceExplainer ?
<div className="documentExplanation">
{noviceExplainer}
</div>
@@ -612,10 +613,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
checkedChildren="edit"
unCheckedChildren="view"
/>} */}
- </div>
+ </div>
</div>
</>
-
+
);
}
}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 47733994b..58289a161 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -111,7 +111,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
@action pointerLeave = () => this._background = "inherit";
- textCallback = (char: string) => this.addNewTextDoc("", false, true);
+ textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b70df93da..06d20f015 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -321,12 +321,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
});
} else {
- let srcUrl: string | undefined;
- let srcWeb: Doc | undefined;
- if (SelectionManager.Views().length) {
- srcWeb = SelectionManager.Views()[0].props.Document;
- srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
- }
+ const srcWeb = SelectionManager.Docs().lastElement();
+ const srcUrl = (srcWeb?.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
const reg = new RegExp(Utils.prepend(""), "g");
const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 229e93b9e..bc02c44f0 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -24,7 +24,7 @@ import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionLinearView } from './collectionLinear';
import CollectionMapView from './CollectionMapView';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
@@ -71,6 +71,7 @@ export interface CollectionViewProps extends FieldViewProps {
childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
childFitWidth?: () => boolean;
+ childShowTitle?: () => string;
childOpacity?: () => number;
childContextMenuItems?: () => { script: ScriptField, label: string }[];
childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 16258404d..3b3e069d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -5,7 +5,6 @@ import { Id } from "../../../../fields/FieldSymbols";
import { List } from "../../../../fields/List";
import { NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { DocumentType } from "../../../documents/DocumentTypes";
import { LinkManager } from "../../../util/LinkManager";
import { SnappingManager } from "../../../util/SnappingManager";
import { DocumentView } from "../../nodes/DocumentView";
@@ -43,8 +42,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
- const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
- const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
+ const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+ const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
const adiv = acont.length ? acont[0] : A.ContentDiv;
const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
const a = adiv.getBoundingClientRect();
@@ -69,8 +68,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
} else {
const m = targetAhyperlink.getBoundingClientRect();
const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- linkDoc.anchor1_x = Math.min(1, mp[0] / A.props.PanelWidth()) * 100;
- linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100;
+ const mpx = mp[0] / A.props.PanelWidth();
+ const mpy = mp[1] / A.props.PanelHeight();
+ if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
+ if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
}
if (!targetBhyperlink) {
if (linkDoc.linkAutoMove) {
@@ -80,8 +81,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
} else {
const m = targetBhyperlink.getBoundingClientRect();
const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- linkDoc.anchor2_x = Math.min(1, mp[0] / B.props.PanelWidth()) * 100;
- linkDoc.anchor2_y = Math.min(1, mp[1] / B.props.PanelHeight()) * 100;
+ const mpx = mp[0] / B.props.PanelWidth();
+ const mpy = mp[1] / B.props.PanelHeight();
+ if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
+ if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
}
}
@@ -140,8 +143,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
- const adiv = (acont.length ? acont[0] : A.ContentDiv);
- const bdiv = (bcont.length ? bcont[0] : B.ContentDiv);
+ const adiv = acont.length ? acont[0] : A.ContentDiv;
+ const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
for (let bpdiv = bdiv; bpdiv; bpdiv = bpdiv.parentElement as any) if ((bpdiv as any).hidden) return;
const a = adiv.getBoundingClientRect();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index e812064b7..dacbb3508 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,30 +1,17 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
-import { Utils } from "../../../../Utils";
import { DocumentManager } from "../../../util/DocumentManager";
-import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { LinkManager } from "../../../util/LinkManager";
@observer
export class CollectionFreeFormLinksView extends React.Component {
@computed get uniqueConnections() {
- const connections = DocumentManager.Instance.LinkedDocumentViews
- .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK)
- .reduce((drawnPairs, connection) => {
- const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b);
- matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l));
- if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set<Doc>([connection.l]) });
- return drawnPairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Set<Doc> }[]);
- const set = new Map<Doc, { a: DocumentView, b: DocumentView, l: Doc[] }>();
- connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) }));
- return Array.from(set.values()).map(c => <CollectionFreeFormLinkView key={c.l[0][Id]} A={c.a} B={c.b} LinkDocs={c.l} />);
+ return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
+ <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
+ );
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dd1f3e707..c8561d901 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1052,7 +1052,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
freezeDimensions={this.props.childFreezeDimensions}
dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
bringToFront={this.bringToFront}
- dontRegisterView={this.props.dontRegisterView}
+ showTitle={this.props.childShowTitle}
+ dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
(this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
@@ -1457,7 +1458,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
<div ref={this._marqueeRef} style={{ display: this.props.dontRenderDocuments ? "none" : undefined }}>
- {this.layoutDoc["_backgroundGridShow"] ? this.backgroundGrid : (null)}
+ {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
<CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.contentTransform}
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index 8fe804466..8fe804466 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index f3ffb4e0b..7fe95fef0 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,24 +1,24 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
import { documentSchema } from '../../../../fields/documentSchemas';
import { Id } from '../../../../fields/FieldSymbols';
import { makeInterface } from '../../../../fields/Schema';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
+import { Colors, Shadows } from '../../global/globalEnums';
import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { DocumentView } from '../../nodes/DocumentView';
import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
import { StyleProp } from '../../StyleProvider';
+import { CollectionSubView } from '../CollectionSubView';
+import { CollectionViewType } from '../CollectionView';
import "./CollectionLinearView.scss";
-import { CollectionSubView } from '.././CollectionSubView';
-import { CollectionViewType } from '.././CollectionView';
-import { Colors, Shadows } from '../../global/globalEnums';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -74,9 +74,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}
dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
- getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
- if (!ele.current) return Transform.Identity();
- const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
+ getTransform = (ele: Opt<HTMLDivElement>) => {
+ if (!ele) return Transform.Identity();
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
return new Transform(-translateX, -translateY, 1);
}
@@ -108,6 +108,58 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}
}
+ myContextMenu = (e: React.MouseEvent) => {
+ console.log("STOPPING");
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+
+ getDisplayDoc = (doc: Doc) => {
+ const nested = doc._viewType === CollectionViewType.Linear;
+ const hidden = doc.hidden === true;
+
+ let dref: Opt<HTMLDivElement>;
+ const docXf = () => this.getTransform(dref);
+ // const scalable = pair.layout.onClick || pair.layout.onDragStart;
+ return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={doc[Id]} ref={r => dref = r || undefined}
+ style={{
+ pointerEvents: "all",
+ width: nested ? undefined : NumCast(doc._width),
+ height: nested ? undefined : NumCast(doc._height),
+ marginLeft: !nested ? 2.5 : 0,
+ marginRight: !nested ? 2.5 : 0,
+ // width: NumCast(pair.layout._width),
+ // height: NumCast(pair.layout._height),
+ }} >
+ <DocumentView
+ Document={doc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={docXf}
+ PanelWidth={nested ? doc[WidthSym] : this.dimension}
+ PanelHeight={nested ? doc[HeightSym] : this.dimension}
+ renderDepth={this.props.renderDepth + 1}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
+ </div>;
+ }
+
render() {
const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content
@@ -126,12 +178,13 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
}}
onPointerDown={e => e.stopPropagation()} >
<div className="collectionLinearView-menuOpener">
- {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"}/> : icon ? icon : <FontAwesomeIcon icon={"plus"}/>}
+ {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
</div>
</label>;
return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
- <div className="collectionLinearView" ref={this.createDashEventsTarget} >
+ <div className="collectionLinearView" ref={this.createDashEventsTarget}
+ onContextMenu={this.myContextMenu} >
{!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
{menuOpener}
</Tooltip>}
@@ -144,49 +197,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
flexDirection: flexDir,
gap: flexGap
}}>
- {this.childLayoutPairs.map((pair, ind) => {
- const nested = pair.layout._viewType === CollectionViewType.Linear;
- const dref = React.createRef<HTMLDivElement>();
- const hidden = pair.layout.hidden === true;
- // const scalable = pair.layout.onClick || pair.layout.onDragStart;
- return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
- style={{
- pointerEvents: "all",
- width: nested ? undefined : NumCast(pair.layout._width),
- height: nested ? undefined : NumCast(pair.layout._height),
- marginLeft: !nested ? 2.5 : 0,
- marginRight: !nested ? 2.5 : 0,
- // width: NumCast(pair.layout._width),
- // height: NumCast(pair.layout._height),
- }} >
- <DocumentView
- Document={pair.layout}
- DataDoc={pair.data}
- isContentActive={this.props.isContentActive}
- isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={this.getTransform(dref)}
- PanelWidth={nested ? pair.layout[WidthSym] : this.dimension}
- PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
- renderDepth={this.props.renderDepth + 1}
- focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- layerProvider={this.props.layerProvider}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- </div>;
- })}
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
</div>
{DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background" style={{
pointerEvents: "all"
diff --git a/src/client/views/collections/collectionLinearView/index.ts b/src/client/views/collections/collectionLinear/index.ts
index ff73e14ae..ff73e14ae 100644
--- a/src/client/views/collections/collectionLinearView/index.ts
+++ b/src/client/views/collections/collectionLinear/index.ts
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 544125ede..fad905d6d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -201,7 +201,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
if (splits.length > 1) {
const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
- return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
+ const script = code[1].value.replace(/^‘/, "").replace(/’$/, ""); // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
+ return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
}
return undefined;
// add input function to props
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ebd0211e2..d88f1b9a4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
import { AudioField } from "../../../fields/URLField";
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -115,6 +115,7 @@ export interface DocumentViewSharedProps {
docFilters: () => string[];
docRangeFilters: () => string[];
searchFilterDocs: () => Doc[];
+ showTitle?: () => string;
whenChildContentsActiveChanged: (isActive: boolean) => void;
rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
addDocTab: (doc: Doc, where: string) => boolean;
@@ -649,7 +650,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
e.preventDefault();
e.stopPropagation();
- !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
+ //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
}
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
if (e) {
@@ -704,13 +705,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const zorders = cm.findByDescription("ZOrder...");
const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
- zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
- zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
- zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+ if (this.props.bringToFront !== emptyFunction) {
+ zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+ }
!zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+ !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
if (!this.Document.annotationOn) {
@@ -848,16 +851,28 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
default: return this.props.styleProvider?.(doc, props, property);
}
}
- @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
+ // We need to use allrelatedLinks to get not just links to the document as a whole, but links to
+ // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document). e.g.,
+ // - PDF text regions are rendered as an Annotations without generating a DocumentView, '
+ // - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
+ // - and links to PDF/Web docs at a certain scroll location never create an explicit view.
+ // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
+ @computed get directLinks() {
+ TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(link =>
+ Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
+ Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
+ ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
+ ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
+ );
+ }
@computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
@computed get allLinkEndpoints() { // the small blue dots that mark the endpoints of links
TraceMobx();
- if (this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
+ if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
- // need to use allLinks for RTF since embedded linked text anchors are not rendered with DocumentViews. All other documents render their anchors with nested DocumentViews so we just need to render the directLinks here
- const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
+ const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
return filtered.map((link, i) =>
- <div className="documentView-anchorCont" key={i + 1}>
+ <div className="documentView-anchorCont" key={link[Id]}>
<DocumentView {...this.props}
Document={link}
PanelWidth={this.anchorPanelWidth}
@@ -938,22 +953,41 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
hideOnLeave={true}
styleProvider={this.captionStyleProvider}
dontRegisterView={true}
+ isContentActive={this.isContentActive}
onClick={this.onClickFunc}
/>
</div>;
+ const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
+ const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
const titleView = !showTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: this.headerMargin ? "relative" : "absolute",
height: this.titleHeight,
- background: StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"),
+ color: lightOrDark(background),
+ background,
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
- contents={showTitle === "title" ? StrCast((this.dataDoc || this.props.Document).title) : showTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
+ contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")}
display={"block"}
fontSize={10}
- GetValue={() => Field.toString((this.dataDoc || this.props.Document)[showTitle.split(";")[0]] as any as Field)}
- SetValue={undoBatch((value) => showTitle.includes("Date") ? true : (Doc.GetProto(this.dataDoc || this.props.Document)[showTitle] = value) ? true : true)}
+ GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
+ SetValue={undoBatch(input => {
+ if (input?.startsWith("#")) {
+ if (this.props.showTitle) {
+ this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
+ } else {
+ Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
+ }
+ return true;
+ } else {
+ var value = input.replace(new RegExp(showTitle + "="), "");
+ if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
+ if (showTitle.includes("Date") || showTitle === "author") return true;
+ return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
+ }
+ return true;
+ })}
/>
</div>;
return this.props.hideTitle || (!showTitle && !showCaption) ?
@@ -1155,7 +1189,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
- !this.props.dontRegisterView && DocumentManager.Instance.RemoveView(this);
+ !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
}
render() {
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 7ad03e055..e9f19bf9e 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -9,7 +9,7 @@ import { List } from "../../../fields/List";
import { RichTextField } from "../../../fields/RichTextField";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -79,7 +79,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
* @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
*/
@computed static get targetDoc() {
- return SelectionManager.Views().length ? SelectionManager.Views()[0].Document : CurrentUserUtils.ActiveDashboard;
+ return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : CurrentUserUtils.ActiveDashboard;
}
@computed static get targetDocChildKey() {
if (SelectionManager.Views().length) {
@@ -88,10 +88,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
return "data";
}
@computed static get targetDocChildren() {
- if (SelectionManager.Views().length) {
- return DocListCast(FilterBox.targetDoc[FilterBox.targetDocChildKey]);
- }
- return DocListCast(CurrentUserUtils.ActiveDashboard.data);
+ return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data);
}
@observable _loaded = false;
@@ -347,7 +344,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
);
}
setTreeHeight = (hgt: number) => {
- this.layoutDoc._height = hgt + 140; // 50? need to add all the border sizes together.
+ this.layoutDoc._height = NumCast(this.layoutDoc._autoHeightMargins) + 150; // need to add all the border sizes together.
}
/**
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 8f9959693..1e0172d24 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -3,7 +3,6 @@ import { action, observable } from "mobx";
import { observer } from "mobx-react";
import { Doc } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from "../../../fields/FieldSymbols";
import { makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 3a399bfdb..9706d73c7 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,13 +3,13 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc";
import { documentSchema } from '../../../fields/documentSchemas';
import { makeInterface } from "../../../fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast } from '../../../fields/Types';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { PdfField } from "../../../fields/URLField";
import { TraceMobx } from '../../../fields/util';
-import { Utils, setupMoveUpEvents, emptyFunction, returnOne } from '../../../Utils';
+import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { KeyCodes } from '../../util/KeyCodes';
import { undoBatch } from '../../util/UndoManager';
@@ -17,14 +17,14 @@ import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeF
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { Colors } from '../global/globalEnums';
+import { AnchorMenu } from '../pdf/AnchorMenu';
import { PDFViewer } from "../pdf/PDFViewer";
import { SidebarAnnos } from '../SidebarAnnos';
import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
-import { AnchorMenu } from '../pdf/AnchorMenu';
-import { Colors } from '../global/globalEnums';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -79,9 +79,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const anchor =
AnchorMenu.Instance?.GetAnchor() ??
Docs.Create.TextanchorDocument({
- title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
- annotationOn: this.rootDoc,
+ title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)),
y: NumCast(this.layoutDoc._scrollTop),
+ unrendered: true
});
this.addDocument(anchor);
return anchor;
@@ -210,13 +210,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{this._pageControls ? pageBtns : (null)}
</div>
<div className="pdfBox-sidebarBtn" key="sidebar" title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? "none" : undefined,
+ style={{
+ display: !this.props.isContentActive() ? "none" : undefined,
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
onPointerDown={this.sidebarBtnDown} >
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
</div>;
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1f85219d6..7e46d8433 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as WebRequest from 'web-request';
import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
@@ -24,6 +24,7 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
+import { Colors } from "../global/globalEnums";
import { LightboxView } from "../LightboxView";
import { MarqueeAnnotator } from "../MarqueeAnnotator";
import { AnchorMenu } from "../pdf/AnchorMenu";
@@ -33,7 +34,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from "./LinkDocPreview";
import "./WebBox.scss";
import React = require("react");
-import { Colors } from "../global/globalEnums";
const _global = (window /* browser */ || global /* node */) as any;
const htmlToText = require("html-to-text");
@@ -187,8 +187,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
Docs.Create.WebanchorDocument(this._url, {
title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
- annotationOn: this.rootDoc,
- y: NumCast(this.layoutDoc._scrollTop)
+ y: NumCast(this.layoutDoc._scrollTop),
+ unrendered: true
});
this.addDocumentWrapper(anchor);
return anchor;
@@ -592,13 +592,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
removeDocument={this.removeDocument}
/>
<div className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? "none" : undefined,
+ style={{
+ display: !this.props.isContentActive() ? "none" : undefined,
top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
}}
onPointerDown={this.sidebarBtnDown} >
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
</div>
</div>);
}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index b72f31ef8..df7c54f67 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -15,7 +15,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { Scripting } from "../../../util/Scripting";
import { SelectionManager } from '../../../util/SelectionManager';
import { ColorScheme } from '../../../util/SettingsManager';
-import { UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { CollectionViewType } from '../../collections/CollectionView';
import { ContextMenu } from '../../ContextMenu';
import { DocComponent } from '../../DocComponent';
@@ -31,6 +31,7 @@ import { ToggleButton } from './toggleButton';
import { IButtonProps } from './ButtonInterface';
import { FontIconBadge } from './FontIconBadge';
import './FontIconBox.scss';
+import { undo } from 'prosemirror-history';
const FontIconSchema = createSchema({
icon: "string",
});
@@ -247,14 +248,15 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
let noneSelected: boolean = false;
if (script === 'setView') {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- if (selected && StrCast(selected.Document.type) === DocumentType.COL) {
- text = StrCast(selected.Document._viewType);
- } else if (selected) {
- dropdown = false;
- text = StrCast(selected.Document.type);
- if (text === DocumentType.RTF) text = "Text";
- icon = Doc.toIcon(selected.Document);
+ const selected = SelectionManager.Docs().lastElement();
+ if (selected) {
+ if (StrCast(selected.type) === DocumentType.COL) {
+ text = StrCast(selected._viewType);
+ } else {
+ dropdown = false;
+ text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+ icon = Doc.toIcon(selected);
+ }
} else {
dropdown = false;
icon = "globe-asia";
@@ -262,13 +264,8 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
}
noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
} else if (script === 'setFont') {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- if (selected && StrCast(selected.Document.type) === DocumentType.RTF) {
- text = StrCast(selected.Document._fontFamily);
- } else {
- const fontFamily = StrCast(Doc.UserDoc()._fontFamily);
- text = fontFamily;
- }
+ const selected = SelectionManager.Docs().lastElement();
+ text = StrCast((selected?.type === DocumentType.RTF ? selected : Doc.UserDoc())._fontFamily);
noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
"Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
}
@@ -303,7 +300,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
<div className={`menuButton ${this.type} ${active}`}
style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
- {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{marginLeft: 5}} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+ {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
<div className="menuButton-dropdown-header">
{text && text[0].toUpperCase() + text.slice(1)}
</div>
@@ -348,7 +345,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
- '#FFFFFF', '#f1efeb'];
+ '#FFFFFF', '#f1efeb', "transparent"];
const colorBox = (func: (color: ColorState) => void) => <SketchPicker
disableAlpha={!stroke}
@@ -369,7 +366,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
const hex: string = value.hex;
const s = ScriptField.MakeScript(script + '("' + hex + '", false)');
if (s) {
- s.script.run().result;
+ undoBatch(() => s.script.run().result)();
}
};
return (
@@ -493,7 +490,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
{this.label}
</div>;
- const buttonProps:IButtonProps = {
+ const buttonProps: IButtonProps = {
type: this.type,
rootDoc: this.rootDoc,
label: label,
@@ -512,9 +509,9 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
button = (
<div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
- {buttonText ?
+ {buttonText ?
<div className="button-text">
- {buttonText}
+ {buttonText}
</div>
: null}
{label}
@@ -583,13 +580,13 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setView(view: string) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
+ const selected = SelectionManager.Docs().lastElement();
+ selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
});
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ const selected = SelectionManager.Docs().lastElement();
if (checkResult) {
if (selected) {
console.log("[Background] (selected): " + StrCast(selected._backgroundColor));
@@ -598,12 +595,19 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
return "#FFFFFF";
}
}
- if (selected && selected.type === DocumentType.INK) selected.fillColor = color;
+ if (selected?.type === DocumentType.INK) selected.fillColor = color;
if (selected) selected._backgroundColor = color;
Doc.UserDoc()._fontColor = color;
});
// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
+ Doc.SharingDoc().userColor = undefined;
+ Doc.GetProto(Doc.SharingDoc()).userColor = color;
+ Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate");
+});
+
+// toggle: Set overlay status of selected document
Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
if (checkResult && selected) {
@@ -627,7 +631,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
- SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font);
+ SelectionManager.Docs().map(doc => doc._fontFamily = font);
const editorView = RichTextMenu.Instance.TextView?.EditorView;
editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView);
Doc.UserDoc()._fontFamily = font;
@@ -657,7 +661,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
if (active === align) return Colors.MEDIUM_BLUE;
else return "transparent";
}
- SelectionManager.Views().map(dv => dv.props.Document.textAlign = align);
+ SelectionManager.Docs().map(doc => doc.textAlign = align);
switch (align) {
case "left":
editorView?.state && RichTextMenu.Instance.alignLeft(editorView, editorView.dispatch);
@@ -695,13 +699,13 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ const selected = SelectionManager.Docs().lastElement();
const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
if (selected) {
- console.log("[Font color] (selected): " + StrCast(selected.rootDoc._fontColor));
- return selected.rootDoc._fontColor;
+ console.log("[Font color] (selected): " + StrCast(selected._fontColor));
+ return selected._fontColor;
} else {
console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
return Doc.UserDoc()._fontColor;
@@ -709,7 +713,7 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
}
if (selected) {
- selected.rootDoc._fontColor = color;
+ selected._fontColor = color;
if (color) {
editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
}
@@ -719,18 +723,18 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+ const selected = SelectionManager.Docs().lastElement();
const editorView = RichTextMenu.Instance.TextView?.EditorView;
if (checkResult) {
if (selected) {
- return selected.Document._fontHighlight;
+ return selected._fontHighlight;
} else {
return Doc.UserDoc()._fontHighlight;
}
}
if (selected) {
- selected.rootDoc._fontColor = color;
+ selected._fontColor = color;
if (color) {
editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
}
@@ -761,7 +765,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
console.log("editorView");
editorView?.state && RichTextMenu.Instance.toggleBold(editorView, true);
}
- SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.bold = !dv.props.Document.bold);
+ SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.bold = !doc.bold);
Doc.UserDoc().bold = !Doc.UserDoc().bold;
return Doc.UserDoc().bold;
});
@@ -776,7 +780,7 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
console.log("editorView");
editorView?.state && RichTextMenu.Instance.toggleUnderline(editorView, true);
}
- SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.underline = !dv.props.Document.underline);
+ SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.underline = !doc.underline);
Doc.UserDoc().underline = !Doc.UserDoc().underline;
return Doc.UserDoc().underline;
});
@@ -791,7 +795,7 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
console.log("editorView");
editorView?.state && RichTextMenu.Instance.toggleItalic(editorView, true);
}
- SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+ SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.italic = !doc.italic);
Doc.UserDoc().italic = !Doc.UserDoc().italic;
return Doc.UserDoc().italic;
});
@@ -833,7 +837,7 @@ Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolea
Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
if (checkResult) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ const selected = SelectionManager.Docs().lastElement();
if (selected && selected.type === DocumentType.INK) {
return Number(selected.strokeWidth);
} else {
@@ -842,13 +846,13 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean
}
}
Doc.UserDoc().activeInkWidth = width;
- SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width));
+ SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.INK).map(doc => doc.strokeWidth = Number(width));
});
// toggle: Set overlay status of selected document
Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
if (checkResult) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ const selected = SelectionManager.Docs().lastElement();
if (selected && selected.type === DocumentType.INK) {
return selected.color;
} else {
@@ -857,7 +861,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
}
}
SetActiveInkColor(StrCast(color));
- SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.color = String(color));
+ SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.INK).map(doc => doc.color = String(color));
});
@@ -865,7 +869,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
* webSetURL
**/
Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ const selected = SelectionManager.Docs().lastElement();
console.log("URL: ", url);
if (checkResult && selected && selected.type === DocumentType.WEB) {
return Cast(selected.data, WebField, null).url;
@@ -880,7 +884,7 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
* toggleSchemaPreview
**/
Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
- const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+ const selected = SelectionManager.Docs().lastElement();
console.log(selected && selected.title);
if (checkResult && selected) {
const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx
index 414b50dcb..e18590a95 100644
--- a/src/client/views/nodes/button/textButton/TextButton.tsx
+++ b/src/client/views/nodes/button/textButton/TextButton.tsx
@@ -12,6 +12,6 @@ export class TextButton extends Component<IButtonProps> {
return (<div className={`menuButton ${this.props.type}`} style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}>
<FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
{this.props.label}
- </div>)
+ </div>);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts
index 01fcde54f..01d62eb7e 100644
--- a/src/client/views/nodes/button/textButton/index.ts
+++ b/src/client/views/nodes/button/textButton/index.ts
@@ -1 +1 @@
-export * from './TextButton' \ No newline at end of file
+export * from './TextButton'; \ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts
index 48b28fb4c..cdb9c527c 100644
--- a/src/client/views/nodes/button/toggleButton/index.ts
+++ b/src/client/views/nodes/button/toggleButton/index.ts
@@ -1 +1 @@
-export * from './ToggleButton' \ No newline at end of file
+export * from './ToggleButton'; \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 62f65cdae..34908e54b 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
if (this._dashDoc) {
- const dashVal = this._dashDoc[DataSym][this._fieldKey] ?? this._dashDoc[this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+ const dashVal = this._dashDoc[this._fieldKey] ?? this._dashDoc[DataSym][this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
const boolVal = Cast(fval, "boolean", null);
const strVal = Field.toString(fval as Field) || "";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 56f5c5631..4134e3c67 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -66,11 +66,12 @@ audiotag:hover {
.formattedTextBox-sidebar-handle {
position: absolute;
- top: 5px;
+ top: 0;
+ left: 17px;
//top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
- width: 25px;
- height: 100%;
- max-height: 25px;
+ width: 17px;
+ height: 17px;
+ border-radius: 3px;
color: white;
background: $medium-gray;
border-radius: 5px;
@@ -80,8 +81,9 @@ audiotag:hover {
cursor:grabbing;
box-shadow: $standard-box-shadow;
// transition: 0.2s;
-
+ opacity: 0.3;
&:hover{
+ opacity: 1 !important;
filter: brightness(0.85);
}
@@ -425,12 +427,7 @@ footnote::after {
.formattedTextBox-sidebar-handle {
position: absolute;
- top: 0;
- //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
- width: 10px;
- height: 35px;
background: lightgray;
- border-radius: 20px;
cursor: grabbing;
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 59d43f8d7..78de1fd89 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEqual } from "lodash";
-import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, observable, trace } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap, selectAll } from "prosemirror-commands";
import { history } from "prosemirror-history";
@@ -432,6 +432,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
protected createDropTarget = (ele: HTMLDivElement) => {
this.ProseRef = ele;
+ this.setupEditor(this.config, this.props.fieldKey);
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc));
// if (this.autoHeight) this.tryUpdateScrollHeight();
@@ -573,12 +574,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const cm = ContextMenu.Instance;
const changeItems: ContextMenuProps[] = [];
- changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
+ changeItems.push({
+ description: "plain", event: undoBatch(() => {
+ Doc.setNativeView(this.rootDoc);
+ this.layoutDoc.autoHeightMargins = undefined;
+ }), icon: "eye"
+ });
+ changeItems.push({
+ description: "metadata", event: undoBatch(() => {
+ this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
+ this.rootDoc.layoutKey = "layout_meta";
+ setTimeout(() => this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50, 50);
+ }), icon: "eye"
+ });
const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
DocListCast(noteTypesDoc?.data).forEach(note => {
const icon: IconProp = StrCast(note.icon) as IconProp;
changeItems.push({
description: StrCast(note.title), event: undoBatch(() => {
+ this.layoutDoc.autoHeightMargins = undefined;
Doc.setNativeView(this.rootDoc);
DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
}), icon: icon
@@ -713,7 +727,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
let tr = state.tr.addMark(sel.from, sel.to, splitter);
if (sel.from !== sel.to) {
- const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) });
+ const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true });
const href = targetHref ?? Doc.localServerPath(anchor);
if (anchor !== anchorDoc) this.addDocument(anchor);
tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
@@ -864,8 +878,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
);
- this.setupEditor(this.config, this.props.fieldKey);
-
this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
{ fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
@@ -1144,8 +1156,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
selectOnLoad && this._editorView!.focus();
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
- if (!this._editorView!.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
- this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
+ if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
+ this._editorView.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
}
}
@@ -1360,6 +1372,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._undoTyping = undefined;
return wasUndoing;
}
+
@action
onBlur = (e: any) => {
FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
@@ -1448,18 +1461,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (children) {
- var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
- var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+ const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+ const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
setScrollHeight();
- // setTimeout(() => {
- // proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
- // scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
- // scrollHeight && setScrollHeight();
- // }, 10);
- } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
+ } else {
+ setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
+ }
}
}
}
@@ -1486,14 +1496,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
const color = !annotated ? Colors.WHITE : Colors.BLACK;
const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
- return (!annotated && !this.props.isContentActive()) ? (null) : <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
- style={{
- left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${"- 30px"}))`,
- backgroundColor: backgroundColor,
- color: color
- }} >
- <FontAwesomeIcon icon={"comment-alt"} />
- </div>;
+ return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) :
+ <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+ style={{
+ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
+ backgroundColor: backgroundColor,
+ color: color,
+ opacity: annotated ? 1 : undefined
+ }} >
+ <FontAwesomeIcon icon={"comment-alt"} />
+ </div>;
}
@computed get sidebarCollection() {
const renderComponent = (tag: string) => {
@@ -1555,7 +1567,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const selPad = Math.min(margins, 10);
const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
- return (
+ const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
+ return (styleFromString?.height === "0px" ? (null) :
<div className="formattedTextBox-cont"
onWheel={e => this.props.isContentActive() && e.stopPropagation()}
style={{
@@ -1564,7 +1577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
width: this.props.dontScale ? undefined : `${100 / scale}%`,
height: this.props.dontScale ? undefined : `${100 / scale}%`,
// overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
- ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
+ ...styleFromString
}}>
<div className={`formattedTextBox-cont`} ref={this._ref}
style={{
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index c94e93541..8afa0f6b5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -2,6 +2,7 @@
.button-dropdown-wrapper {
position: relative;
+ display: flex;
.dropdown-button {
width: 15px;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 3d9d9543d..3919fbf94 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -279,33 +279,33 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
}
- toggleBold = (view: EditorView, forceBool?:boolean) => {
- const mark = view.state.schema.mark(view.state.schema.marks.strong, {strong: forceBool});
+ toggleBold = (view: EditorView, forceBool?: boolean) => {
+ const mark = view.state.schema.mark(view.state.schema.marks.strong, { strong: forceBool });
this.setMark(mark, view.state, view.dispatch, false);
view.focus();
}
- toggleUnderline = (view: EditorView, forceBool?:boolean) => {
- const mark = view.state.schema.mark(view.state.schema.marks.underline, {underline: forceBool});
+ toggleUnderline = (view: EditorView, forceBool?: boolean) => {
+ const mark = view.state.schema.mark(view.state.schema.marks.underline, { underline: forceBool });
this.setMark(mark, view.state, view.dispatch, false);
view.focus();
}
- toggleItalic = (view: EditorView, forceBool?:boolean) => {
- const mark = view.state.schema.mark(view.state.schema.marks.em, {em: forceBool});
+ toggleItalic = (view: EditorView, forceBool?: boolean) => {
+ const mark = view.state.schema.mark(view.state.schema.marks.em, { em: forceBool });
this.setMark(mark, view.state, view.dispatch, false);
view.focus();
}
- setFontSize = (size:number, view: EditorView) => {
+ setFontSize = (size: number, view: EditorView) => {
const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
view.focus();
this.updateMenu(view, undefined, this.props);
}
- setFontFamily = (family:string, view: EditorView) => {
+ setFontFamily = (family: string, view: EditorView) => {
const fmark = view.state.schema.marks.pFontFamily.create({ family: family });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
view.focus();
@@ -516,7 +516,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
-
+
@action setActiveHighlight(color: string) { this.activeHighlightColor = color; }
@@ -566,7 +566,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (linkDoc instanceof Doc) {
const anchor1 = await Cast(linkDoc.anchor1, Doc);
const anchor2 = await Cast(linkDoc.anchor2, Doc);
- const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
+ const currentDoc = SelectionManager.Docs().lastElement();
if (currentDoc && anchor1 && anchor2) {
if (Doc.AreProtosEqual(currentDoc, anchor1)) {
return StrCast(anchor2.title);
diff --git a/src/client/views/pdf/AnchorMenu.scss b/src/client/views/pdf/AnchorMenu.scss
index b7afb26a5..6990bdcf1 100644
--- a/src/client/views/pdf/AnchorMenu.scss
+++ b/src/client/views/pdf/AnchorMenu.scss
@@ -4,6 +4,35 @@
padding: 5px;
grid-template-columns: 90px 20px 90px;
}
+.anchorMenu-highlighter {
+ padding-right: 5px;
+ .antimodeMenu-button {
+ padding: 0;
+ padding: 0;
+ padding-right: 0px;
+ padding-left: 0px;
+ width: 5px;
+ }
+}
+.anchor-color-preview-button {
+ width: 25px !important;
+ .anchor-color-preview {
+ display: flex;
+ flex-direction: column;
+ padding-right: 3px;
+ width: unset !important;
+ .color-preview {
+ width: 60%;
+ top: 80%;
+ height: 4px;
+ position: relative;
+ top: unset;
+ width: 15px;
+ margin-top: 5px;
+ display: block;
+ }
+ }
+}
.color-wrapper {
display: flex;
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 3bf1f0828..3ba427c29 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -41,7 +41,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private highlightColor: string = "rgba(245, 230, 95, 0.616)";
@observable private _showLinkPopup: boolean = false;
- @observable public _colorBtn = false;
@observable public Highlighting: boolean = false;
@observable public Status: "marquee" | "annotation" | "" = "";
@@ -97,9 +96,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get highlighter() {
const button =
- <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
- <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
- <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
+ <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
+ <div className="anchor-color-preview" >
+ <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
+ <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
+ </div>
</button>;
const dropdownContent =
@@ -117,7 +118,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
</div>;
return (
<Tooltip key="highlighter" title={<div className="dash-tooltip">{"Click to Highlight"}</div>}>
- <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
+ <div className="anchorMenu-highlighter">
+ <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} />
+ </div>
</Tooltip>
);
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 02010e123..d953c6b6c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,14 +2,13 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
-import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
-import { createSchema } from "../../../fields/Schema";
import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { PdfField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse, returnEmptyString, returnEmptyFilter } from "../../../Utils";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { Networking } from "../../Network";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b09ff93d0..4d040f3bc 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1136,7 +1136,7 @@ export namespace Doc {
// filters document in a container collection:
// all documents with the specified value for the specified key are included/excluded
// based on the modifiers :"check", "x", undefined
- export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
+ export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
if (!container) return;
const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters";
const docFilters = Cast(container[filterField], listSpec("string"), []);