aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2021-04-21 09:49:01 -0400
committerbobzel <zzzman@gmail.com>2021-04-21 09:49:01 -0400
commit3c6fd425c1273f87dc2142038a9ddc48d6159b1b (patch)
tree67c4a7a0f9b730f36ce08bcbc0c7695c82439576
parenteb7a6f091419318bd85e75787067ef5f036e2659 (diff)
parent45a43e9f0417d6929a6f351fea0cf97fe11b0ef0 (diff)
Fixed errors merging with master
-rw-r--r--.gitignore4
-rw-r--r--package-lock.json6
-rw-r--r--package.json4
-rw-r--r--src/client/ClientRecommender.tsx2
-rw-r--r--src/client/documents/Documents.ts48
-rw-r--r--src/client/util/CaptureManager.scss175
-rw-r--r--src/client/util/CaptureManager.tsx140
-rw-r--r--src/client/util/CurrentUserUtils.ts43
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/util/GroupManager.tsx5
-rw-r--r--src/client/util/SelectionManager.ts2
-rw-r--r--src/client/util/SettingsManager.scss18
-rw-r--r--src/client/util/SharingManager.tsx60
-rw-r--r--src/client/util/type_decls.d1
-rw-r--r--src/client/views/AntimodeMenu.scss14
-rw-r--r--src/client/views/AntimodeMenu.tsx2
-rw-r--r--src/client/views/ContextMenu.tsx27
-rw-r--r--src/client/views/ContextMenuItem.tsx7
-rw-r--r--src/client/views/DocComponent.tsx10
-rw-r--r--src/client/views/DocumentDecorations.tsx19
-rw-r--r--src/client/views/EditableView.scss2
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/LightboxView.tsx19
-rw-r--r--src/client/views/MainView.scss3
-rw-r--r--src/client/views/MainView.tsx17
-rw-r--r--src/client/views/MainViewModal.tsx2
-rw-r--r--src/client/views/PropertiesView.scss61
-rw-r--r--src/client/views/PropertiesView.tsx401
-rw-r--r--src/client/views/SidebarAnnos.tsx2
-rw-r--r--src/client/views/StyleProvider.tsx88
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx34
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss15
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx75
-rw-r--r--src/client/views/collections/CollectionMenu.scss124
-rw-r--r--src/client/views/collections/CollectionMenu.tsx72
-rw-r--r--src/client/views/collections/CollectionPileView.tsx13
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss1
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx11
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx4
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx20
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx21
-rw-r--r--src/client/views/collections/CollectionView.tsx3
-rw-r--r--src/client/views/collections/TabDocView.tsx18
-rw-r--r--src/client/views/collections/TreeView.scss41
-rw-r--r--src/client/views/collections/TreeView.tsx38
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx29
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx33
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx27
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx27
-rw-r--r--src/client/views/nodes/AudioBox.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx15
-rw-r--r--src/client/views/nodes/DocumentView.scss64
-rw-r--r--src/client/views/nodes/DocumentView.tsx216
-rw-r--r--src/client/views/nodes/FieldView.tsx5
-rw-r--r--src/client/views/nodes/FilterBox.scss168
-rw-r--r--src/client/views/nodes/FilterBox.tsx343
-rw-r--r--src/client/views/nodes/ImageBox.tsx48
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/PresBox.tsx43
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx33
-rw-r--r--src/client/views/nodes/SliderBox.tsx5
-rw-r--r--src/client/views/nodes/VideoBox.scss10
-rw-r--r--src/client/views/nodes/VideoBox.tsx21
-rw-r--r--src/client/views/nodes/WebBox.tsx25
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx37
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.scss7
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.tsx3
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx2
-rw-r--r--src/fields/Doc.ts26
74 files changed, 2091 insertions, 790 deletions
diff --git a/.gitignore b/.gitignore
index 6d4b98289..43b63a362 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,6 @@ src/server/session_manager/logs/**/*.log
*.crt
*.key
*.pfx
-*.properties \ No newline at end of file
+*.properties
+debug.log
+.vscodeignore \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index cdc1c6326..d286a4e02 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9315,9 +9315,9 @@
"integrity": "sha512-vTgEjKjS89C5yHL5qWPpT6BzKuOVqABp+A3Szpbx34pIy3sngxlGaFpgHhfj6fKze1w0QKeOSDbU7SKu7wDvRQ=="
},
"mobx": {
- "version": "5.15.4",
- "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.4.tgz",
- "integrity": "sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw=="
+ "version": "5.15.7",
+ "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.7.tgz",
+ "integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw=="
},
"mobx-react": {
"version": "5.4.4",
diff --git a/package.json b/package.json
index 72e4e85ce..8853714f1 100644
--- a/package.json
+++ b/package.json
@@ -187,8 +187,8 @@
"lodash": "^4.17.15",
"material-ui": "^0.20.2",
"mobile-detect": "^1.4.4",
- "mobx": "^5.15.3",
- "mobx-react": "^5.3.5",
+ "mobx": "^5.15.7",
+ "mobx-react": "^5.4.4",
"mobx-react-devtools": "^6.1.1",
"mobx-utils": "^5.6.1",
"mongodb": "^3.5.9",
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
index 3f875057e..1d4653471 100644
--- a/src/client/ClientRecommender.tsx
+++ b/src/client/ClientRecommender.tsx
@@ -40,8 +40,6 @@ const fieldkey = "data";
@observer
export class ClientRecommender extends React.Component<RecommenderProps> {
-
-
static Instance: ClientRecommender;
private mainDoc?: RecommenderDocument;
private docVectors: Set<RecommenderDocument> = new Set();
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 337c3e928..219890945 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -164,6 +164,7 @@ export class DocumentOptions {
version?: string; // version identifier for a document
label?: string;
hidden?: boolean;
+ mediaState?: string; // status of media document: "pendingRecording", "recording", "paused", "playing"
autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline.
dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it.
toolTip?: string; // tooltip to display on hover
@@ -171,7 +172,7 @@ export class DocumentOptions {
description?: string; // added for links
layout?: string | Doc; // default layout string for a document
contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
- childLimitHeight?: number; // whether to limit the height of colleciton children. 0 - means height can be no bigger than width
+ childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width
childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
childLayoutString?: string; // template string for collection to use to render its children
childDontRegisterViews?: boolean;
@@ -248,6 +249,7 @@ export class DocumentOptions {
treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
treeViewShowClearButton?: boolean; // whether a clear button should be displayed
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)
treeViewOpen?: boolean; // whether this document is expanded in a tree view
treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view
treeViewExpandedViewLock?: boolean; // whether the expanded view can be changed
@@ -668,7 +670,7 @@ export namespace Docs {
viewProps["acl-Override"] = "None";
viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true);
- ![DocumentType.LINK, DocumentType.TEXTANCHOR, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(viewDoc);
+ ![DocumentType.LINK, DocumentType.TEXTANCHOR, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
!Doc.IsSystem(dataDoc) && ![DocumentType.HTMLANCHOR, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) &&
!dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
@@ -702,8 +704,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options);
}
- export function ScreenshotDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options);
+ export function ScreenshotDocument(title: string, options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", { ...options, title });
}
export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) {
@@ -947,7 +949,14 @@ export namespace DocUtils {
}
return false;
}
- export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField) {
+ /**
+ * @param docs
+ * @param docFilters
+ * @param docRangeFilters
+ * @param viewSpecScript
+ * Given a list of docs and docFilters, @returns the list of Docs that match those filters
+ */
+ export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField, parentCollection?: Doc) {
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
if (!docFilters?.length && !docRangeFilters?.length) {
return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one
@@ -971,11 +980,20 @@ export namespace DocUtils {
if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
return false;
}
+
for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== "cookies")) {
const facet = filterFacets[facetKey];
+
+ // facets that match some value in the field of the document (e.g. some text field)
const matches = Object.keys(facet).filter(value => value !== "cookies" && facet[value] === "match");
+
+ // facets that have a check next to them
const checks = Object.keys(facet).filter(value => facet[value] === "check");
+
+ // 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;
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 satisfiesMatchFacets = !matches.length ? true : matches.some(value => {
@@ -987,11 +1005,17 @@ export namespace DocUtils {
}
return Field.toString(d[facetKey] as Field).includes(value);
});
- if (!satisfiesCheckFacets || !satisfiesMatchFacets || failsNotEqualFacets) {
- return false;
+ // 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 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;
}
+
}
- return true;
+ return (parentCollection?.currentFilter as Doc)?.filterBoolean === "OR" ? false : true;
}) : childDocs;
const rangeFilteredDocs = filteredDocs.filter(d => {
for (let i = 0; i < docRangeFilters.length; i += 3) {
@@ -1046,10 +1070,10 @@ export namespace DocUtils {
export let ActiveRecordings: { props: FieldViewProps, getAnchor: () => Doc }[] = [];
- export function MakeLinkToActiveAudio(doc: Doc, broadcastEvent = true) {
+ export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
return DocUtils.ActiveRecordings.map(audio =>
- DocUtils.MakeLink({ doc: doc }, { doc: audio.getAnchor() || audio.props.Document }, "recording link", "recording timeline"));
+ DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording link", "recording timeline"));
}
export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
@@ -1317,7 +1341,7 @@ export namespace DocUtils {
}
}
- export function LeavePushpin(doc: Doc) {
+ export function LeavePushpin(doc: Doc, annotationField: string) {
if (doc.isPushpin) return undefined;
const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
const hasContextAnchor = DocListCast(doc.links).
@@ -1330,7 +1354,7 @@ export namespace DocUtils {
icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), backgroundColor: "#ACCEF7",
_width: 15, _height: 15, _xPadding: 0, _isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null)
});
- Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin);
+ Doc.AddDocToList(context, annotationField, pushpin);
const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
doc._timecodeToShow = undefined;
return pushpin;
diff --git a/src/client/util/CaptureManager.scss b/src/client/util/CaptureManager.scss
new file mode 100644
index 000000000..71539ee1e
--- /dev/null
+++ b/src/client/util/CaptureManager.scss
@@ -0,0 +1,175 @@
+@import "../views/globalCssVariables";
+
+.capture-interface {
+ //background-color: whitesmoke !important;
+ width: 450px;
+
+ button {
+ background: #315a96;
+ outline: none;
+ border-radius: 5px;
+ border: 0px;
+ color: #fcfbf7;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ font-size: 75%;
+ padding: 10px;
+ margin: 10px;
+ transition: transform 0.2s;
+ margin: 2px;
+ }
+}
+
+.capture-t1 {
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ font-size: 20px;
+ font-family: 'Roboto';
+ font-weight: 600;
+ color: black;
+
+ .recordButtonOutline {
+ border-radius: 100%;
+ width: 25px;
+ height: 25px;
+ margin-right: 10px;
+ border: solid 1px #a94442;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .recordButtonInner {
+ border-radius: 100%;
+ width: 70%;
+ height: 70%;
+ background: #a94442;
+ }
+}
+
+.capture-t2 {
+ font-size: 12px;
+ padding-right: 15px;
+ color: black;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ /* right: 135; */
+ // position: absolute;
+ // left: 243;
+}
+
+.capture-block {
+ display: block;
+ padding-bottom: 8px;
+ padding-top: 6px;
+
+ .capture-block-title {
+ font-size: 16;
+ font-weight: bold;
+ text-align: left;
+ color: black;
+ width: 80;
+ margin-right: 50px;
+ margin-bottom: 5px;
+ }
+
+ .capture-block-list {
+ height: 135px;
+ width: calc(100% + 15px);
+ overflow: scroll;
+ }
+
+ .capture-block-radio {
+ font-size: 12;
+ display: block;
+ font-weight: normal;
+
+ .radio-container {
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ font-size: 13px;
+ font-family: 'Roboto';
+ }
+ }
+
+ .list-item {
+ display: flex;
+ height: 25px;
+ font-family: 'Roboto';
+ font-size: 13px;
+
+ .number {
+ width: 20px;
+ height: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: #BDDBE8;
+ border-radius: 100%;
+ font-weight: 800;
+ margin-right: 5px;
+ }
+ }
+
+ .buttons {
+ display: flex;
+ position: absolute;
+ bottom: 0;
+ right: 15;
+ justify-content: flex-end;
+ align-items: center;
+ height: 60px;
+
+ .save {
+ cursor: pointer;
+ width: 80px;
+ height: 40px;
+ font-size: 14px;
+ display: flex;
+ font-weight: bold;
+ justify-content: center;
+ align-items: center;
+ background: #337ab7;
+ color: whitesmoke;
+ border-radius: 5px;
+ text-transform: uppercase;
+ }
+
+ .cancel {
+ cursor: pointer;
+ width: 80px;
+ height: 40px;
+ font-size: 14px;
+ display: flex;
+ font-weight: 100;
+ justify-content: center;
+ align-items: center;
+ background: #ccc;
+ color: black;
+ border-radius: 5px;
+ text-transform: uppercase;
+ margin-left: 10px;
+ }
+ }
+}
+
+.close-button {
+ position: absolute;
+ top: 10;
+ right: 10;
+ background:transparent;
+ border-radius:100%;
+ width: 25px;
+ height: 25px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: 0.2s;
+}
+
+.close-button:hover {
+ background: rgba(0,0,0,0.1);
+}
+
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
new file mode 100644
index 000000000..c38337c91
--- /dev/null
+++ b/src/client/util/CaptureManager.tsx
@@ -0,0 +1,140 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { convertToObject } from "typescript";
+import { Doc, DocListCast } from "../../fields/Doc";
+import { BoolCast, StrCast, Cast } from "../../fields/Types";
+import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
+import { LightboxView } from "../views/LightboxView";
+import { MainViewModal } from "../views/MainViewModal";
+import "./CaptureManager.scss";
+import { SelectionManager } from "./SelectionManager";
+import { undoBatch } from "./UndoManager";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+export class CaptureManager extends React.Component<{}> {
+ public static Instance: CaptureManager;
+ static _settingsStyle = addStyleSheet();
+ @observable _document: any;
+ @observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
+
+
+ constructor(props: {}) {
+ super(props);
+ CaptureManager.Instance = this;
+ }
+
+ public close = action(() => this.isOpen = false);
+ public open = action((doc: Doc) => {
+ this.isOpen = true;
+ this._document = doc;
+ });
+
+
+ @computed get visibilityContent() {
+
+ return <div className="capture-block">
+ <div className="capture-block-title">Visibility</div>
+ <div className="capture-block-radio">
+ <div className="radio-container">
+ <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
+ </div>
+ <div className="radio-container">
+ <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+ </div>
+ </div>
+ </div>;
+ }
+
+ @computed get linksContent() {
+ const doc = this._document;
+ const order: JSX.Element[] = [];
+ if (doc) {
+ console.log('title', doc.title);
+ console.log('links', doc.links);
+ const linkDocs = DocListCast(doc.links);
+ const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
+ const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
+ linkDocs.forEach((l, i) => {
+ if (l) {
+ console.log(i, (l.anchor1 as Doc).title);
+ console.log(i, (l.anchor2 as Doc).title);
+ order.push(
+ <div className="list-item">
+ <div className="number">{i}</div>
+ {(l.anchor1 as Doc).title}
+ </div>
+ );
+ }
+ });
+ }
+
+ return <div className="capture-block">
+ <div className="capture-block-title">Links</div>
+ <div className="capture-block-list">
+ {order}
+ </div>
+ </div>;
+ }
+
+ @computed get closeButtons() {
+ return <div className="capture-block">
+ <div className="buttons">
+ <div className="save" onClick={() => {
+ LightboxView.SetLightboxDoc(this._document);
+ this.close();
+ }}>
+ Save
+ </div>
+ <div className="cancel" onClick={() => {
+ const selected = SelectionManager.Views().slice();
+ SelectionManager.DeselectAll();
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+ this.close();
+ }}>
+ Cancel
+ </div>
+ </div>
+ </div>
+ }
+
+
+
+ private get captureInterface() {
+ return <div className="capture-interface">
+ <div className="capture-t1">
+ <div className="recordButtonOutline" style={{}}>
+ <div className="recordButtonInner" style={{}}>
+ </div>
+ </div>
+ Conversation Capture
+ </div>
+ <div className="capture-t2">
+
+ </div>
+ {this.visibilityContent}
+ {this.linksContent}
+ <div className="close-button" onClick={this.close}>
+ <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+ </div>
+ {this.closeButtons}
+ </div>;
+
+ }
+
+ render() {
+ return <MainViewModal
+ contents={this.captureInterface}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ closeOnExternalClick={this.close}
+ dialogueBoxStyle={{ width: "500px", height: "350px", border: "none", background: "whitesmoke" }}
+ overlayStyle={{ background: "black" }}
+ overlayDisplayedOpacity={0.6}
+ />
+ }
+} \ No newline at end of file
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0dc8cc5c0..14bb87e89 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -423,7 +423,7 @@ export class CurrentUserUtils {
((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyScreenshot === undefined) {
- doc.emptyScreenshot = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, _width: 400, _height: 200, system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyWall === undefined) {
doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) });
@@ -516,13 +516,13 @@ export class CurrentUserUtils {
static async menuBtnDescriptions(doc: Doc) {
return [
{ title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
- { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+ { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' },
+ { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
{ title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+ { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
{ title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
- { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
- { title: "Filter", target: Cast(doc.myFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' },
+ // { title: "Filter", target: Cast(doc.currentFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' },
{ title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
- { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' },
{ title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
{ title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
{ title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
@@ -808,20 +808,19 @@ export class CurrentUserUtils {
}
}
static setupFilterDocs(doc: Doc) {
- // setup Recently Closed library item
- doc.myFilter === undefined;
- if (doc.myFilter === undefined) {
- doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({
- title: "FilterDoc",
+ // setup Filter item
+ if (doc.currentFilter === undefined) {
+ doc.currentFilter = Docs.Create.FilterDocument({
+ title: "unnamed filter", _height: 150,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
treeViewTruncateTitleWidth: 150, ignoreClick: true,
- _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
- }));
+ _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true
+ });
+ const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
+ (doc.currentFilter as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+ (doc.currentFilter as Doc).contextMenuLabels = new List<string>(["Clear All"]);
+ (doc.currentFilter as Doc).filterBoolean = "AND";
}
- const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([]); scriptContext._docFilters = scriptContext._docRangeFilters = undefined;`, { scriptContext: Doc.name });
- (doc.myFilter as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
- (doc.myFilter as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
-
}
static setupUserDoc(doc: Doc) {
@@ -853,7 +852,7 @@ export class CurrentUserUtils {
CurrentUserUtils.setupPresentations(doc);
CurrentUserUtils.setupFilesystem(doc);
CurrentUserUtils.setupRecentlyClosedDocs(doc);
- CurrentUserUtils.setupFilterDocs(doc);
+ // CurrentUserUtils.setupFilterDocs(doc);
CurrentUserUtils.setupUserDoc(doc);
}
@@ -1019,6 +1018,8 @@ export class CurrentUserUtils {
doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); //
Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]);
+ doc.savedFilters = new List<Doc>();
+ doc.filterDocCount = 0;
this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupImportSidebar(doc);
@@ -1170,10 +1171,10 @@ export class CurrentUserUtils {
CurrentUserUtils.openDashboard(userDoc, copy);
}
- public static createNewDashboard = (userDoc: Doc, id?: string) => {
- const myPresentations = userDoc.myPresentations as Doc;
+ public static createNewDashboard = async (userDoc: Doc, id?: string) => {
+ const myPresentations = await userDoc.myPresentations as Doc;
const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
- const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
+ const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
const dashboardCount = DocListCast(dashboards.data).length + 1;
const emptyPane = Cast(userDoc.emptyPane, Doc, null);
emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
@@ -1246,3 +1247,5 @@ Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Insta
"returns all the links to the document or its annotations", "(doc: any)");
Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
"imports files from device directly into the import sidebar");
+Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; },
+ "toggle between regular rendeing and an informal sketch/comic style");
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index c1235163b..d8c2f913e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -207,7 +207,7 @@ export namespace DragManager {
) {
const addAudioTag = (dropDoc: any) => {
dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField);
- dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc);
+ dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc);
return dropDoc;
};
const finishDrag = (e: DragCompleteEvent) => {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 68af67a3b..62d656c5d 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import Select from 'react-select';
@@ -284,8 +284,7 @@ export class GroupManager extends React.Component<{}> {
placeholder="Group name"
onChange={action(() => this.buttonColour = this.inputRef.current?.value ? "black" : "#979797")} />
<Select
- isMulti={true}
- isSearchable={true}
+ isMulti
options={this.options}
onChange={this.handleChange}
placeholder={"Select users"}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index c52127edd..ca5ef75d2 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -66,7 +66,7 @@ export namespace SelectionManager {
manager.SelectSchemaView(colSchema, document);
}
- const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
+ const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
return manager.SelectedViews.get(doc) ? true : false;
});
// computed functions, such as used in IsSelected generate errors if they're called outside of a
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index 5ca54517c..d8342ea56 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -75,8 +75,8 @@
width: 130;
color: black;
border-radius: 5px;
- padding:7px;
-
+ padding: 7px;
+
}
}
@@ -169,7 +169,7 @@
}
}
-.prefs-content{
+.prefs-content {
text-align: left;
}
@@ -210,6 +210,7 @@
.preferences-font-controls {
display: flex;
justify-content: space-between;
+ width: 130%;
}
.font-select {
@@ -429,11 +430,12 @@
font-size: 16px;
font-weight: bold;
margin-bottom: 16px;
- }
+ }
- .tab-column-title, .tab-column-content {
- padding-left: 16px;
- }
+ .tab-column-title,
+ .tab-column-content {
+ padding-left: 16px;
+ }
}
@@ -462,4 +464,4 @@
.settings-interface .settings-heading {
font-size: 25;
}
-}
+} \ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index ded56d1da..5a247730e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -7,7 +7,7 @@ import Select from "react-select";
import * as RequestPromise from "request-promise";
import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
-import { Cast, StrCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
@@ -78,7 +78,16 @@ export class SharingManager extends React.Component<{}> {
@observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
private populating: boolean = false; // whether the list of users is populating or not
@observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
- @observable private myDocAcls: boolean = false;
+ @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
+
+ // maps acl symbols to SharingPermissions
+ private AclMap = new Map<symbol, string>([
+ [AclPrivate, SharingPermissions.None],
+ [AclReadonly, SharingPermissions.View],
+ [AclAddonly, SharingPermissions.Add],
+ [AclEdit, SharingPermissions.Edit],
+ [AclAdmin, SharingPermissions.Admin]
+ ]);
// private get linkVisible() {
// return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
@@ -154,6 +163,7 @@ export class SharingManager extends React.Component<{}> {
});
}
}
+
/**
* Shares the document with a user.
*/
@@ -164,10 +174,16 @@ export class SharingManager extends React.Component<{}> {
const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
- // ! ensures it returns true if document has been shared successfully, false otherwise
return !docs.map(doc => {
doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc);
+
+ if (permission === SharingPermissions.None) {
+ if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
+ }
+ else {
+ if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
+ }
+
distributeAcls(acl, permission as SharingPermissions, doc);
if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
@@ -191,6 +207,14 @@ export class SharingManager extends React.Component<{}> {
// ! ensures it returns true if document has been shared successfully, false otherwise
return !docs.map(doc => {
doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc);
+
+ if (permission === SharingPermissions.None) {
+ if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
+ }
+ else {
+ if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
+ }
+
distributeAcls(acl, permission as SharingPermissions, doc);
if (group instanceof Doc) {
@@ -400,20 +424,12 @@ export class SharingManager extends React.Component<{}> {
}
distributeOverCollection = (targetDoc?: Doc) => {
- const AclMap = new Map<symbol, string>([
- [AclPrivate, SharingPermissions.None],
- [AclReadonly, SharingPermissions.View],
- [AclAddonly, SharingPermissions.Add],
- [AclEdit, SharingPermissions.Edit],
- [AclAdmin, SharingPermissions.Admin]
- ]);
-
const target = targetDoc || this.targetDoc!;
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
for (const [key, value] of Object.entries(doc[AclSym])) {
- distributeAcls(key, AclMap.get(value)! as SharingPermissions, target);
+ distributeAcls(key, this.AclMap.get(value)! as SharingPermissions, target);
}
});
}
@@ -474,7 +490,8 @@ export class SharingManager extends React.Component<{}> {
const targetDoc = docs[0];
// tslint:disable-next-line: no-unnecessary-callback-wrapper
- const admin = this.myDocAcls ? Boolean(docs.length) : docs.map(doc => GetEffectiveAcl(doc)).every(acl => acl === AclAdmin); // if the user has admin access to all selected docs
+ const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
+ const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
// users in common between all docs
const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
@@ -538,7 +555,7 @@ export class SharingManager extends React.Component<{}> {
<span className={"padding"}>Me</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
- {targetDoc?.[`acl-${Doc.CurrentUserEmailNormalized}`]}
+ {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : "-multiple-"}
</div>
</div>
</div>
@@ -548,7 +565,7 @@ export class SharingManager extends React.Component<{}> {
// the list of groups shared with
const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true);
- groupListMap.unshift({ title: "Public" }, { title: "Override" });
+ groupListMap.unshift({ title: "Public" });//, { title: "Override" });
const groupListContents = groupListMap.map(group => {
const groupKey = `acl-${StrCast(group.title)}`;
const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
@@ -600,9 +617,10 @@ export class SharingManager extends React.Component<{}> {
{<div className="share-container">
<div className="share-setup">
<Select
- className={"user-search"}
- placeholder={"Enter user or group name..."}
+ className="user-search"
+ placeholder="Enter user or group name..."
isMulti
+ isSearchable
closeMenuOnSelect={false}
options={options}
onChange={this.handleUsersChange}
@@ -626,16 +644,10 @@ export class SharingManager extends React.Component<{}> {
</div>
<div className="acl-container">
- <div className="myDocs-acls">
- <input type="checkbox" onChange={action(() => this.myDocAcls = !this.myDocAcls)} checked={this.myDocAcls} /> <label>My Docs</label>
- </div>
{Doc.UserDoc().noviceMode ? (null) :
<div className="layoutDoc-acls">
<input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
</div>}
- <button className="distribute-button" onClick={() => this.distributeOverCollection()}>
- Distribute
- </button>
</div>
</div>
}
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
index 3b23d5d56..ac0bea46a 100644
--- a/src/client/util/type_decls.d
+++ b/src/client/util/type_decls.d
@@ -215,5 +215,6 @@ declare const Docs: {
StackingDocument(documents: Doc[], options?: DocumentOptions): Doc;
};
+declare function idToDoc(id:string):any;
declare function assignDoc(doc:Doc, field:any, id:any):string;
declare function d(...args:any[]):any;
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index c9b5e7658..a275901be 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -7,7 +7,7 @@
height: $antimodemenu-height;
background: #323232;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
- border-radius: 0px 6px 6px 6px;
+ // border-radius: 0px 6px 6px 6px;
z-index: 1001;
display: flex;
@@ -24,6 +24,16 @@
background-color: transparent;
width: 35px;
height: 35px;
+ padding: 5;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+
+ .svg {
+ margin: 0;
+ }
&.active {
background-color: #121212;
@@ -31,7 +41,7 @@
}
.antimodeMenu-button:hover {
- background-color: #121212;
+ background-color: rgba(0, 0, 0, 0.4);
}
.antimodeMenu-dragger {
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 5acb3e4c8..fe6d39ca4 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -140,7 +140,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay,
position: this.Pinned ? "unset" : undefined
}}>
- <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: "20px" }} />
+ {/* {this.getDragger} */}
{buttons}
</div>
);
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 1f7abf61a..d96de72e3 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -15,6 +15,7 @@ export class ContextMenu extends React.Component {
@observable private _pageY: number = 0;
@observable private _display: boolean = false;
@observable private _searchString: string = "";
+ @observable private _showSearch: boolean = false;
// afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be
@observable private _yRelativeToTop: boolean = true;
@observable selectedIndex = -1;
@@ -137,11 +138,13 @@ export class ContextMenu extends React.Component {
return y;
}
+
@action
- displayMenu = (x: number, y: number, initSearch = "") => {
+ displayMenu = (x: number, y: number, initSearch = "", showSearch = false) => {
//maxX and maxY will change if the UI/font size changes, but will work for any amount
//of items added to the menu
+ this._showSearch = showSearch;
this._pageX = x;
this._pageY = y;
this._searchString = initSearch;
@@ -215,11 +218,15 @@ export class ContextMenu extends React.Component {
@computed get menuItems() {
if (!this._searchString) {
- return this._items.map(item => <ContextMenuItem {...item} key={item.description} closeMenu={this.closeMenu} />);
+ return this._items.map(item => <ContextMenuItem {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} key={item.description} closeMenu={this.closeMenu} />);
}
return this.filteredViews;
}
+ @computed get itemsNeedSearch() {
+ return this._showSearch ? 1 : this._items.reduce((p, mi) => p + ((mi as any).noexpand ? 1 : (mi as any).subitems?.length || 1), 0) > 15;
+ }
+
render() {
if (!this._display) {
return null;
@@ -229,12 +236,13 @@ export class ContextMenu extends React.Component {
const contents = (
<>
- <span className={"search-icon"}>
- <span className="icon-background">
- <FontAwesomeIcon icon="search" size="lg" />
- </span>
- <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
- </span>
+ {!this.itemsNeedSearch ? (null) :
+ <span className={"search-icon"}>
+ <span className="icon-background">
+ <FontAwesomeIcon icon="search" size="lg" />
+ </span>
+ <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
+ </span>}
{this.menuItems}
</>
);
@@ -244,8 +252,7 @@ export class ContextMenu extends React.Component {
<div className="contextMenu-cont" style={style} ref={measureRef}>
{contents}
</div>
- )
- }
+ )}
</Measure>
);
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index e63631161..6fe2abd21 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -4,7 +4,6 @@ import { observer } from "mobx-react";
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { UndoManager } from "../util/UndoManager";
-import { NumberLiteralType } from "typescript";
export interface OriginalMenuProps {
description: string;
@@ -33,8 +32,8 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
constructor(props: ContextMenuProps | SubmenuProps) {
super(props);
- if ("subitems" in this.props) {
- this.props.subitems?.forEach(i => this._items.push(i));
+ if ((this.props as SubmenuProps).subitems) {
+ (this.props as SubmenuProps).subitems?.forEach(i => this._items.push(i));
}
}
@@ -107,7 +106,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
}}>
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
- if (!("noexpand" in this.props)) {
+ if (!(this.props as SubmenuProps).noexpand) {
return <div className="contextMenu-inlineMenu">
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 5ff96ac8d..a878a7afb 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -124,8 +124,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || "";
};
divKeys.map((prop: string) => {
- const p = (this.props as any)[prop] as string;
- p && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
+ const p = (this.props as any)[prop];
+ typeof p === "string" && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
});
return style;
}
@@ -151,7 +151,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
if (toRemove.length !== 0) {
const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
toRemove.forEach(doc => {
- leavePushpin && DocUtils.LeavePushpin(doc);
+ leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
doc.context = undefined;
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
@@ -182,7 +182,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
addDocument(doc: Doc | Doc[], annotationKey?: string): boolean {
const docs = doc instanceof Doc ? [doc] : doc;
if (this.props.filterAddDocument?.(docs) === false ||
- docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) {
+ docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
return false;
}
const targetDataDoc = this.props.Document[DataSym];
@@ -216,7 +216,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
else {
added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document
this.props.layerProvider?.(doc, true);
- DocUtils.LeavePushpin(doc);
+ //DocUtils.LeavePushpin(doc);
doc._stayInCollection = undefined;
doc.context = this.props.Document;
if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ce7ff1bbd..24ca3a8fa 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { Document } from '../../fields/documentSchemas';
import { HtmlField } from '../../fields/HtmlField';
import { InkField } from "../../fields/InkField";
import { ScriptField } from '../../fields/ScriptField';
-import { Cast, NumCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
import { GetEffectiveAcl } from '../../fields/util';
import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
import { Docs, DocUtils } from "../documents/Documents";
@@ -26,6 +26,7 @@ import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { DocumentView } from "./nodes/DocumentView";
import React = require("react");
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
@observer
export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> {
@@ -83,7 +84,21 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
titleFieldKey === "title" && (d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
//@ts-ignore
- Doc.SetInPlace(d.rootDoc, titleFieldKey, +this._accumulatedTitle == this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle, true);
+ const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
+ Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
+
+ if (d.rootDoc.syncLayoutFieldWithTitle) {
+ const title = titleField.toString();
+ const curKey = Doc.LayoutFieldKey(d.rootDoc);
+ if (curKey !== title && d.dataDoc[title] === undefined) {
+ d.rootDoc.layout = FormattedTextBox.LayoutString(title);
+ setTimeout(() => {
+ const val = d.dataDoc[curKey];
+ d.dataDoc[curKey] = undefined;
+ d.dataDoc[title] = val;
+ });
+ }
+ }
}), "title blur");
}
}
diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss
index e912bc85a..5dc0c1962 100644
--- a/src/client/views/EditableView.scss
+++ b/src/client/views/EditableView.scss
@@ -8,6 +8,8 @@
}
.editableView-container-editing-oneLine {
+ width: 100%;
+
span {
white-space: nowrap;
overflow: hidden;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 671c0c507..cbaa706e0 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -29,6 +29,7 @@ import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
import { AnchorMenu } from "./pdf/AnchorMenu";
import { SearchBox } from "./search/SearchBox";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { SettingsManager } from "../util/SettingsManager";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -135,6 +136,7 @@ export class KeyManager {
// DictationManager.Controls.stop();
GoogleAuthenticationManager.Instance.cancel();
SharingManager.Instance.close();
+ if (!GroupManager.Instance.isOpen) SettingsManager.Instance.close();
GroupManager.Instance.close();
CollectionFreeFormViewChrome.Instance?.clearKeepPrimitiveMode();
window.getSelection()?.empty();
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index e33b3b35e..ce36d9182 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -14,7 +14,7 @@ import { Transform } from '../util/Transform';
import { TabDocView } from './collections/TabDocView';
import "./LightboxView.scss";
import { DocumentView, ViewAdjustment } from './nodes/DocumentView';
-import { DefaultStyleProvider } from './StyleProvider';
+import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
interface LightboxViewProps {
PanelWidth: number;
@@ -26,6 +26,8 @@ interface LightboxViewProps {
export class LightboxView extends React.Component<LightboxViewProps> {
@computed public static get LightboxDoc() { return this._doc; }
+ private static LightboxDocTemplate = () => LightboxView._layoutTemplate;
+ @observable private static _layoutTemplate: Opt<Doc>;
@observable private static _doc: Opt<Doc>;
@observable private static _docTarget: Opt<Doc>;
@observable private static _docFilters: string[] = []; // filters
@@ -35,7 +37,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
private static _future: Opt<Doc[]> = [];
private static _docView: Opt<DocumentView>;
static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }> }[] = [];
- @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[]) {
+ @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) {
if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
this.LightboxDoc._panX = this._savedState.panX;
this.LightboxDoc._panY = this._savedState.panY;
@@ -62,6 +64,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this._future = future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length);
}
this._doc = doc;
+ this._layoutTemplate = layoutTemplate;
this._docTarget = target || doc;
this._tourMap = DocListCast(doc?.links).map(link => {
const opp = LinkManager.getOppositeAnchor(link, doc!);
@@ -101,13 +104,13 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this._docFilters = (f => this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f])(`cookies:${cookie}:provide`);
}
}
- public static AddDocTab = (doc: Doc, location: string) => {
+ public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc) => {
SelectionManager.DeselectAll();
return LightboxView.SetLightboxDoc(doc, undefined,
[...DocListCast(doc[Doc.LayoutFieldKey(doc)]),
...DocListCast(doc[Doc.LayoutFieldKey(doc) + "-annotations"]),
...(LightboxView._future ?? [])
- ].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)));
+ ].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)), layoutTemplate);
}
docFilters = () => LightboxView._docFilters || [];
addDocTab = LightboxView.AddDocTab;
@@ -116,7 +119,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
const target = LightboxView._docTarget = LightboxView._future?.pop();
const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
if (targetDocView && target) {
- const l = DocUtils.MakeLinkToActiveAudio(targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
+ const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen");
targetDocView.focus(target, { originalTarget: target, willZoom: true, scale: 0.9 });
if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
@@ -214,7 +217,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
left: this.leftBorder,
top: this.topBorder,
width: this.lightboxWidth(),
- height: this.lightboxHeight()
+ height: this.lightboxHeight(),
+ clipPath: `path('${Doc.UserDoc().renderStyle === "comic" ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`
}}>
<DocumentView ref={action((r: DocumentView | null) => {
LightboxView._docView = r !== null ? r : undefined;
@@ -229,6 +233,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
})}
Document={LightboxView.LightboxDoc}
DataDoc={undefined}
+ LayoutTemplate={LightboxView.LightboxDocTemplate}
addDocument={undefined}
fitContentsToDoc={this.fitToBox}
isDocumentActive={returnFalse}
@@ -282,7 +287,7 @@ interface LightboxTourBtnProps {
export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
render() {
return this.props.navBtn("50%", 0, 0, "chevron-down",
- () => LightboxView.LightboxDoc && this.props.future()?.length ? "" : "none", e => {
+ () => LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? "" : "none", e => {
e.stopPropagation();
this.props.stepInto();
},
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 8ccb64744..3f04a0f3a 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -358,9 +358,6 @@
.mainView-libraryFlyout-out {
transition: width .25s;
box-shadow: rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw;
- .mainView-docButtons {
- left: 0;
- }
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 204ec370f..e54d84a6c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -17,6 +17,7 @@ import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, return
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocUtils } from '../documents/Documents';
+import { CaptureManager } from '../util/CaptureManager';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
import { DocumentManager } from '../util/DocumentManager';
import { GroupManager } from '../util/GroupManager';
@@ -45,6 +46,7 @@ import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
+import "./collections/TreeView.scss";
import { AudioBox } from './nodes/AudioBox';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView, DocumentViewProps, DocAfterFocusFunc } from './nodes/DocumentView';
@@ -60,7 +62,7 @@ import { AnchorMenu } from './pdf/AnchorMenu';
import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
import { SearchBox } from './search/SearchBox';
-import { DefaultStyleProvider, StyleProp } from './StyleProvider';
+import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider';
const _global = (window /* browser */ || global /* node */) as any;
@observer
@@ -166,7 +168,8 @@ export class MainView extends React.Component {
fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
- fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines);
+ fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
+ fa.faSave, fa.faBookmark);
this.initAuthenticationRouters();
}
@@ -272,7 +275,11 @@ export class MainView extends React.Component {
@computed get dockingContent() {
return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
- style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)` }}>
+ style={{
+ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`,
+ transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
+ width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`
+ }}>
{!this.mainContainer ? (null) : this.mainDocView}
</div>;
}
@@ -619,6 +626,7 @@ export class MainView extends React.Component {
<DictationOverlay />
<SharingManager />
<SettingsManager />
+ <CaptureManager />
<GroupManager />
<GoogleAuthenticationManager />
<DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
@@ -696,5 +704,4 @@ export class MainView extends React.Component {
}
}
-Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
-Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); \ No newline at end of file
+Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); \ No newline at end of file
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 7f91c0079..55dee005d 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -34,7 +34,7 @@ export class MainViewModal extends React.Component<MainViewOverlayProps> {
<div className="overlay"
onClick={this.props?.closeOnExternalClick}
style={{
- backgroundColor: "gainsboro",
+ backgroundColor: "black",
...(p.overlayStyle || {}),
opacity: p.isDisplayed ? overlayOpacity : 0
}}
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 1365725cb..fa45a065d 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -157,6 +157,66 @@
}
}
+ .propertiesView-filters {
+ //border-bottom: 1px solid black;
+ //padding: 8.5px;
+
+ .propertiesView-filters-title {
+ font-weight: bold;
+ font-size: 12.5px;
+ padding: 4px;
+ display: flex;
+ color: white;
+ padding-left: 8px;
+ background-color: rgb(51, 51, 51);
+
+ &:hover {
+ cursor: pointer;
+ }
+
+ .propertiesView-filters-title-icon {
+ float: right;
+ justify-items: right;
+ align-items: flex-end;
+ margin-left: auto;
+ margin-right: 9px;
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ .propertiesView-filters-content {
+ font-size: 10px;
+ padding: 10px;
+ margin-left: 5px;
+ // max-height: 40%;// overflow-y: scroll;
+
+ .propertiesView-buttonContainer {
+ float: right;
+ display: flex;
+
+ button {
+ width: 15;
+ height: 15;
+ padding: 0;
+ margin-top: -5;
+ }
+ }
+
+ button {
+ width: 5;
+ height: 5;
+ }
+
+ input {
+ width: 100%;
+ }
+ }
+ }
+
+
.propertiesView-appearance {
//border-bottom: 1px solid black;
//padding: 8.5px;
@@ -332,6 +392,7 @@
}
}
}
+
.propertiesView-fields {
//border-bottom: 1px solid black;
//padding: 8.5px;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c8ce8bfeb..bb0ad4c66 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -2,10 +2,10 @@ import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Checkbox, Tooltip } from "@material-ui/core";
import { intersection } from "lodash";
-import { action, computed, observable } from "mobx";
+import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
-import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { InkField } from "../../fields/InkField";
import { ComputedField } from "../../fields/ScriptField";
@@ -29,6 +29,8 @@ import { PropertiesButtons } from "./PropertiesButtons";
import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
import { DefaultStyleProvider } from "./StyleProvider";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { List } from "../../fields/List";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -46,15 +48,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get MAX_EMBED_HEIGHT() { return 200; }
- @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
+ @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || CurrentUserUtils.ActiveDashboard; }
@computed get selectedDocumentView() {
if (SelectionManager.Views().length) return SelectionManager.Views()[0];
if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
return undefined;
}
@computed get isPres(): boolean {
- if (this.selectedDoc?.type === DocumentType.PRES) return true;
- return false;
+ return this.selectedDoc?.type === DocumentType.PRES;
}
@computed get dataDoc() { return this.selectedDoc?.[DataSym]; }
@@ -67,6 +68,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@observable openContexts: boolean = true;
@observable openAppearance: boolean = true;
@observable openTransform: boolean = true;
+ @observable openFilters: boolean = true; // should be false
+
+ /**
+ * autorun to set up the filter doc of a collection if that collection has been selected and the filters panel is open
+ */
+ private selectedDocListenerDisposer: Opt<Lambda>;
+
// @observable selectedUser: string = "";
// @observable addButtonPressed: boolean = false;
@observable layoutDocAcls: boolean = false;
@@ -81,6 +89,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@observable _controlBtn: boolean = false;
@observable _lock: boolean = false;
+ componentDidMount() {
+ this.selectedDocListenerDisposer?.();
+ this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc());
+ }
+
+ componentWillUnmount() {
+ this.selectedDocListenerDisposer?.();
+ }
+
@computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; }
rtfWidth = () => {
@@ -149,6 +166,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
<EditableView
key="editableView"
+ oneLine
contents={"add key:value or #tags"}
height={13}
fontSize={10}
@@ -205,6 +223,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
<EditableView
key="editableView"
+ oneLine
contents={"add key:value or #tags"}
height={13}
fontSize={10}
@@ -400,7 +419,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
// users in common between all docs
- const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym])));
+ const commonKeys: string[] = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym])));
const tableEntries = [];
@@ -417,7 +436,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
// shifts the current user, owner, public to the top of the doc.
- tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
+ // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Public"] === docs[0]["acl-Public"]) ? (AclMap.get(target[AclSym]?.["acl-Public"]) || SharingPermissions.None) : "-multiple-"));
tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-", !ownerSame));
if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner"));
@@ -444,13 +463,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const titles = new Set<string>();
SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title);
- return <div className="editable-title"><EditableView
- key="editableView"
- contents={title}
- height={25}
- fontSize={14}
- GetValue={() => title}
- SetValue={this.setTitle} /> </div>;
+ return <div className="editable-title">
+ <EditableView
+ key="editableView"
+ contents={title}
+ height={25}
+ fontSize={14}
+ GetValue={() => title}
+ SetValue={this.setTitle} />
+ </div>;
}
@undoBatch
@@ -514,8 +535,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
}
-
-
@computed
get controlPointsButton() {
const formatInstance = InkStrokeProperties.Instance;
@@ -831,6 +850,232 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>;
}
+ @computed get optionsSubMenu() {
+ return <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
+ onPointerLeave={action(() => this.inOptions = false)}>
+ <div className="propertiesView-settings-title"
+ onPointerDown={action(() => this.openOptions = !this.openOptions)}
+ style={{ backgroundColor: this.openOptions ? "black" : "" }}>
+ Options
+ <div className="propertiesView-settings-title-icon">
+ <FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {!this.openOptions ? (null) :
+ <div className="propertiesView-settings-content">
+ <PropertiesButtons />
+ </div>}
+ </div>;
+ }
+
+ @computed get sharingSubMenu() {
+ return <div className="propertiesView-sharing">
+ <div className="propertiesView-sharing-title"
+ onPointerDown={action(() => this.openSharing = !this.openSharing)}
+ style={{ backgroundColor: this.openSharing ? "black" : "" }}>
+ Sharing {"&"} Permissions
+ <div className="propertiesView-sharing-title-icon">
+ <FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {!this.openSharing ? (null) :
+ <div className="propertiesView-sharing-content">
+ <div className="propertiesView-buttonContainer">
+ {!Doc.UserDoc().noviceMode ? (<div className="propertiesView-acls-checkbox">
+ <Checkbox
+ color="primary"
+ onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
+ checked={this.layoutDocAcls}
+ />
+ <div className="propertiesView-acls-checkbox-text">Layout</div>
+ </div>) : (null)}
+ {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
+ <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
+ </button>
+ </Tooltip> */}
+ </div>
+ {this.sharingTable}
+ </div>}
+ </div>;
+ }
+
+ /**
+ * Checks if a currentFilter (FilterDoc) exists on the current collection (if the Properties Panel + Filters submenu are open).
+ * If it doesn't exist, it creates it.
+ */
+ checkFilterDoc() {
+ if (this.selectedDoc.type === DocumentType.COL && !this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+ }
+
+ /**
+ * Creates a new currentFilter for this.filterDoc,
+ */
+ createNewFilterDoc = () => {
+ const currentDocFilters = this.selectedDoc._docFilters;
+ const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+ this.selectedDoc._docFilters = new List<string>();
+ this.selectedDoc._docRangeFilters = new List<string>();
+ (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+ (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+ this.selectedDoc.currentFilter = undefined;
+ CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+ }
+
+ /**
+ * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
+ */
+ updateFilterDoc = (doc: Doc) => {
+ if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
+ const savedDocFilters = doc._docFiltersList;
+ const currentDocFilters = this.selectedDoc._docFilters;
+ this.selectedDoc._docFilters = new List<string>();
+ (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+ this.selectedDoc.currentFilter = doc;
+ doc._docFiltersList = new List<string>();
+ this.selectedDoc._docFilters = savedDocFilters;
+
+ const savedDocRangeFilters = doc._docRangeFiltersList;
+ const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+ this.selectedDoc._docRangeFilters = new List<string>();
+ (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+ this.selectedDoc.currentFilter = doc;
+ doc._docRangeFiltersList = new List<string>();
+ this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+ }
+
+ @computed get filtersSubMenu() {
+ return !(this.selectedDoc?.currentFilter instanceof Doc) ? (null) : <div className="propertiesView-filters">
+ <div className="propertiesView-filters-title"
+ onPointerDown={action(() => this.openFilters = !this.openFilters)}
+ style={{ backgroundColor: this.openFilters ? "black" : "" }}>
+ Filters
+ <div className="propertiesView-filters-title-icon">
+ <FontAwesomeIcon icon={this.openFilters ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {
+ !this.openFilters ? (null) :
+ <div className="propertiesView-filters-content" style={{ height: this.selectedDoc.currentFilter[HeightSym]() + 15 }}>
+ <DocumentView
+ Document={this.selectedDoc.currentFilter}
+ DataDoc={undefined}
+ addDocument={undefined}
+ addDocTab={returnFalse}
+ pinToPres={emptyFunction}
+ rootSelected={returnTrue}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={this.getTransform}
+ PanelWidth={() => this.props.width}
+ PanelHeight={this.selectedDoc.currentFilter[HeightSym]}
+ renderDepth={0}
+ scriptContext={this.selectedDoc.currentFilter}
+ focus={emptyFunction}
+ styleProvider={DefaultStyleProvider}
+ isContentActive={returnTrue}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ createNewFilterDoc={this.createNewFilterDoc}
+ updateFilterDoc={this.updateFilterDoc}
+ docViewPath={returnEmptyDoclist}
+ layerProvider={undefined}
+ dontCenter="y"
+ />
+ </div>
+ }
+ </div >;
+ }
+
+ @computed get inkSubMenu() {
+ return <>
+ {!this.isInk ? (null) :
+ <div className="propertiesView-appearance">
+ <div className="propertiesView-appearance-title"
+ onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
+ style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
+ Appearance
+ <div className="propertiesView-appearance-title-icon">
+ <FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {!this.openAppearance ? (null) :
+ <div className="propertiesView-appearance-content">
+ {this.appearanceEditor}
+ </div>}
+ </div>}
+
+ {this.isInk ? <div className="propertiesView-transform">
+ <div className="propertiesView-transform-title"
+ onPointerDown={action(() => this.openTransform = !this.openTransform)}
+ style={{ backgroundColor: this.openTransform ? "black" : "" }}>
+ Transform
+ <div className="propertiesView-transform-title-icon">
+ <FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openTransform ? <div className="propertiesView-transform-content">
+ {this.transformEditor}
+ </div> : null}
+ </div> : null}
+ </>;
+ }
+
+ @computed get fieldsSubMenu() {
+ return <div className="propertiesView-fields">
+ <div className="propertiesView-fields-title"
+ onPointerDown={action(() => this.openFields = !this.openFields)}
+ style={{ backgroundColor: this.openFields ? "black" : "" }}>
+ Fields {"&"} Tags
+ <div className="propertiesView-fields-title-icon">
+ <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {!Doc.UserDoc().noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
+ {this.fieldsCheckbox}
+ <div className="propertiesView-fields-checkbox-text">Layout</div>
+ </div> : null}
+ {!this.openFields ? (null) :
+ <div className="propertiesView-fields-content">
+ {Doc.UserDoc().noviceMode ? this.noviceFields : this.expandedField}
+ </div>}
+ </div>;
+ }
+
+ @computed get contextsSubMenu() {
+ return <div className="propertiesView-contexts">
+ <div className="propertiesView-contexts-title"
+ onPointerDown={action(() => this.openContexts = !this.openContexts)}
+ style={{ backgroundColor: this.openContexts ? "black" : "" }}>
+ Contexts
+ <div className="propertiesView-contexts-title-icon">
+ <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openContexts ? <div className="propertiesView-contexts-content" >{this.contexts}</div> : null}
+ </div>;
+ }
+
+ @computed get layoutSubMenu() {
+ return <div className="propertiesView-layout">
+ <div className="propertiesView-layout-title"
+ onPointerDown={action(() => this.openLayout = !this.openLayout)}
+ style={{ backgroundColor: this.openLayout ? "black" : "" }}>
+ Layout
+ <div className="propertiesView-layout-title-icon">
+ <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
+ </div>
+ </div>
+ {this.openLayout ? <div className="propertiesView-layout-content" >{this.layoutPreview}</div> : null}
+ </div>;
+ }
+
+
+
/**
* Handles adding and removing members from the sharing panel
*/
@@ -851,8 +1096,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>;
} else {
- const novice = Doc.UserDoc().noviceMode;
-
if (this.selectedDoc && !this.isPres) {
return <div className="propertiesView" style={{
width: this.props.width,
@@ -865,121 +1108,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-name">
{this.editableTitle}
</div>
- <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
- onPointerLeave={action(() => this.inOptions = false)}>
- <div className="propertiesView-settings-title"
- onPointerDown={action(() => this.openOptions = !this.openOptions)}
- style={{ backgroundColor: this.openOptions ? "black" : "" }}>
- Options
- <div className="propertiesView-settings-title-icon">
- <FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {!this.openOptions ? (null) :
- <div className="propertiesView-settings-content">
- <PropertiesButtons />
- </div>}
- </div>
- <div className="propertiesView-sharing">
- <div className="propertiesView-sharing-title"
- onPointerDown={action(() => this.openSharing = !this.openSharing)}
- style={{ backgroundColor: this.openSharing ? "black" : "" }}>
- Sharing {"&"} Permissions
- <div className="propertiesView-sharing-title-icon">
- <FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {!this.openSharing ? (null) :
- <div className="propertiesView-sharing-content">
- <div className="propertiesView-buttonContainer">
- {!novice ? (<div className="propertiesView-acls-checkbox">
- <Checkbox
- color="primary"
- onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
- checked={this.layoutDocAcls}
- />
- <div className="propertiesView-acls-checkbox-text">Layout</div>
- </div>) : (null)}
- <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
- <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
- <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
- </button>
- </Tooltip>
- </div>
- {this.sharingTable}
- </div>}
- </div>
+ {this.optionsSubMenu}
- {!this.isInk ? (null) :
- <div className="propertiesView-appearance">
- <div className="propertiesView-appearance-title"
- onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
- style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
- Appearance
- <div className="propertiesView-appearance-title-icon">
- <FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {!this.openAppearance ? (null) :
- <div className="propertiesView-appearance-content">
- {this.appearanceEditor}
- </div>}
- </div>}
+ {this.sharingSubMenu}
- {this.isInk ? <div className="propertiesView-transform">
- <div className="propertiesView-transform-title"
- onPointerDown={action(() => this.openTransform = !this.openTransform)}
- style={{ backgroundColor: this.openTransform ? "black" : "" }}>
- Transform
- <div className="propertiesView-transform-title-icon">
- <FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {this.openTransform ? <div className="propertiesView-transform-content">
- {this.transformEditor}
- </div> : null}
- </div> : null}
+ {this.filtersSubMenu}
- <div className="propertiesView-fields">
- <div className="propertiesView-fields-title"
- onPointerDown={action(() => this.openFields = !this.openFields)}
- style={{ backgroundColor: this.openFields ? "black" : "" }}>
- Fields {"&"} Tags
- <div className="propertiesView-fields-title-icon">
- <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {!novice && this.openFields ? <div className="propertiesView-fields-checkbox">
- {this.fieldsCheckbox}
- <div className="propertiesView-fields-checkbox-text">Layout</div>
- </div> : null}
- {!this.openFields ? (null) :
- <div className="propertiesView-fields-content">
- {novice ? this.noviceFields : this.expandedField}
- </div>}
- </div>
- <div className="propertiesView-contexts">
- <div className="propertiesView-contexts-title"
- onPointerDown={action(() => this.openContexts = !this.openContexts)}
- style={{ backgroundColor: this.openContexts ? "black" : "" }}>
- Contexts
- <div className="propertiesView-contexts-title-icon">
- <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {this.openContexts ? <div className="propertiesView-contexts-content" >{this.contexts}</div> : null}
- </div>
- <div className="propertiesView-layout">
- <div className="propertiesView-layout-title"
- onPointerDown={action(() => this.openLayout = !this.openLayout)}
- style={{ backgroundColor: this.openLayout ? "black" : "" }}>
- Layout
- <div className="propertiesView-layout-title-icon">
- <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
- </div>
- </div>
- {this.openLayout ? <div className="propertiesView-layout-content" >{this.layoutPreview}</div> : null}
- </div>
+ {this.inkSubMenu}
+
+ {this.fieldsSubMenu}
+
+ {this.contextsSubMenu}
+
+ {this.layoutSubMenu}
</div>;
}
if (this.isPres) {
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 6c3eb1e95..bff4c95fc 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -113,8 +113,6 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
NativeHeight={returnZero}
PanelHeight={this.panelHeight}
PanelWidth={this.panelWidth}
- xMargin={0}
- yMargin={0}
styleProvider={this.sidebarStyleProvider}
docFilters={this.docFilters}
scaleField={this.sidebarKey() + "-scale"}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 2f0bbc4a8..96f9eab97 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -14,14 +14,18 @@ import { CollectionViewType } from './collections/CollectionView';
import { MainView } from './MainView';
import { DocumentViewProps } from "./nodes/DocumentView";
import "./StyleProvider.scss";
+import "./collections/TreeView.scss";
+import "./nodes/FilterBox.scss";
import React = require("react");
import Color = require('color');
+import { FieldViewProps } from './nodes/FieldView';
export enum StyleLayers {
Background = "background"
}
export enum StyleProp {
+ TreeViewIcon = "treeViewIcon",
DocContents = "docContents", // when specified, the JSX returned will replace the normal rendering of the document view
Opacity = "opacity", // opacity of the document view
Hidden = "hidden", // whether the document view should not be isplayed
@@ -37,6 +41,8 @@ export enum StyleProp {
HeaderMargin = "headerMargin", // margin at top of documentview, typically for displaying a title -- doc contents will start below that
TitleHeight = "titleHeight", // Height of Title area
ShowTitle = "showTitle", // whether to display a title on a Document (optional :hover suffix)
+ JitterRotation = "jitterRotation", // whether documents should be randomly rotated
+ BorderPath = "customBorder", // border path for document view
}
function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@@ -56,21 +62,28 @@ export function testDocProps(toBeDetermined: any): toBeDetermined is DocumentVie
return (toBeDetermined?.isContentActive) ? toBeDetermined : undefined;
}
-//
+export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
+ return `M ${pw * .5} ${ph * inset} C ${pw * .6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * .25} ${pw * (1 - inset)} ${ph * .3} C ${pw * (1 - inset)} ${ph * .4} ${pw} ${ph * (1 - 2 * inset)} ${pw * (1 - inset)} ${ph * (1 - inset)} C ${pw * (1 - 2 * inset)} ${ph} ${pw * .6} ${ph * (1 - inset)} ${pw * .5} ${ph * (1 - inset)} C ${pw * .3} ${ph * (1 - inset)} ${pw * (2 * inset)} ${ph} ${pw * inset} ${ph * (1 - inset)} C 0 ${ph * (1 - 2 * inset)} ${pw * inset} ${ph * .8} ${pw * inset} ${ph * .75} C ${pw * inset} ${ph * .7} 0 ${ph * (2 * inset)} ${pw * inset} ${ph * inset} C ${pw * (2 * inset)} 0 ${pw * .25} ${ph * inset} ${pw * .5} ${ph * inset}`;
+}
+
// a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
//
export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any {
const docProps = testDocProps(props) ? props : undefined;
+ const fieldKey = (props as any)?.fieldKey ? (props as any).fieldKey + "-" : "";
const selected = property.includes(":selected");
const isCaption = property.includes(":caption");
const isAnchor = property.includes(":anchor");
const isAnnotated = property.includes(":annotated");
+ const isOpen = property.includes(":open");
+ const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic";
const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
-
+ const random = (min: number, max: number, x: number, y: number) => { /* min should not be equal to max */ return min + ((Math.abs(x * y) * 9301 + 49297) % 233280 / 233280) * (max - min); }
switch (property.split(":")[0]) {
+ case StyleProp.TreeViewIcon: return Doc.toIcon(doc, isOpen);
case StyleProp.DocContents: return undefined;
case StyleProp.WidgetColor: return isAnnotated ? "lightBlue" : darkScheme() ? "lightgrey" : "dimgrey";
case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
@@ -79,21 +92,24 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
!Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
(doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
case StyleProp.Color:
+ const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
+ if (docColor) return docColor;
if (isCaption) return "white";
- const backColor = backgroundCol() || "black";
+ const backColor = backgroundCol();// || (darkScheme() ? "black" : "white");
+ if (!backColor) return undefined;
const col = Color(backColor).rgb();
const colsum = (col.red() + col.green() + col.blue());
if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return "black";
return "white";
case StyleProp.Hidden: return BoolCast(doc?._hidden);
- case StyleProp.BorderRounding: return StrCast(doc?._borderRounding);
+ case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], StrCast(doc?._borderRounding));
case StyleProp.TitleHeight: return 15;
+ case StyleProp.BorderPath: return comicStyle() && props?.renderDepth ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
+ case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) ||
doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
case StyleProp.BackgroundColor: {
- if (isCaption) return "rgba(0,0,0 ,0.4)";
- if (Doc.UserDoc().renderStyle === "comic") return "transparent";
- let docColor: Opt<string> = StrCast(doc?._backgroundColor);
+ let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rbga(0,0,0,0.4)" : ""));
if (MainView.Instance.LastButton === doc) return darkScheme() ? "dimgrey" : "lightgrey";
switch (doc?.type) {
case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
@@ -124,7 +140,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
Doc.UserDoc().activeCollectionBackground));
break;
//if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
- default: docColor = darkScheme() ? "black" : "white"; break;
+ default: docColor = docColor || (darkScheme() ? "black" : "white"); break;
}
if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor.toLowerCase()).fade(0.5).toString();
return docColor;
@@ -158,6 +174,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
if (doc?.type !== DocumentType.INK && layer === true) return "all";
return undefined;
case StyleProp.Decorations:
+ // if (isFooter)
+
if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) {
return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
@@ -169,6 +187,60 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
}
}
+
+function toggleHidden(e: React.MouseEvent, doc: Doc) {
+ UndoManager.RunInBatch(() => runInAction(() => {
+ e.stopPropagation();
+ doc.hidden = doc.hidden ? undefined : true;
+ }), "toggleHidden");
+}
+
+function toggleLock(e: React.MouseEvent, doc: Doc) {
+ UndoManager.RunInBatch(() => runInAction(() => {
+ e.stopPropagation();
+ doc.lockedPosition = doc.lockedPosition ? undefined : true;
+ }), "toggleHidden");
+}
+
+/**
+ * add lock and hide button decorations for the "Dashboards" flyout TreeView
+ */
+export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) {
+ switch (property.split(":")[0]) {
+ case StyleProp.Decorations:
+ if (doc) {
+ const hidden = doc.hidden;
+ const locked = doc.lockedPosition;
+ return doc._viewType === CollectionViewType.Docking || (Doc.IsSystem(doc) && Doc.UserDoc().noviceMode) ? (null) :
+ <>
+ <div className={`styleProvider-treeView-hide${hidden ? "-active" : ""}`} onClick={(e) => toggleHidden(e, doc)}>
+ <FontAwesomeIcon icon={hidden ? "eye-slash" : "eye"} size="sm" />
+ </div>
+ <div className={`styleProvider-treeView-lock${locked ? "-active" : ""}`} onClick={(e) => toggleLock(e, doc)}>
+ <FontAwesomeIcon icon={locked ? "lock" : "unlock"} size="sm" />
+ </div>
+ </>;
+ }
+ default: return DefaultStyleProvider(doc, props, property);
+
+ }
+}
+
+function changeFilterBool(e: any, doc: Doc) {
+ UndoManager.RunInBatch(() => runInAction(() => {
+ //e.stopPropagation();
+ //doc.lockedPosition = doc.lockedPosition ? undefined : true;
+ }), "changeFilterBool");
+}
+
+function closeFilter(e: React.MouseEvent, doc: Doc) {
+ UndoManager.RunInBatch(() => runInAction(() => {
+ e.stopPropagation();
+ //doc.lockedPosition = doc.lockedPosition ? undefined : true;
+ }), "closeFilter");
+}
+
+
//
// a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
// currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index b2ae441d6..2251b60e5 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -13,6 +13,7 @@ import { DragManager } from '../../util/DragManager';
import { DocumentView } from '../nodes/DocumentView';
import "./CollectionCarousel3DView.scss";
import { CollectionSubView } from './CollectionSubView';
+import { StyleProp } from '../StyleProvider';
type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
const Carousel3DDocument = makeInterface(documentSchema, collectionSchema);
@@ -40,11 +41,8 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._itemIndex);
const displayDoc = (childPair: { layout: Doc, data: Doc }) => {
- const script = ScriptField.MakeScript("this._showCaption = 'caption'", { this: Doc.name });
- const onChildClick = script && (() => script);
- return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit}
onDoubleClick={this.onChildDoubleClick}
- onClick={onChildClick}
renderDepth={this.props.renderDepth + 1}
LayoutTemplate={this.props.childLayoutTemplate}
LayoutTemplateString={this.props.childLayoutString}
@@ -52,7 +50,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
DataDoc={childPair.data}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
bringToFront={returnFalse}
/>;
};
@@ -104,27 +101,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
}, 1500);
}
- _downX = 0;
- _downY = 0;
- onPointerDown = (e: React.PointerEvent) => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- document.addEventListener("pointerup", this.onpointerup);
- }
- private _lastTap: number = 0;
- private _doubleTap = false;
- onpointerup = (e: PointerEvent) => {
- this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
- this._lastTap = Date.now();
- }
-
- onClick = (e: React.MouseEvent) => {
- if (this._doubleTap) {
- e.stopPropagation();
- this.props.Document.isLightboxOpen = true;
- }
- }
-
@computed get buttons() {
if (!this.props.isContentActive()) return null;
return <div className="arrow-buttons" >
@@ -167,7 +143,11 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
const index = NumCast(this.layoutDoc._itemIndex);
const translateX = this.panelWidth() * (1 - index);
- return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}>
+ return <div className="collectionCarousel3DView-outer" ref={this.createDashEventsTarget}
+ style={{
+ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+ color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ }} >
<div className="carousel-wrapper" style={{ transform: `translateX(${translateX}px)` }}>
{this.content}
</div>
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index a9a1898f5..8660113cd 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,13 +1,10 @@
.collectionCarouselView-outer {
- background: gray;
height : 100%;
.collectionCarouselView-caption {
- margin-left: 10%;
- margin-right: 10%;
height: 50;
display: inline-block;
- width: 80%;
+ width: 100%;
}
.collectionCarouselView-image {
height: calc(100% - 50px);
@@ -19,13 +16,17 @@
.carouselView-back, .carouselView-fwd {
position: absolute;
display: flex;
- top: 50%;
+ top: 42.5%;
width: 30;
- height: 30;
+ height: 15%;
align-items: center;
border-radius: 5px;
justify-content: center;
- background : rgba(255, 255, 255, 0.46);
+ color: rgba(255,255,255,0.5);
+ background : rgba(0,0,0, 0.1);
+ &:hover {
+ color:white;
+ }
}
.carouselView-fwd {
right: 0;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index cc90b9134..cd81a99c9 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -2,17 +2,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
import { collectionSchema, documentSchema } from '../../../fields/documentSchemas';
import { makeInterface } from '../../../fields/Schema';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { OmitKeys, returnFalse } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import "./CollectionCarouselView.scss";
-import { CollectionSubView } from './CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
const CarouselDocument = makeInterface(documentSchema, collectionSchema);
@@ -38,77 +38,70 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
e.stopPropagation();
this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
}
- panelHeight = () => this.props.PanelHeight() - 50;
+ captionStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+ const captionProps = { ...this.props, fieldKey: "caption" };
+ return this.props.styleProvider?.(doc, props, property) || this.props.styleProvider?.(this.layoutDoc, captionProps, property);
+ }
+ panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._showCaption) ? 50 : 0);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
@computed get content() {
const index = NumCast(this.layoutDoc._itemIndex);
const curDoc = this.childLayoutPairs?.[index];
+ const captionProps = { ...this.props, fieldKey: "caption" };
+ const marginX = NumCast(this.layoutDoc["caption-xMargin"]);
+ const marginY = NumCast(this.layoutDoc["caption-yMargin"]);
return !(curDoc?.layout instanceof Doc) ? (null) :
<>
<div className="collectionCarouselView-image" key="image">
- <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit}
onDoubleClick={this.onContentDoubleClick}
onClick={this.onContentClick}
renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionView={this}
LayoutTemplate={this.props.childLayoutTemplate}
LayoutTemplateString={this.props.childLayoutString}
Document={curDoc.layout}
- DataDoc={curDoc.data}
+ DataDoc={curDoc.layout.resolvedDataDoc as Doc}
PanelHeight={this.panelHeight}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
bringToFront={returnFalse}
/>
</div>
<div className="collectionCarouselView-caption" key="caption"
style={{
- background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BackgroundColor)),
- color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)),
- borderRadius: StrCast(this.layoutDoc._captionBorderRounding),
+ display: StrCast(this.layoutDoc._showCaption) ? undefined : "none",
+ borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
+ marginRight: marginX, marginLeft: marginX,
+ width: `calc(100% - ${marginX * 2}px)`
}}>
- <FormattedTextBox key={index} {...this.props}
- xMargin={NumCast(this.layoutDoc["_carousel-caption-xMargin"])}
- yMargin={NumCast(this.layoutDoc["_carousel-caption-yMargin"])}
- Document={curDoc.layout} DataDoc={undefined} fieldKey={"caption"} />
+ <FormattedTextBox key={index} {...captionProps}
+ styleProvider={this.captionStyleProvider}
+ Document={curDoc.layout} DataDoc={undefined}
+ fontSize={NumCast(this.layoutDoc["caption-fontSize"])}
+ xPadding={NumCast(this.layoutDoc["caption-xPadding"])}
+ yPadding={NumCast(this.layoutDoc["caption-yPadding"])} />
</div>
</>;
}
@computed get buttons() {
return <>
- <div key="back" className="carouselView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.goback}>
- <FontAwesomeIcon icon={"caret-left"} size={"2x"} />
+ <div key="back" className="carouselView-back" onClick={this.goback}>
+ <FontAwesomeIcon icon={"chevron-left"} size={"2x"} />
</div>
- <div key="fwd" className="carouselView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.advance}>
- <FontAwesomeIcon icon={"caret-right"} size={"2x"} />
+ <div key="fwd" className="carouselView-fwd" onClick={this.advance}>
+ <FontAwesomeIcon icon={"chevron-right"} size={"2x"} />
</div>
</>;
}
- _downX = 0;
- _downY = 0;
- onPointerDown = (e: React.PointerEvent) => {
- this._downX = e.clientX;
- this._downY = e.clientY;
- document.addEventListener("pointerup", this.onpointerup);
- }
- private _lastTap: number = 0;
- private _doubleTap = false;
- onpointerup = (e: PointerEvent) => {
- this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
- this._lastTap = Date.now();
- }
-
- onClick = (e: React.MouseEvent) => {
- if (this._doubleTap) {
- e.stopPropagation();
- this.props.Document.isLightboxOpen = true;
- }
- }
-
render() {
- return <div className="collectionCarouselView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}>
+ return <div className="collectionCarouselView-outer" ref={this.createDashEventsTarget}
+ style={{
+ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+ color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ }}>
{this.content}
- {!this.props.Document._chromeHidden ? (null) : this.buttons}
+ {this.props.Document._chromeHidden ? (null) : this.buttons}
</div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 2c81d727e..dc5231a3a 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -14,35 +14,53 @@
top: 0;
width: 100%;
- .antimodeMenu-button {
- padding: 0;
- width: 30px;
+ .recordButtonOutline {
+ border-radius: 100%;
+ width: 18px;
+ height: 18px;
+ border: solid 1px #f5f5f5;
display: flex;
+ align-items: center;
+ justify-content: center;
+ }
- svg {
- margin: auto;
- }
+ .recordButtonInner {
+ border-radius: 100%;
+ width: 70%;
+ height: 70%;
+ background: white;
}
.collectionMenu {
display: flex;
- padding-bottom: 1px;
- height: 32px;
- border-bottom: .5px solid rgb(180, 180, 180);
+ height: 100%;
overflow: visible;
z-index: 901;
border: unset;
+ .collectionMenu-divider {
+ height: 85%;
+ margin-left: 3px;
+ margin-right: 3px;
+ width: 1.5px;
+ background-color: #656060;
+ }
+
.collectionViewBaseChrome {
display: flex;
+ align-items: center;
.collectionViewBaseChrome-viewPicker {
font-size: 75%;
- background: #323232;
outline-color: black;
color: white;
border: none;
- border-right: solid gray 1px;
+ background: #323232;
+ }
+
+ .collectionViewBaseChrome-viewPicker:focus {
+ outline: none;
+ border: none;
}
.collectionViewBaseChrome-viewPicker:active {
@@ -65,18 +83,26 @@
margin-left: 3px;
margin-right: 0px;
font-size: 75%;
- background: #323232;
+ text-transform: capitalize;
color: white;
border: none;
- border-right: solid gray 1px;
+ background: #323232;
+ }
+
+ .collectionViewBaseChrome-cmdPicker:focus {
+ border: none;
+ outline: none;
}
.commandEntry-outerDiv {
pointer-events: all;
- background-color: #323232;
+ background-color: transparent;
display: flex;
flex-direction: row;
- height: 30px;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ overflow: hidden;
.commandEntry-drop {
color: white;
@@ -86,6 +112,15 @@
}
}
+ .commandEntry-outerDiv:hover{
+ background-color: rgba(0,0,0,0.2);
+
+ .collectionViewBaseChrome-viewPicker,
+ .collectionViewBaseChrome-cmdPicker{
+ background: rgb(41,41,41);
+ }
+ }
+
.collectionViewBaseChrome-collapse {
transition: all .5s, opacity 0.3s;
position: absolute;
@@ -103,11 +138,12 @@
.collectionViewBaseChrome-template,
.collectionViewBaseChrome-viewModes {
- display: grid;
- background: rgb(238, 238, 238);
+ align-items: center;
+ height: 100%;
+ display: flex;
+ background: transparent;
color: grey;
- margin-top: auto;
- margin-bottom: auto;
+ justify-content: center;
}
.collectionViewBaseChrome-viewSpecs {
@@ -263,27 +299,52 @@
.collectionTreeViewChrome-pivotField-cont,
.collection3DCarouselViewChrome-scrollSpeed-cont {
justify-self: right;
+ align-items: center;
display: flex;
+ grid-auto-columns: auto;
font-size: 75%;
letter-spacing: 2px;
.collectionStackingViewChrome-pivotField-label,
.collectionTreeViewChrome-pivotField-label,
.collection3DCarouselViewChrome-scrollSpeed-label {
- vertical-align: center;
- padding-left: 10px;
- margin: auto;
+ grid-column: 1;
+ margin-right: 7px;
+ user-select: none;
+ font-family: 'Roboto';
+ letter-spacing: normal;
+ }
+
+ .collectionStackingViewChrome-sortIcon {
+ transition: transform .5s;
+ grid-column: 3;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ width: 25px;
+ height: 25px;
+ border-radius: 100%;
+ }
+
+ .collectionStackingViewChrome-sortIcon:hover {
+ background-color: rgba(0,0,0,0.2);
}
.collectionStackingViewChrome-pivotField,
.collectionTreeViewChrome-pivotField,
.collection3DCarouselViewChrome-scrollSpeed {
color: white;
- width: 100%;
+ grid-column: 2;
+ grid-row: 1;
+ width: 90%;
min-width: 100px;
display: flex;
+ height: 80%;
+ border-radius: 7px;
align-items: center;
- background: rgb(238, 238, 238);
+ background: #eeeeee;
.editable-view-input,
input,
@@ -378,20 +439,13 @@
display: inline-flex;
position: relative;
align-items: center;
- margin-left: 10px;
-
- .antimodeMenu-button {
- text-align: center;
- display: block;
- position: relative;
- }
+ height: 100%;
.color-previewI {
- width: 80%;
- height: 20%;
- bottom: 0;
+ width: 60%;
+ top: 80%;
position: absolute;
- margin-left: 2px;
+ height: 4px;
}
.color-previewII {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 623e05b33..ab4f197a6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -34,6 +34,10 @@ import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
import { LightboxView } from "../LightboxView";
+import { Docs } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
@observer
export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -257,6 +261,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" {...this.props} />);
case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" {...this.props} />);
case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
+ case CollectionViewType.Carousel:
case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" {...this.props} />);
case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" {...this.props} />);
case CollectionViewType.Docking: return (<CollectionDockingChrome key="collchrome" {...this.props} />);
@@ -388,7 +393,29 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
<button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
onClick={e => TabDocView.PinDoc(targetDoc, { unpin: isPinned })}>
- <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" />
+ <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
+ </button>
+ </Tooltip>;
+ }
+
+ @undoBatch
+ @action
+ startRecording = () => {
+ const doc = Docs.Create.ScreenshotDocument("screen recording", { _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
+ //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+ CollectionDockingView.AddSplit(doc, "right");
+ }
+
+ @computed
+ get recordButton() {
+ const targetDoc = this.selectedDoc;
+ return <Tooltip key="record" title={<div className="dash-tooltip">{"Capture screen"}</div>} placement="top">
+ <button className="antimodeMenu-button"
+ onClick={e => this.startRecording()}>
+ <div className="recordButtonOutline" style={{}}>
+ <div className="recordButtonInner" style={{}}>
+ </div>
+ </div>
</button>
</Tooltip>;
}
@@ -478,7 +505,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
const targetDoc = this.selectedDoc;
return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
<button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" />
+ <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
</button>
</Tooltip>;
}
@@ -486,37 +513,47 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
@computed get lightboxButton() {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"View in Lightbox"}</div>} placement="top">
- <button className="antimodeMenu-button" style={{ borderRight: "1px solid gray", justifyContent: 'center' }} onPointerDown={() => {
+ <button className="antimodeMenu-button" onPointerDown={() => {
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
}}>
- <FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" />
+ <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
</button>
</Tooltip>;
}
+ @computed get toggleOverlayButton() {
+ return <>
+ <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
+ <button className={"antimodeMenu-button"} key="float"
+ style={{
+ backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined,
+ pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
+ color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
+ }}
+ onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
+ <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
+ </button>
+ </Tooltip>
+ </>
+ }
+
render() {
return (
<div className="collectionMenu-cont" >
<div className="collectionMenu">
<div className="collectionViewBaseChrome">
+ {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
+ <div className="collectionMenu-divider" key="divider1"></div>
{this.aliasButton}
{/* {this.pinButton} */}
+ {this.toggleOverlayButton}
{this.pinWithViewButton}
- {this.lightboxButton}
- <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
- <button className={"antimodeMenu-button"} key="float"
- style={{
- backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined, borderRight: "1px solid gray",
- pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
- color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
- }}
- onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
- <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
- </button>
- </Tooltip>
+ <div className="collectionMenu-divider" key="divider2"></div>
{this.subChrome}
- {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
+ <div className="collectionMenu-divider" key="divider3"></div>
+ {this.lightboxButton}
+ {this.recordButton}
{!this._buttonizableCommands ? (null) : this.templateChrome}
</div>
</div>
@@ -986,6 +1023,7 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionMe
return (
<div className="collection3DCarouselViewChrome-cont">
<div className="collection3DCarouselViewChrome-scrollSpeed-cont">
+ {FormattedTextBox.Focused ? <RichTextMenu /> : (null)}
<div className="collectionStackingViewChrome-scrollSpeed-label">
AUTOSCROLL SPEED:
</div>
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 6f6cdd5d2..6baf633dd 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -2,7 +2,7 @@ import { action, computed } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, setupMoveUpEvents, returnTrue } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { SelectionManager } from "../../util/SelectionManager";
import { SnappingManager } from "../../util/SnappingManager";
@@ -31,9 +31,16 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
// returns the contents of the pileup in a CollectionFreeFormView
@computed get contents() {
+ const isStarburst = this.layoutEngine() === "starburst";
const draggingSelf = this.props.isSelected();
- return <div className="collectionPileView-innards" style={{ pointerEvents: this.layoutEngine() === "starburst" || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none", zIndex: this.layoutEngine() === "starburst" && !SnappingManager.GetIsDragging() ? -10 : "auto" }} >
- <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine}
+ return <div className="collectionPileView-innards"
+ style={{
+ pointerEvents: isStarburst || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none",
+ zIndex: isStarburst && !SnappingManager.GetIsDragging() ? -10 : "auto"
+ }} >
+ <CollectionFreeFormView {...this.props}
+ layoutEngine={this.layoutEngine}
+ childDocumentsActive={isStarburst ? returnTrue : undefined}
addDocument={undoBatch((doc: Doc | Doc[]) => {
(doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d));
return this.props.addDocument?.(doc) || false;
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index 19ad5cefa..e456c0664 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -62,6 +62,7 @@
.collectionStackedTimeline-waveform {
position: absolute;
width: 100%;
+ height: 100%;
top: 0;
left: 0;
pointer-events: none;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 6a1242f20..ab9c4aa2c 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -234,8 +234,9 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
return level;
}
- dictationHeight = () => this.props.PanelHeight() / 3;
- timelineContentHeight = () => this.props.PanelHeight() * 2 / 3;
+ dictationHeightPercent = 50;
+ dictationHeight = () => `${this.dictationHeightPercent}%`;
+ timelineContentHeight = () => this.props.PanelHeight() * this.dictationHeightPercent / 100;
dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight());
@computed get renderDictation() {
const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
@@ -348,7 +349,11 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
(time) => {
const dictationDoc = Cast(this.props.layoutDoc["data-dictation"], Doc, null);
const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
- if ((isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc)) && DocListCast(this.props.mark.links).length &&
+ if (!LightboxView.LightboxDoc
+ // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront.
+ // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video.
+ /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/
+ && DocListCast(this.props.mark.links).length &&
time > NumCast(this.props.mark[this.props.startTag]) &&
time < NumCast(this.props.mark[this.props.endTag]) &&
this._lastTimecode < NumCast(this.props.mark[this.props.startTag])) {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8d8c69fd5..30f8e0112 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -55,10 +55,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
@computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
@computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
@computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
- @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
+ @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
@computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); }
@computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
+ @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
@computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index bee7aeb87..47733994b 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -235,7 +235,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
});
const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y);
- ContextMenu.Instance.displayMenu(x, y);
+ ContextMenu.Instance.displayMenu(x, y, undefined, true);
}
@computed get innards() {
TraceMobx();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a5d679df0..a5d62acb4 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -74,7 +74,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const { Document, DataDoc } = this.props;
const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)).
filter(pair => { // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys
- return pair.layout && !pair.layout.hidden && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length));
+ return pair.layout && /*!pair.layout.hidden &&*/ (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length));
});
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
}
@@ -90,7 +90,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
searchFilterDocs = () => {
return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)];
}
- @computed get childDocs() {
+ @computed.struct get childDocs() {
+ TraceMobx();
let rawdocs: (Doc | Promise<Doc>)[] = [];
if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
rawdocs = [this.dataField];
@@ -114,10 +115,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
}
+ // console.log(CurrentUserUtils.ActiveDashboard._docFilters);
+ // if (!this.props.Document._docFilters && this.props.Document.currentFilter) {
+ // (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean;
+ // }
const docsforFilter: Doc[] = [];
childDocs.forEach((d) => {
- if (DocUtils.Excluded(d, docFilters)) return;
- let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0));
+ // if (DocUtils.Excluded(d, docFilters)) return;
+ let notFiltered = d.z || Doc.IsSystem(d) || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
const fieldKey = Doc.LayoutFieldKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
const data = d[annos ? fieldKey + "-annotations" : fieldKey];
@@ -125,13 +130,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
let subDocs = DocListCast(data);
if (subDocs.length > 0) {
let newarray: Doc[] = [];
- notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript).length);
+ notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript, d).length);
while (subDocs.length > 0 && !notFiltered) {
newarray = [];
subDocs.forEach((t) => {
const fieldKey = Doc.LayoutFieldKey(t);
const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
- notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript).length));
+ notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript, d).length));
DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
});
subDocs = newarray;
@@ -474,5 +479,6 @@ import { CollectionView, CollectionViewType, CollectionViewProps } from "./Colle
import { SelectionManager } from "../../util/SelectionManager";
import { OverlayView } from "../OverlayView";
import { Hypothesis } from "../../util/HypothesisUtils";
-import { GetEffectiveAcl } from "../../../fields/util";
+import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
+import { FilterBox } from "../nodes/FilterBox";
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5a4864d2d..82c8a9114 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -49,7 +49,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
@computed get doc() { return this.props.Document; }
@computed get dataDoc() { return this.props.DataDoc || this.doc; }
@computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
- @computed get treeChildren() { return this.props.childDocuments || this.childDocs; }
+ @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
@computed get outlineMode() { return this.doc.treeViewType === "outline"; }
@computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; }
@@ -67,19 +67,23 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- componentWillMount() {
+ componentDidMount() {
this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight,
auto => auto && this.computeHeight(),
- { fireImmediately: true })
+ { fireImmediately: true });
}
refList: Set<any> = new Set();
observer: any;
computeHeight = () => {
- this.rootDoc._height = this.paddingTop() + 26/* bcz: ugh: title bar height hack ... get ref and compute instead */ +
+ const hgt = this.paddingTop() + 26/* bcz: ugh: title bar height hack ... get ref and compute instead */ +
Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
+ this.props.setHeight(hgt);
+ }
+ unobserveHeight = (ref: any) => {
+ this.refList.delete(ref);
+ this.rootDoc.autoHeight && this.computeHeight();
}
- unobserveHeight = (ref: any) => this.refList.delete(ref);
observerHeight = (ref: any) => {
if (ref) {
this.refList.add(ref);
@@ -111,15 +115,14 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const targetDataDoc = this.doc[DataSym];
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const result = value.filter(v => !docs.includes(v));
- SelectionManager.DeselectAll();
+ if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views().some(dv => Doc.AreProtosEqual(dv.rootDoc, doc)))) SelectionManager.DeselectAll();
if (result.length !== value.length) {
const ind = targetDataDoc[this.props.fieldKey].indexOf(doc);
const prev = ind && targetDataDoc[this.props.fieldKey][ind - 1];
- targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
+ this.props.removeDocument?.(doc);
if (ind > 0) {
FormattedTextBox.SelectOnLoad = prev[Id];
- const prevView = DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView);
- prevView?.select(false);
+ DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView)?.select(false);
}
return true;
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 21dd4b206..85ae66fdc 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable } from 'mobx';
+import { computed, observable } from 'mobx';
import { observer } from "mobx-react";
import * as React from 'react';
import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
@@ -66,6 +66,7 @@ export interface CollectionViewProps extends FieldViewProps {
// property overrides for child documents
children?: never | (() => JSX.Element[]) | React.ReactNode;
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;
childOpacity?: () => number;
childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6aecbb14..7e2f7811e 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -9,7 +9,7 @@ import * as ReactDOM from 'react-dom';
import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Id } from '../../../fields/FieldSymbols';
import { FieldId } from "../../../fields/RefField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -92,7 +92,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
};
tab.element[0].style.borderTopRightRadius = "8px";
tab.element[0].children[1].appendChild(toggle);
- tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+ tab._disposers.layerDisposer = reaction(() =>
+ ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
}
// shifts the focus to this tab when another tab is dragged over it
@@ -298,6 +299,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
PanelHeight = () => this._panelHeight;
miniMapColor = () => this.tabColor;
tabView = () => this._view;
+ disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
+ hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
@computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
@computed get docView() {
@@ -329,13 +332,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
bringToFront={emptyFunction}
pinToPres={TabDocView.PinDoc} />
<TabMinimapView key="minimap"
+ hideMinimap={this.hideMinimap}
addDocTab={this.addDocTab}
PanelHeight={this.PanelHeight}
PanelWidth={this.PanelWidth}
background={this.miniMapColor}
document={this._document}
tabView={this.tabView} />
- <Tooltip style={{ display: this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
+ <Tooltip style={{ display: this.disableMinimap() ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
<div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
<FontAwesomeIcon icon={"globe-asia"} size="lg" />
</div>
@@ -346,7 +350,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
render() {
this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
return (
- <div className="collectionDockingView-content" style={{ height: "100%", width: "100%" }} ref={ref => {
+ <div className="collectionDockingView-content" style={{
+ fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined,
+ height: "100%", width: "100%"
+ }} ref={ref => {
if (this._mainCont = ref) {
(this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
@@ -360,6 +367,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
interface TabMinimapViewProps {
document: Doc;
+ hideMinimap: () => boolean;
tabView: () => DocumentView | undefined;
addDocTab: (doc: Doc, where: string) => boolean;
PanelWidth: () => number;
@@ -410,7 +418,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2;
const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2;
const miniSize = this.returnMiniSize();
- return this.props.document.hideMinimap ? (null) :
+ return this.props.hideMinimap() ? (null) :
<div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
<CollectionFreeFormView
Document={this.props.document}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index bf896b8d3..5b0c04f33 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -8,6 +8,7 @@
width: 100%;
overflow: hidden;
}
+
.treeView-container,
.treeView-container-active {
.bullet-outline {
@@ -21,21 +22,26 @@
.treeView-bulletIcons {
width: $TREE_BULLET_WIDTH;
+
.treeView-expandIcon {
display: none;
left: -10px;
position: absolute;
}
+
.treeView-checkIcon {
- left: -10px;
+ left: 3.5px;
+ top: 2px;
position: absolute;
}
+
&:hover {
.treeView-expandIcon {
display: unset;
}
}
}
+
.bullet {
position: relative;
width: $TREE_BULLET_WIDTH;
@@ -46,9 +52,11 @@
border-radius: 4px;
}
}
+
.treeView-container-active {
z-index: 100;
position: relative;
+
.formattedTextbox-sidebar {
background-color: #ffff001f !important;
height: 500px !important;
@@ -71,7 +79,8 @@
display: flex;
overflow: hidden;
}
-.treeView-border{
+
+.treeView-border {
border-left: dashed 1px #00000042;
}
@@ -79,15 +88,20 @@
.treeView-header {
border: transparent 1px solid;
display: flex;
+
//align-items: center;
::-webkit-scrollbar {
- display: none;
+ display: none;
}
+
.formattedTextBox-cont {
- .formattedTextbox-sidebar, .formattedTextbox-sidebar-inking {
+
+ .formattedTextbox-sidebar,
+ .formattedTextbox-sidebar-inking {
overflow: visible !important;
border-left: unset;
}
+
overflow: visible !important;
}
@@ -105,12 +119,18 @@
margin-left: 0.25rem;
opacity: 0.75;
- >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide, .styleProvider-treeView-lock-active, .styleProvider-treeView-hide-active {
+ >svg,
+ .styleProvider-treeView-lock,
+ .styleProvider-treeView-hide,
+ .styleProvider-treeView-lock-active,
+ .styleProvider-treeView-hide-active {
margin-left: 0.25rem;
- margin-right: 0.25rem;
+ margin-right: 0.25rem;
}
-
- >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide {
+
+ >svg,
+ .styleProvider-treeView-lock,
+ .styleProvider-treeView-hide {
display: none;
}
}
@@ -135,7 +155,10 @@
}
.right-buttons-container {
- >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide {
+
+ >svg,
+ .styleProvider-treeView-lock,
+ .styleProvider-treeView-hide {
display: inherit;
}
}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 2c3a6c0d7..e1fd78270 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -21,7 +21,7 @@ import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { EditableView } from "../EditableView";
import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss';
-import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps, StyleProviderFunc, DocumentViewInternal } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { KeyValueBox } from '../nodes/KeyValueBox';
@@ -150,9 +150,9 @@ export class TreeView extends React.Component<TreeViewProps> {
if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
this.treeViewOpen = !this.treeViewOpen;
} else {
+ // choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias
this.props.addDocTab(this.props.document, "add:right");
}
- docView?.select(false);
}
constructor(props: any) {
super(props);
@@ -174,7 +174,7 @@ export class TreeView extends React.Component<TreeViewProps> {
componentWillUnmount() {
this._selDisposer?.();
- this._treeEle && this.props.unobserveHeight(this._treeEle)
+ this._treeEle && this.props.unobserveHeight(this._treeEle);
document.removeEventListener("pointermove", this.onDragMove, true);
document.removeEventListener("pointermove", this.onDragUp, true);
}
@@ -442,7 +442,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderBullet() {
TraceMobx();
- const iconType = this.doc.isFolder ? (this.treeViewOpen ? "chevron-down" : "chevron-right") : Doc.toIcon(this.doc);
+ const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ":open" : "")) || "question";
const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"}
title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
@@ -483,22 +483,29 @@ export class TreeView extends React.Component<TreeViewProps> {
}
@computed get headerElements() {
- return this.props.treeViewHideHeaderFields() || Doc.IsSystem(this.doc) ? (null)
+ return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? (null)
: <>
- <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />
- {this.doc.treeViewExpandedViewLock ? (null) :
+ {this.doc.hideContextMenu ? (null) : <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />}
+ {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? (null) :
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
{this.treeViewExpandedView}
</span>}
</>;
}
- showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
- contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : this.doc.isFolder ?
- [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] :
- this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
- [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] :
- [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]
+ showContextMenu = (e: React.MouseEvent) => {
+ DocumentViewInternal.SelectAfterContextMenu = false;
+ simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ DocumentViewInternal.SelectAfterContextMenu = true;
+ }
+ contextMenuItems = () => {
+ const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" };
+ return this.doc.isFolder ? [makeFolder] :
+ Doc.IsSystem(this.doc) ? [] :
+ this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
+ [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, makeFolder] :
+ [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }];
+ }
onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
@@ -522,7 +529,6 @@ export class TreeView extends React.Component<TreeViewProps> {
}}>
{StrCast(doc?.title)}
</div>;
- case StyleProp.Decorations: return (null);
default: return this.props?.treeView?.props.styleProvider?.(doc, props, property);
}
}
@@ -542,7 +548,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
}
}
- titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - treeBulletWidth()));
+ titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
/**
* Renders the EditableView title element for placement into the tree.
@@ -726,7 +732,7 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
<div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
ref={this.createTreeDropTarget}
- onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()}
+ //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
onKeyDown={this.onKeyDown}>
<li className="collection-child">
{hideTitle && this.doc.type !== DocumentType.RTF ?
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c623ce653..407216217 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1032,7 +1032,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
docRangeFilters={this.freeformRangeDocFilters}
searchFilterDocs={this.searchFilterDocs}
isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse}
- isDocumentActive={this.isContentActive}
+ isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
focus={this.focusDocument}
addDocTab={this.addDocTab}
addDocument={this.props.addDocument}
@@ -1051,7 +1051,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
dontRegisterView={this.props.dontRegisterView}
pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
(this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
- jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
+ jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
//fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
/>;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d14b68fa7..b1f2750c3 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -101,7 +101,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
else if (e.key === ":") {
DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
- cm.displayMenu(this._downX, this._downY);
+ cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
} else if (e.key === "a" && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
@@ -155,7 +155,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : "";
FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
- this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0));
+ this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0));
e.stopPropagation();
}
}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 8b5c02b75..f3a39a262 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -49,6 +49,11 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
}
+ @computed
+ private get minimumDim() {
+ return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+ }
+
/**
* This loops through all childLayoutPairs and extracts the values for _dimUnit
* and _dimMagnitude, ignoring any that are malformed. Additionally, it then
@@ -63,7 +68,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
const widthSpecifiers: WidthSpecifier[] = [];
this.childLayoutPairs.map(pair => {
const unit = StrCast(pair.layout._dimUnit, "*");
- const magnitude = NumCast(pair.layout._dimMagnitude, 1);
+ const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
(unit === DimUnit.Ratio) && (starSum += magnitude);
widthSpecifiers.push({ magnitude, unit });
@@ -80,15 +85,15 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
* themselves to drift toward zero. Thus, whenever we change any of the values,
* we normalize everything (dividing by the smallest magnitude).
*/
- setTimeout(() => {
- const { ratioDefinedDocs } = this;
- if (this.childLayoutPairs.length) {
- const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc._dimMagnitude, 1)));
- if (minimum !== 0) {
- ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1);
- }
- }
- });
+ // setTimeout(() => {
+ // const { ratioDefinedDocs } = this;
+ // if (this.childLayoutPairs.length) {
+ // const minimum = this.minimumDim;
+ // if (minimum !== 0) {
+ // ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1);
+ // }
+ // }
+ // });
return { widthSpecifiers, starSum };
}
@@ -161,7 +166,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
if (columnUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
- let width = NumCast(layout._dimMagnitude, 1);
+ let width = NumCast(layout._dimMagnitude, this.minimumDim);
if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
width *= columnUnitLength;
}
@@ -273,6 +278,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
<ResizeBar
width={resizerWidth}
key={"resizer" + i}
+ styleProvider={this.props.styleProvider}
+ isContentActive={this.props.isContentActive}
select={this.props.select}
columnUnitLength={this.getColumnUnitLength}
toLeft={layout}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 2c5e40d02..29cb3511a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -49,6 +49,11 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio);
}
+ @computed
+ private get minimumDim() {
+ return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+ }
+
/**
* This loops through all childLayoutPairs and extracts the values for _dimUnit
* and _dimUnit, ignoring any that are malformed. Additionally, it then
@@ -63,7 +68,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
const heightSpecifiers: HeightSpecifier[] = [];
this.childLayoutPairs.map(pair => {
const unit = StrCast(pair.layout._dimUnit, "*");
- const magnitude = NumCast(pair.layout._dimMagnitude, 1);
+ const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
(unit === DimUnit.Ratio) && (starSum += magnitude);
heightSpecifiers.push({ magnitude, unit });
@@ -80,15 +85,15 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
* themselves to drift toward zero. Thus, whenever we change any of the values,
* we normalize everything (dividing by the smallest magnitude).
*/
- setTimeout(() => {
- const { ratioDefinedDocs } = this;
- if (this.childLayoutPairs.length) {
- const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1)));
- if (minimum !== 0) {
- ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum);
- }
- }
- });
+ // setTimeout(() => {
+ // const { ratioDefinedDocs } = this;
+ // if (this.childLayoutPairs.length) {
+ // const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1)));
+ // if (minimum !== 0) {
+ // ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum);
+ // }
+ // }
+ // });
return { heightSpecifiers, starSum };
}
@@ -161,7 +166,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
if (rowUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
- let height = NumCast(layout._dimMagnitude, 1);
+ let height = NumCast(layout._dimMagnitude, this.minimumDim);
if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) {
height *= rowUnitLength;
}
@@ -261,12 +266,16 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
const height = () => this.lookupPixels(layout);
const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
collector.push(
- <div className={"document-wrapper"} key={"wrapper" + i} >
+ <div className={"document-wrapper"}
+ style={{ height: height() }}
+ key={"wrapper" + i} >
{this.getDisplayDoc(layout, dxf, width, height)}
<HeightLabel layout={layout} collectionDoc={Document} />
</div>,
<ResizeBar
height={resizerHeight}
+ styleProvider={this.props.styleProvider}
+ isContentActive={this.props.isContentActive}
key={"resizer" + i}
columnUnitLength={this.getRowUnitLength}
toTop={layout}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 734915a93..9fe18d118 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -5,9 +5,13 @@ import { Doc } from "../../../../fields/Doc";
import { NumCast, StrCast } from "../../../../fields/Types";
import { DimUnit } from "./CollectionMulticolumnView";
import { UndoManager } from "../../../util/UndoManager";
+import { StyleProviderFunc } from "../../nodes/DocumentView";
+import { StyleProp } from "../../StyleProvider";
interface ResizerProps {
width: number;
+ styleProvider?: StyleProviderFunc;
+ isContentActive?: () => boolean;
columnUnitLength(): number | undefined;
toLeft?: Doc;
toRight?: Doc;
@@ -85,19 +89,16 @@ export default class ResizeBar extends React.Component<ResizerProps> {
}
render() {
- return (
- <div
- className={"multiColumnResizer"}
- style={{
- width: this.props.width,
- opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0
- }}
- onPointerEnter={action(() => this.isHoverActive = true)}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
- >
- <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
- </div>
- );
+ return <div className="multiColumnResizer"
+ style={{
+ width: this.props.width,
+ backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor)
+ }}
+ onPointerEnter={action(() => this.isHoverActive = true)}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
+ >
+ <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index d0bc4d01c..5478bf709 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -5,9 +5,13 @@ import { Doc } from "../../../../fields/Doc";
import { NumCast, StrCast } from "../../../../fields/Types";
import { DimUnit } from "./CollectionMultirowView";
import { UndoManager } from "../../../util/UndoManager";
+import { StyleProp } from "../../StyleProvider";
+import { StyleProviderFunc } from "../../nodes/DocumentView";
interface ResizerProps {
height: number;
+ styleProvider?: StyleProviderFunc;
+ isContentActive?: () => boolean;
columnUnitLength(): number | undefined;
toTop?: Doc;
toBottom?: Doc;
@@ -83,19 +87,16 @@ export default class ResizeBar extends React.Component<ResizerProps> {
}
render() {
- return (
- <div
- className={"multiRowResizer"}
- style={{
- height: this.props.height,
- opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0
- }}
- onPointerEnter={action(() => this.isHoverActive = true)}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
- >
- <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
- </div>
- );
+ return <div className="multiRowResizer"
+ style={{
+ height: this.props.height,
+ backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor)
+ }}
+ onPointerEnter={action(() => this.isHoverActive = true)}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
+ >
+ <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 85899578c..a2e36f12e 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -53,7 +53,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _position: number = 0;
@observable _waveHeight: Opt<number> = this.layoutDoc._height;
@observable _paused: boolean = false;
- @computed get mediaState(): undefined | "recording" | "paused" | "playing" { return this.dataDoc.mediaState as (undefined | "recording" | "paused" | "playing"); }
+ @computed get mediaState(): undefined | "pendingRecording" | "recording" | "paused" | "playing" { return this.dataDoc.mediaState as (undefined | "pendingRecording" | "recording" | "paused" | "playing"); }
set mediaState(value) { this.dataDoc.mediaState = value; }
public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; AudioBox._scrubTime = timeInMillisFrom1970; });
@computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1fda4cc5e..092823603 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -37,10 +37,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames
@observable _animPos: number[] | undefined = undefined;
@observable _contentView: DocumentView | undefined | null;
- random(min: number, max: number) { /* min should not be equal to max */ return min + ((Math.abs(this.X * this.Y) * 9301 + 49297) % 233280 / 233280) * (max - min); }
get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
- get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
+ get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; }
get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
@@ -156,7 +155,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
returnThis = () => this;
render() {
TraceMobx();
- const backgroundColor = () => this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor);
const divProps: DocumentViewProps = {
...this.props,
CollectionFreeFormDocumentView: this.returnThis,
@@ -175,17 +173,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
zIndex: this.ZInd,
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
- display: this.ZInd === -99 ? "none" : undefined,
+ display: this.ZInd === -99 ? "none" : undefined
}} >
-
- {Doc.UserDoc().renderStyle !== "comic" ? (null) :
- <div style={{ width: "100%", height: "100%", position: "absolute" }}>
- <svg style={{ transform: `scale(1,${this.props.PanelHeight() / this.props.PanelWidth()})`, transformOrigin: "top left", overflow: "visible" }} viewBox="0 0 12 14">
- <path d="M 7 0 C 9 -1 13 1 12 4 C 11 10 13 12 10 12 C 6 12 7 13 2 12 Q -1 11 0 8 C 1 4 0 4 0 2 C 0 0 1 0 1 0 C 3 0 3 1 7 0"
- style={{ stroke: "black", fill: backgroundColor(), strokeWidth: 0.2 }} />
- </svg>
- </div>}
-
<DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
</div>;
}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 749ffa7fd..bdbece621 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -3,6 +3,14 @@
.documentView-effectsWrapper {
border-radius: inherit;
}
+
+.documentView-customBorder {
+ width:100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+}
+
.documentView-node,
.documentView-node-topmost {
position: inherit;
@@ -37,12 +45,13 @@
overflow-y: scroll;
height: calc(100% - 20px);
}
+
.documentView-linkAnchorBoxAnchor {
- display:flex;
+ display: flex;
overflow: hidden;
.documentView-node {
- width:10px !important;
+ width: 10px !important;
}
}
@@ -64,6 +73,7 @@
top: 15%;
}
}
+
.documentView-treeView {
max-height: 1.5em;
text-overflow: ellipsis;
@@ -71,7 +81,8 @@
white-space: pre;
width: 100%;
overflow: hidden;
- > .documentView-node {
+
+ >.documentView-node {
position: absolute;
}
}
@@ -80,14 +91,33 @@
border-radius: inherit;
width: 100%;
height: 100%;
+
+ .sharingIndicator {
+ height: 30px;
+ width: 30px;
+ border-radius: 50%;
+ position: absolute;
+ right: -15;
+ opacity: 0.9;
+ pointer-events: auto;
+ background-color: #9dca96;
+ letter-spacing: 2px;
+ font-size: 10px;
+ transition: transform 0.2s;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ }
}
.documentView-anchorCont {
position: absolute;
- top: 0;
- left: 0;
+ top: 0;
+ left: 0;
width: 100%;
- height: 100%;
+ height: 100%;
display: inline-block;
pointer-events: none;
}
@@ -100,6 +130,7 @@
top: 0;
left: 0;
}
+
.documentView-styleWrapper {
position: absolute;
display: inline-block;
@@ -113,7 +144,8 @@
position: absolute;
}
- .documentView-titleWrapper, .documentView-titleWrapper-hover {
+ .documentView-titleWrapper,
+ .documentView-titleWrapper-hover {
overflow: hidden;
color: white;
transform-origin: top left;
@@ -126,8 +158,9 @@
white-space: pre;
position: absolute;
}
+
.documentView-titleWrapper-hover {
- display:none;
+ display: none;
}
.documentView-searchHighlight {
@@ -150,18 +183,21 @@
}
-.documentView-node:hover, .documentView-node-topmost:hover {
- > .documentView-styleWrapper {
- > .documentView-titleWrapper-hover {
- display:inline-block;
+.documentView-node:hover,
+.documentView-node-topmost:hover {
+ >.documentView-styleWrapper {
+ >.documentView-titleWrapper-hover {
+ display: inline-block;
}
}
- > .documentView-styleWrapper {
- > .documentView-captionWrapper {
+
+ >.documentView-styleWrapper {
+ >.documentView-captionWrapper {
opacity: 1;
}
}
}
+
.contentFittingDocumentView {
position: relative;
display: flex;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26cf52f17..7f6905042 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { AudioField } from "../../../fields/URLField";
-import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
@@ -47,6 +47,7 @@ import { PresBox } from './PresBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
import { ScriptingBox } from "./ScriptingBox";
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
const { Howl } = require('howler');
interface Window {
@@ -124,6 +125,8 @@ export interface DocumentViewSharedProps {
disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
pointerEvents?: string;
scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
+ createNewFilterDoc?: () => void;
+ updateFilterDoc?: (doc: Doc) => void;
}
export interface DocumentViewProps extends DocumentViewSharedProps {
// properties specific to DocumentViews but not to FieldView
@@ -160,6 +163,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
@observer
export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) {
+ public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
@observable _animateScalingTo = 0;
@observable _mediaState = 0;
@observable _pendingDoubleClick = false;
@@ -197,7 +201,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
@computed get nativeWidth() { return this.props.NativeWidth(); }
@computed get nativeHeight() { return this.props.NativeHeight(); }
- @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
+ @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onfClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
@@ -450,10 +454,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}, console.log);
UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
} else if (!Doc.IsSystem(this.rootDoc)) {
- if (this.props.Document.type !== DocumentType.LABEL) {
- UndoManager.RunInBatch(() => this.props.addDocTab((this.rootDoc._fullScreenView as Doc) || this.rootDoc, "lightbox"), "double tap");
- SelectionManager.DeselectAll();
- }
+ UndoManager.RunInBatch(() =>
+ LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.())
+ //this.props.addDocTab((this.rootDoc._fullScreenView as Doc) || this.rootDoc, "lightbox")
+ , "double tap");
+ SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
} else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
@@ -505,6 +510,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
!(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+ !this.Document.ignoreClick &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
!CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
@@ -599,7 +605,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
alert((e.target as any)?.closest?.("*.lm_content") ?
"You can't perform this move most likely because you don't have permission to modify the destination." :
- "linking to document tabs not yet supported. Drop link on document content.");
+ "Linking to document tabs not yet supported. Drop link on document content.");
return;
}
const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
@@ -656,95 +662,110 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
- cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
+ cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
this.props.contextMenuItems?.().forEach(item =>
item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
- const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
- const appearance = cm.findByDescription("UI Controls...");
- const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
- !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
- DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
- !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
-
- if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
- !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
- const existingOnClick = cm.findByDescription("OnClick...");
- const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-
- 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" });
- !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.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), 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) {
- const options = cm.findByDescription("Options...");
- const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
- !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
- onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
- !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
- onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
- onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
- onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
- !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
- } else if (DocListCast(this.Document.links).length) {
- onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
- onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
- onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
- !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ if (!this.props.Document.isFolder) {
+ const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
+ const appearance = cm.findByDescription("UI Controls...");
+ const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
+ !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+ appearanceItems.push({
+ description: "Add a Field", event: () => {
+ const alias = Doc.MakeAlias(this.rootDoc);
+ alias.layout = FormattedTextBox.LayoutString("newfield");
+ alias.title = "newfield";
+ alias._yMargin = 10;
+ alias._height = 35;
+ alias._width = 100;
+ alias.syncLayoutFieldWithTitle = true;
+ alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
+ alias.y = NumCast(this.rootDoc.y);
+ this.props.addDocument?.(alias);
+ }, icon: "eye"
+ });
+ DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
+ !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
+
+ if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+ !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
+ const existingOnClick = cm.findByDescription("OnClick...");
+ const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+
+ 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" });
+ !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.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), 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) {
+ const options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+
+ onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+ onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
+ !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
+ onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
+ onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+ } else if (DocListCast(this.Document.links).length) {
+ onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
+ onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
+ onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ }
}
- }
- const funcs: ContextMenuProps[] = [];
- if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
- funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
- funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
- funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
- cm.addItem({ description: "OnDrag...", noexpand: true, subitems: funcs, icon: "asterisk" });
- }
+ const funcs: ContextMenuProps[] = [];
+ if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
+ funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+ funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
+ funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
+ cm.addItem({ description: "OnDrag...", noexpand: true, subitems: funcs, icon: "asterisk" });
+ }
- const more = cm.findByDescription("More...");
- const moreItems = more && "subitems" in more ? more.subitems : [];
- if (!Doc.IsSystem(this.rootDoc)) {
- (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
- if (!Doc.UserDoc().noviceMode) {
- moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
- moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
-
- if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
- moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
- moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
- moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+ const more = cm.findByDescription("More...");
+ const moreItems = more && "subitems" in more ? more.subitems : [];
+ if (!Doc.IsSystem(this.rootDoc)) {
+ (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+ if (!Doc.UserDoc().noviceMode) {
+ moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+ moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
+
+ if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+ moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
+ moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
+ moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+ }
+ moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
}
- moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
}
- }
- if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && CurrentUserUtils.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
- moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
- }
-
- !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
- cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
+ if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && CurrentUserUtils.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
+ moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+ }
- const help = cm.findByDescription("Help...");
- const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
- !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
- helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
- !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
- cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
+ !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+ cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
+ const help = cm.findByDescription("Help...");
+ const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
+ !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+ helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+ !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+ cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
+ }
if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
- !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
+ DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
}
rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
@@ -792,6 +813,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>;
}
+ get indicatorIcon() {
+ if (this.props.Document["acl-Public"] !== SharingPermissions.None) return "globe-americas";
+ else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return "users";
+ else return "user";
+ }
+
@undoBatch
hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true)
anchorPanelWidth = () => this.props.PanelWidth() || 1;
@@ -881,7 +908,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
TraceMobx();
const showTitle = this.ShowTitle?.split(":")[0];
const showTitleHover = this.ShowTitle?.includes(":hover");
- const showCaption = StrCast(this.layoutDoc._showCaption);
+ const showCaption = StrCast(this.layoutDoc._showCaption) && this.Document._viewType !== CollectionViewType.Carousel;
const captionView = !showCaption ? (null) :
<div className="documentView-captionWrapper"
style={{
@@ -889,8 +916,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
color: StrCast(this.layoutDoc["caption-color"])
}}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
- yMargin={10}
- xMargin={10}
+ yPadding={10}
+ xPadding={10}
hideOnLeave={true}
styleProvider={this.captionStyleProvider}
dontRegisterView={true}
@@ -951,6 +978,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]"; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way
+ const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
+ const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
@@ -966,8 +995,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
border: highlighting && this.borderRounding && highlightStyle === "dashed" ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
boxShadow,
+ clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined
}}>
- {PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc}
+ {!borderPath.path ? internal :
+ <>
+ {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
+ {internal}
+ </div> */}
+ {internal}
+ <div key="border2" className="documentView-customBorder" style={{ pointerEvents: "none" }} >
+ <svg style={{ overflow: "visible" }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
+ <path d={borderPath.path} style={{ stroke: "black", fill: "transparent", strokeWidth: borderPath.width }} />
+ </svg>
+ </div>
+ </>
+ }
</div>;
}
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 0fc7a752f..86250c9d1 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -20,6 +20,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
select: (isCtrlPressed: boolean) => void;
isContentActive: (outsideReaction?: boolean) => boolean;
+ isDocumentActive?: () => boolean;
isSelected: (outsideReaction?: boolean) => boolean;
scaling?: () => number;
setHeight: (height: number) => void;
@@ -31,8 +32,8 @@ export interface FieldViewProps extends DocumentViewSharedProps {
width?: number;
background?: string;
color?: string;
- xMargin?: number;
- yMargin?: number;
+ xPadding?: number;
+ yPadding?: number;
}
@observer
diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss
index d32cc0d2b..107ad2e36 100644
--- a/src/client/views/nodes/FilterBox.scss
+++ b/src/client/views/nodes/FilterBox.scss
@@ -1,17 +1,155 @@
-
-
.filterBox-flyout {
- width: 400px;
display: block;
text-align: left;
+ font-weight: 100;
+
.filterBox-flyout-facet {
- background-color: lightgray;
- text-align: left;
- display: inline-block;
- position: relative;
- width: 100%;
+ background-color: white;
+ text-align: left;
+ display: inline-block;
+ position: relative;
+ width: 100%;
+
+ .filterBox-flyout-facet-check {
+ margin-right: 6px;
+ }
+ }
+}
+
+
+.filter-bookmark {
+ //display: flex;
+
+ .filter-bookmark-icon {
+ float: right;
+ margin-right: 10px;
+ margin-top: 7px;
+ }
+}
+
+// .filterBox-bottom {
+// // position: fixed;
+// // bottom: 0;
+// // width: 100%;
+// }
+
+.filterBox-select {
+ // width: 90%;
+ margin-top: 5px;
+ // margin-bottom: 15px;
+}
+
+
+.filterBox-saveBookmark {
+ background-color: #e9e9e9;
+ border-radius: 11px;
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin: 8px;
+ display: flex;
+ font-size: 11px;
+ cursor: pointer;
+
+ &:hover {
+ background-color: white;
+ }
+
+ .filterBox-saveBookmark-icon {
+ margin-right: 6px;
+ margin-top: 4px;
+ margin-left: 2px;
+ }
+
+}
+
+.filterBox-select-scope,
+.filterBox-select-bool,
+.filterBox-addWrapper,
+.filterBox-select-matched,
+.filterBox-saveWrapper {
+ font-size: 10px;
+ justify-content: center;
+ justify-items: center;
+ padding-bottom: 10px;
+ display: flex;
+}
+
+.filterBox-addWrapper {
+ font-size: 11px;
+ width: 100%;
+}
+
+.filterBox-saveWrapper {
+ width: 100%;
+}
+
+// .filterBox-top {
+// padding-bottom: 20px;
+// border-bottom: 2px solid black;
+// position: fixed;
+// top: 0;
+// width: 100%;
+// }
+
+.filterBox-select-scope {
+ padding-bottom: 20px;
+ border-bottom: 2px solid black;
+}
+
+.filterBox-title {
+ font-size: 15;
+ // border: 2px solid black;
+ width: 100%;
+ align-self: center;
+ text-align: center;
+ background-color: #d3d3d3;
+}
+
+.filterBox-select-bool {
+ margin-top: 6px;
+}
+
+.filterBox-select-text {
+ margin-right: 8px;
+ margin-left: 8px;
+ margin-top: 3px;
+}
+
+.filterBox-select-box {
+ margin-right: 2px;
+ font-size: 30px;
+ border: 0;
+ background: transparent;
+}
+
+.filterBox-selection {
+ border-radius: 6px;
+ border: none;
+ background-color: #e9e9e9;
+ padding: 2px;
+
+ &:hover {
+ background-color: white;
+ }
+}
+
+.filterBox-addFilter {
+ width: 120px;
+ background-color: #e9e9e9;
+ border-radius: 12px;
+ padding: 5px;
+ margin: 5px;
+ display: flex;
+ text-align: center;
+ justify-content: center;
+
+ &:hover {
+ background-color: white;
}
}
+
.filterBox-treeView {
display: flex;
flex-direction: column;
@@ -20,24 +158,23 @@
position: absolute;
right: 0;
top: 0;
- border-left: solid 1px;
z-index: 1;
+ background-color: #9F9F9F;
+
+ .filterBox-tree {
+ z-index: 0;
+ }
.filterBox-addfacet {
display: inline-block;
width: 200px;
height: 30px;
- background: darkGray;
text-align: left;
.filterBox-addFacetButton {
display: flex;
margin: auto;
cursor: pointer;
-
- .filterBox-span {
- margin-right: 15px;
- }
}
>div,
@@ -50,6 +187,7 @@
.filterBox-tree {
display: inline-block;
width: 100%;
- height: calc(100% - 30px);
+ margin-bottom: 10px;
+ //height: calc(100% - 30px);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index bf8d2b99e..78449636a 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -2,55 +2,114 @@ import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { computed, observable, action, trace, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Field, Opt, DocListCastAsync } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, Opt, DocListCastAsync, HeightSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { List } from "../../../fields/List";
import { RichTextField } from "../../../fields/RichTextField";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero, returnTrue } from "../../../Utils";
+import { Cast, StrCast } from "../../../fields/Types";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionTreeView } from "../collections/CollectionTreeView";
import { ViewBoxBaseComponent } from "../DocComponent";
import { SearchBox } from "../search/SearchBox";
import { FieldView, FieldViewProps } from './FieldView';
import './FilterBox.scss';
import { Scripting } from "../../util/Scripting";
+import { SelectionManager } from "../../util/SelectionManager";
import { CollectionView } from "../collections/CollectionView";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
+import Select from "react-select";
+import { UserOptions } from "../../util/GroupManager";
+import { DocumentViewProps } from "./DocumentView";
+import { DefaultStyleProvider, StyleProp } from "../StyleProvider";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { EditableView } from "../EditableView";
+import { undoBatch } from "../../util/UndoManager";
type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
const FilterBoxDocument = makeInterface(documentSchema);
@observer
export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
+
+ constructor(props: Readonly<FieldViewProps>) {
+ super(props);
+ const targetDoc = FilterBox.targetDoc;
+ if (targetDoc && !targetDoc.currentFilter) CurrentUserUtils.setupFilterDocs(targetDoc);
+ }
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
+ public _filterSelected = false;
+ public _filterMatch = "matched";
+
+ @computed static get currentContainingCollectionDoc() {
+ let docView: any = SelectionManager.Views()[0];
+ let doc = docView.Document;
+
+ while (docView && doc && doc.type !== "collection") {
+ doc = docView.props.ContainingCollectionDoc;
+ docView = docView.props.ContainingCollectionView;
+ }
+
+ return doc;
+ }
+
+ // /**
+ // * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
+ // */
+
+
+ // trying to get this to work rather than the version below this
+
+ // @computed static get targetDoc() {
+ // console.log(FilterBox.currentContainingCollectionDoc.type);
+ // if (FilterBox._filterScope === "Current Collection") {
+ // return FilterBox.currentContainingCollectionDoc;
+ // }
+ // else return CollectionDockingView.Instance.props.Document;
+ // // return FilterBox._filterScope === "Current Collection" ? SelectionManager.Views()[0].Document || CollectionDockingView.Instance.props.Document : CollectionDockingView.Instance.props.Document;
+ // }
+
+
+ /**
+ * @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() {
+ if (SelectionManager.Views().length) {
+ return SelectionManager.Views()[0]?.Document.type === DocumentType.COL ? SelectionManager.Views()[0].Document : SelectionManager.Views()[0]?.props.ContainingCollectionDoc!;
+ }
+ return CurrentUserUtils.ActiveDashboard;
+ }
+
@observable _loaded = false;
componentDidMount() {
- reaction(() => DocListCastAsync(CollectionDockingView.Instance.props.Document.data),
+ reaction(() => DocListCastAsync(this.layoutDoc.data),
async (activeTabsAsync) => {
const activeTabs = await activeTabsAsync;
activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
runInAction(() => this._loaded = true);
}, { fireImmediately: true });
}
+ @observable _allDocs = [] as Doc[];
@computed get allDocs() {
- const allDocs = new Set<Doc>();
- if (this._loaded && CollectionDockingView.Instance) {
- const activeTabs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+ // trace();
+ const targetDoc = FilterBox.targetDoc;
+ if (this._loaded && targetDoc) {
+ const allDocs = new Set<Doc>();
+ const activeTabs = DocListCast(targetDoc.data);
SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc));
- setTimeout(() => CollectionDockingView.Instance.props.Document.allDocuments = new List<Doc>(Array.from(allDocs)));
+ setTimeout(action(() => this._allDocs = Array.from(allDocs)));
}
- return allDocs;
+ return this._allDocs;
}
@computed get _allFacets() {
+ // trace();
const noviceReqFields = ["author", "tags", "text", "type"];
const noviceLayoutFields: string[] = [];//["_curPage"];
const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
@@ -60,6 +119,21 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
}
+
+ /**
+ * The current attributes selected to filter based on
+ */
+ @computed get activeAttributes() {
+ return DocListCast(this.dataDoc[this.props.fieldKey]);
+ }
+
+ /**
+ * @returns a string array of the current attributes
+ */
+ @computed get currentFacets() {
+ return this.activeAttributes.map(attribute => StrCast(attribute.title));
+ }
+
gatherFieldValues(dashboard: Doc, facetKey: string) {
const childDocs = DocListCast((dashboard.data as any)[0].data);
const valueSet = new Set<string>();
@@ -80,7 +154,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
subDocs.forEach((t) => {
const facetVal = t[facetKey];
if (facetVal instanceof RichTextField) rtFields++;
- facetVal && valueSet.add(Field.toString(facetVal as Field));
+ facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
const fieldKey = Doc.LayoutFieldKey(t);
const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
@@ -92,28 +166,39 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
});
return { strings: Array.from(valueSet.keys()), rtFields };
}
- /**
- * Responds to clicking the check box in the flyout menu
- */
- facetClick = (facetHeader: string) => {
- const targetDoc = CollectionDockingView.Instance.props.Document;
- const found = DocListCast(this.dataDoc[this.props.fieldKey]).findIndex(doc => doc.title === facetHeader);
+ removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
+ public removeFilter = (filterName: string) => {
+ const targetDoc = FilterBox.targetDoc;
+ const filterDoc = targetDoc.currentFilter as Doc;
+ const attributes = DocListCast(filterDoc.data);
+ const found = attributes.findIndex(doc => doc.title === filterName);
if (found !== -1) {
- (this.dataDoc[this.props.fieldKey] as List<Doc>).splice(found, 1);
+ (filterDoc.data as List<Doc>).splice(found, 1);
const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
if (docFilter) {
let index: number;
- while ((index = docFilter.findIndex(item => item.split(":")[0] === facetHeader)) !== -1) {
+ while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
docFilter.splice(index, 1);
}
}
const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
if (docRangeFilters) {
let index: number;
- while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === facetHeader)) !== -1) {
+ while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
docRangeFilters.splice(index, 3);
}
}
+ }
+ return true;
+ }
+ /**
+ * Responds to clicking the check box in the flyout menu
+ */
+ facetClick = (facetHeader: string) => {
+ const targetDoc = FilterBox.targetDoc;
+ const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
+ if (found !== -1) {
+ this.removeFilter(facetHeader);
} else {
const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data);
const facetValues = this.gatherFieldValues(targetDoc, facetHeader);
@@ -131,13 +216,18 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
});
let newFacet: Opt<Doc>;
if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
- newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, system: true, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, _forceActive: true, ignoreClick: true });
+ newFacet = Docs.Create.TextDocument("", {
+ _width: 100, _height: 25, system: true, _stayInCollection: true, target: targetDoc,
+ treeViewExpandedView: "layout", title: facetHeader, _treeViewOpen: true, _forceActive: true, ignoreClick: true
+ });
Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
newFacet._textBoxPadding = 4;
const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
} else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
- newFacet = Docs.Create.SliderDocument({ title: facetHeader, _overflow: "visible", _fitWidth: true, _height: 40, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", treeViewOpen: true });
+ newFacet = Docs.Create.SliderDocument({
+ title: facetHeader, _overflow: "visible", _fitWidth: true, target: targetDoc, _height: 40, _stayInCollection: true, treeViewExpandedView: "layout", _treeViewOpen: true
+ });
const newFacetField = Doc.LayoutFieldKey(newFacet);
const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
@@ -147,7 +237,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
- const scriptText = `setDocFilterRange(this?.target, "${facetHeader}", range)`;
+ const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
} else {
newFacet = new Doc();
@@ -160,7 +250,8 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
newFacet.target = targetDoc;
newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${facetHeader}")`);
}
- newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
+ newFacet.hideContextMenu = true;
+ Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
}
}
@@ -169,28 +260,145 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
return script ? () => script : undefined;
}
+
+ /**
+ * Sets whether filters are ANDed or ORed together
+ */
+ @action
+ changeBool = (e: any) => {
+ (FilterBox.targetDoc.currentFilter as Doc).filterBoolean = e.currentTarget.value;
+ }
+
+ /**
+ * Changes whether to select matched or unmatched documents
+ */
+ @action
+ changeMatch = (e: any) => {
+ this._filterMatch = e.currentTarget.value;
+ }
+
+ @action
+ changeSelected = () => {
+ if (this._filterSelected) {
+ this._filterSelected = false;
+ SelectionManager.DeselectAll();
+ } else {
+ this._filterSelected = true;
+ // helper method to select specified docs
+ }
+ }
+
+ FilteringStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) {
+ switch (property.split(":")[0]) {
+ case StyleProp.Decorations:
+ if (doc && !doc.treeViewHideHeaderFields) {
+ return <>
+ <div style={{ marginRight: "5px", fontSize: "10px" }}>
+ <select className="filterBox-selection">
+ <option value="Is" key="Is">Is</option>
+ <option value="Is Not" key="Is Not">Is Not</option>
+ </select>
+ </div>
+ <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
+ <FontAwesomeIcon icon={"times"} size="sm" />
+ </div>
+ </>;
+ }
+ }
+ return DefaultStyleProvider(doc, props, property);
+ }
+
suppressChildClick = () => ScriptField.MakeScript("")!;
- @computed get flyoutpanel() {
- return <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
- {this._allFacets.map(facet => <label className="filterBox-flyout-facet" key={`${facet}`} onClick={e => this.facetClick(facet)}>
- <input type="checkbox" onChange={emptyFunction} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
- <span className="checkmark" />
- {facet}
- </label>)}
- </div>;
+
+ /**
+ * Adds a filterDoc to the list of saved filters
+ */
+ saveFilter = () => {
+ Doc.AddDocToList(Doc.UserDoc(), "savedFilters", this.props.Document);
+ }
+
+ /**
+ * Changes the title of the filterDoc
+ */
+ onTitleValueChange = (val: string) => {
+ this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc.title}`;
+ return true;
+ }
+
+ /**
+ * The flyout from which you can select a saved filter to apply
+ */
+ @computed get flyoutPanel() {
+ return DocListCast(Doc.UserDoc().savedFilters).map(doc => {
+ return <>
+ <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 50, border: "2px" }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
+ {StrCast(doc.title)}
+ </div>
+ </>;
+ }
+ );
+ }
+ setTreeHeight = (hgt: number) => {
+ this.layoutDoc._height = hgt + 140; // 50? need to add all the border sizes together.
}
+
+ /**
+ * add lock and hide button decorations for the "Dashboards" flyout TreeView
+ */
+ FilterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
+ switch (property.split(":")[0]) {
+ case StyleProp.Decorations:
+ return !doc || doc.treeViewHideHeaderFields ? (null) :
+ <>
+ <div className={`styleProvider-treeView-hide${doc.hidden ? "-active" : ""}`} onClick={undoBatch(e =>
+ this.removeFilter(StrCast(doc.title))
+ )}>
+ <FontAwesomeIcon icon={"trash"} size="sm" />
+ </div>
+ </>;
+ }
+ return this.props.styleProvider?.(doc, props, property);
+ }
+
+ layoutHeight = () => this.layoutDoc[HeightSym]();
render() {
const facetCollection = this.props.Document;
+ // TODO uncomment the line below when the treeview x works
+ // const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
+ const options = this._allFacets.map(facet => ({ value: facet, label: facet }));
return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
- <div className="filterBox-addFacet" style={{ width: "100%" }} onPointerDown={e => e.stopPropagation()}>
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyoutpanel}>
- <div className="filterBox-addFacetButton">
- <FontAwesomeIcon icon={"edit"} size={"lg"} />
- <span className="filterBox-span">Choose Facets</span>
- </div>
- </Flyout>
+
+ <div className="filterBox-title">
+ <EditableView
+ key="editableView"
+ contents={this.props.Document.title}
+ height={24}
+ fontSize={15}
+ GetValue={() => StrCast(this.props.Document.title)}
+ SetValue={this.onTitleValueChange}
+ />
</div>
+
+ <div className="filterBox-select-bool">
+ <select className="filterBox-selection" onChange={this.changeBool}>
+ <option value="AND" key="AND" selected={(FilterBox.targetDoc.currentFilter as Doc)?.filterBoolean === "AND"}>AND</option>
+ <option value="OR" key="OR" selected={(FilterBox.targetDoc.currentFilter as Doc)?.filterBoolean === "OR"}>OR</option>
+ </select>
+ <div className="filterBox-select-text">filters together</div>
+ </div>
+
+ <div className="filterBox-select">
+ <Select
+ placeholder="Add a filter..."
+ options={options}
+ isMulti={false}
+ onChange={val => this.facetClick((val as UserOptions).value)}
+ value={null}
+ closeMenuOnSelect={true}
+ />
+ </div>
+
<div className="filterBox-tree" key="tree">
<CollectionTreeView
Document={facetCollection}
@@ -198,15 +406,14 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
fieldKey={this.props.fieldKey}
CollectionView={undefined}
disableDocBrushing={true}
- setHeight={returnFalse} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
- onChildClick={this.suppressChildClick}
+ setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
ContainingCollectionView={this.props.ContainingCollectionView}
PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
+ PanelHeight={this.layoutHeight}
rootSelected={this.props.rootSelected}
renderDepth={1}
dropAction={this.props.dropAction}
@@ -220,17 +427,53 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
whenChildContentsActiveChanged={returnFalse}
treeViewHideTitle={true}
focus={returnFalse}
- treeViewHideHeaderFields={true}
+ treeViewHideHeaderFields={false}
onCheckedClick={this.scriptField}
dontRegisterView={true}
- styleProvider={this.props.styleProvider}
+ styleProvider={this.FilterStyleProvider}
layerProvider={this.props.layerProvider}
docViewPath={this.props.docViewPath}
scriptContext={this.props.scriptContext}
moveDocument={returnFalse}
- removeDocument={returnFalse}
+ removeDocument={this.removeFilterDoc}
addDocument={returnFalse} />
</div>
+ <div className="filterBox-bottom">
+ {/* <div className="filterBox-select-matched">
+ <input className="filterBox-select-box" type="checkbox"
+ onChange={this.changeSelected} />
+ <div className="filterBox-select-text">select</div>
+ <select className="filterBox-selection" onChange={e => this.changeMatch(e)}>
+ <option value="matched" key="matched">matched</option>
+ <option value="unmatched" key="unmatched">unmatched</option>
+ </select>
+ <div className="filterBox-select-text">documents</div>
+ </div> */}
+
+ <div style={{ display: "flex" }}>
+ <div className="filterBox-saveWrapper">
+ <div className="filterBox-saveBookmark"
+ onPointerDown={this.saveFilter}
+ >
+ <div>SAVE</div>
+ </div>
+ </div>
+ <div className="filterBox-saveWrapper">
+ <div className="filterBox-saveBookmark">
+ <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
+ <div>FILTERS</div>
+ </Flyout>
+ </div>
+ </div>
+ <div className="filterBox-saveWrapper">
+ <div className="filterBox-saveBookmark"
+ onPointerDown={this.props.createNewFilterDoc}
+ >
+ <div>NEW</div>
+ </div>
+ </div>
+ </div>
+ </div>
</div>;
}
}
@@ -245,22 +488,26 @@ Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader:
}
return undefined;
});
-Scripting.addGlobal(function readFacetData(targetDoc: Doc, facetHeader: string) {
- const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
+Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
+ const allCollectionDocs = new Set<Doc>();
+ const activeTabs = DocListCast(layoutDoc.data);
+ SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allCollectionDocs.add(doc));
const set = new Set<string>();
if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
const facetValues = Array.from(set).filter(v => v);
let nonNumbers = 0;
+
facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
const doc = new Doc();
doc.system = true;
doc.title = facetValue.toString();
- doc.target = targetDoc;
+ doc.target = layoutDoc;
doc.facetHeader = facetHeader;
doc.facetValue = facetValue;
+ doc.treeViewHideHeaderFields = true;
doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(self.target, self.facetHeader, self.facetValue)");
return doc;
});
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8a6946b78..c7067f313 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,14 +1,13 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { Dictionary } from 'typescript-collections';
import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc';
import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { createSchema, makeInterface } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, OmitKeys, returnOne, Utils } from '../../../Utils';
@@ -59,8 +58,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
+ setViewSpec = (anchor: Doc, preview: boolean) => {
+
+ } // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
componentDidMount() {
+ this.props.setContentView?.(this); // bcz: do not remove this. without it, stepping into an image in the lightbox causes an infinite loop....
this._disposers.sizer = reaction(() => (
{
forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
@@ -102,8 +105,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const targetDoc = layoutDoc[DataSym];
if (targetDoc[targetField] instanceof ImageField) {
this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
- Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc));
- Doc.SetNativeWidth(this.dataDoc, Doc.NativeHeight(targetDoc));
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
+ Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
e.stopPropagation();
}
}
@@ -315,6 +318,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _marqueeing: number[] | undefined;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
+ TraceMobx();
return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
}
@action
@@ -331,7 +335,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
- return (<div className={`imageBox`} onContextMenu={this.specificContextMenu} ref={this._mainCont}
+ return (<div className="imageBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
style={{
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
@@ -340,32 +344,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}} >
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- CollectionView={undefined}
- PanelHeight={this.props.PanelHeight}
- scaling={returnOne}
- ScreenToLocalTransform={this.screenToLocalTransform}
- PanelWidth={this.props.PanelWidth}
fieldKey={this.annotationKey}
+ CollectionView={undefined}
isAnnotationOverlay={true}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
annotationLayerHostsContent={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
isContentActive={this.isContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}>
+ scaling={returnOne}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}>
{this.contentFunc}
</CollectionFreeFormView>
{this.annotationLayer}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
- <MarqueeAnnotator rootDoc={this.rootDoc}
- scrollTop={0} down={this._marqueeing}
+ <MarqueeAnnotator
+ rootDoc={this.rootDoc}
+ scrollTop={0}
+ down={this._marqueeing}
scaling={this.props.scaling}
docView={this.props.docViewPath().lastElement()}
addDocument={this.addDocument}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 45e3c8382..b73fb10df 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -26,7 +26,7 @@ interface LinkDocPreviewProps {
@observer
export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@action public static Clear() { LinkDocPreview.LinkInfo = undefined; }
- @action public static SetLinkInfo(info?: LinkDocPreviewProps) { LinkDocPreview.LinkInfo != info && (LinkDocPreview.LinkInfo = info); }
+ @action public static SetLinkInfo(info?: LinkDocPreviewProps) { LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info); }
_infoRef = React.createRef<HTMLDivElement>();
@observable public static LinkInfo: Opt<LinkDocPreviewProps>;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d55156057..d5056d934 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -262,6 +262,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
setPdfViewer={this.setPdfViewer}
addDocument={this.addDocument}
+ moveDocument={this.moveDocument}
+ removeDocument={this.removeDocument}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
startupLive={true}
ContentScaling={this.props.scaling}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 8c5f77550..f3fb6ff17 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
-import { Doc, DocListCast, DocListCastAsync } from "../../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
@@ -29,6 +29,7 @@ import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
import Color = require("color");
+import { LightboxView } from "../LightboxView";
export enum PresMovement {
Zoom = "zoom",
@@ -246,11 +247,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// }
}
- stopTempMedia = (targetDoc: Doc) => {
- if (targetDoc.type === DocumentType.AUDIO) {
+ stopTempMedia = (targetDocField: FieldResult) => {
+ const targetDoc = Cast(targetDocField, Doc, null);
+ if (targetDoc?.type === DocumentType.AUDIO) {
if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
targetDoc._audioStop = true;
- } else if (targetDoc.type === DocumentType.VID) {
+ } else if (targetDoc?.type === DocumentType.VID) {
if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
targetDoc._triggerVideoStop = true;
}
@@ -330,14 +332,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.rootDoc._itemIndex = index;
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
- if (from && from.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
- const mediaDocList = DocListCast(from.mediaStopTriggerList);
- mediaDocList.forEach((doc) => {
- this.stopTempMedia(Cast(doc.presentationTargetDoc, Doc, null));
- });
+ if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
+ DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
}
- if (from && from.mediaStop === "auto" && this.layoutDoc.presStatus !== PresStatus.Edit) {
- this.stopTempMedia(Cast(from.presentationTargetDoc, Doc, null));
+ if (from?.mediaStop === "auto" && this.layoutDoc.presStatus !== PresStatus.Edit) {
+ this.stopTempMedia(from.presentationTargetDoc);
}
// If next slide is audio / video 'Play automatically' then the next slide should be played
if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && (activeItem.mediaStart === "auto")) {
@@ -433,10 +432,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
};
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
- openInTab(targetDoc);
+ LightboxView.SetLightboxDoc(targetDoc);
} else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
+ LightboxView.SetLightboxDoc(undefined);
await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection); // documents open in new tab instead of on right
} else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
+ LightboxView.SetLightboxDoc(undefined);
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection); // documents open in new tab instead of on right
}
@@ -574,6 +575,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (this._presTimer) clearTimeout(this._presTimer);
this.layoutDoc.presStatus = PresStatus.Manual;
this.layoutDoc.presLoop = false;
+ this.childDocs.forEach(this.stopTempMedia);
}
}
@@ -633,10 +635,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
updateMinimize = () => {
const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+ console.log("case 1");
this.layoutDoc.presStatus = PresStatus.Edit;
Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
CollectionDockingView.AddSplit(this.rootDoc, "right");
} else if (this.layoutDoc.context && docView) {
+ console.log("case 2");
this.layoutDoc.presStatus = PresStatus.Edit;
clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
@@ -647,15 +651,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
docView.props.removeDocument?.(this.layoutDoc);
Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
} else {
- this.layoutDoc.presStatus = PresStatus.Edit;
- clearTimeout(this._presTimer);
- const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
- this.rootDoc.y = pt[1] + 10;
- this.rootDoc._height = 35;
- this.rootDoc._width = 250;
- this.props.addDocTab?.(this.rootDoc, "close");
- Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+ console.log("case 3");
+ // TODO glr: fix this case
}
}
@@ -1193,7 +1190,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None';
activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
return (
- <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
+ <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
<div className="ribbon-box">
Movement
{isPresCollection || (isPresCollection && isPinWithView) ?
@@ -1248,7 +1245,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-doubleButton">
{isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>}
{isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>}
- <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Open</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip>
</div>
{(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <>
<div className="ribbon-doubleButton" >
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 862344a94..3c281086a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -15,16 +15,17 @@ import { emptyFunction, OmitKeys, returnFalse, returnOne, Utils, numberRange } f
import { DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
+import { CaptureManager } from "../../util/CaptureManager";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline";
import { ContextMenu } from "../ContextMenu";
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
import "./ScreenshotBox.scss";
import { VideoBox } from "./VideoBox";
import { TraceMobx } from "../../../fields/util";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
import { Canvas } from 'react-three-fiber';
import * as THREE from 'three';
import { Vector3, Vector2, Camera } from "three"
@@ -116,7 +117,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
private _audioRec: any;
private _videoRec: any;
- @observable private _videoRef: HTMLVideoElement | undefined;
+ @observable private _videoRef: HTMLVideoElement | null = null;
@observable _screenCapture = false;
@computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
@@ -174,7 +175,15 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
@computed get content() {
const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
- return <video className={"videoBox-content" + interactive} key="video" ref={action((r: any) => this._videoRef = r)}
+ return <video className={"videoBox-content" + interactive} key="video"
+ ref={r => {
+ this._videoRef = r;
+ setTimeout(() => {
+ if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) { // TODO glr: use mediaState
+ this.toggleRecording();
+ }
+ }, 1000);
+ }}
autoPlay={this._screenCapture}
style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
onCanPlay={this.videoLoad}
@@ -245,6 +254,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this.dataDoc.mediaState = "paused";
const ind = DocUtils.ActiveRecordings.indexOf(this);
ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+
+ CaptureManager.Instance.open(this.rootDoc);
}
});
@@ -287,31 +298,27 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView></div>
- {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
- <div className="videoBox-dictation" style={{ position: "relative", height: this.formattedPanelHeight() }}>
+ <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
+ {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
<FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
Document={this.dataDoc[this.fieldKey + "-dictation"]}
fieldKey={"text"}
PanelHeight={this.formattedPanelHeight}
- PanelWidth={this.props.PanelWidth}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
isAnnotationOverlay={true}
select={emptyFunction}
isContentActive={returnFalse}
scaling={returnOne}
- xMargin={25}
- yMargin={10}
+ xPadding={25}
+ yPadding={10}
whenChildContentsActiveChanged={emptyFunction}
removeDocument={returnFalse}
moveDocument={returnFalse}
addDocument={returnFalse}
CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- </FormattedTextBox>
- </div>}
+ </FormattedTextBox>}
+ </div>
</div>
{!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
<div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 67f8aed3c..92d1f7446 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -4,8 +4,7 @@ import * as React from 'react';
import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
import { documentSchema } from '../../../fields/documentSchemas';
import { createSchema, makeInterface } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxBaseComponent } from '../DocComponent';
@@ -41,7 +40,7 @@ export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocume
onChange = (values: readonly number[]) => runInAction(() => {
this.dataDoc[this.minThumbKey] = values[0];
this.dataDoc[this.maxThumbKey] = values[1];
- Cast(this.layoutDoc.onThumbChanged, ScriptField, null)?.script.run({
+ ScriptCast(this.layoutDoc.onThumbChanged, null)?.script.run({
self: this.rootDoc,
scriptContext: this.props.scriptContext, range: values, this: this.layoutDoc
});
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index dd8d77603..30f0c4393 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,3 +1,13 @@
+.mini-viewer{
+ cursor: grab;
+ position: absolute;
+ right: 10;
+ top: 10;
+ opacity: 0.1;
+ transition: all 0.4s;
+ color: white;
+}
+
.videoBox {
transform-origin: top left;
width: 100%;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fb58ddefc..8b75f45b0 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -567,22 +567,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
<div style={{ position: "absolute", transition: this.transition, width: this.panelWidth(), height: this.panelHeight(), top: 0, left: `${(100 - this.heightPercent) / 2}%` }}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+ renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
+ CollectionView={undefined}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
- select={emptyFunction}
- isContentActive={this.isContentActive}
- scaling={returnOne}
- docFilters={this.timelineDocFilter}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
+ docFilters={this.timelineDocFilter}
+ select={emptyFunction}
+ isContentActive={this.isContentActive}
+ scaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
- addDocument={this.addDocWithTimecode}
- CollectionView={undefined}
- renderDepth={this.props.renderDepth + 1}>
+ addDocument={this.addDocWithTimecode}>
{this.contentFunc}
</CollectionFreeFormView>
</div>
@@ -591,16 +591,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{this.renderTimeline}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
<MarqueeAnnotator
- scrollTop={0}
rootDoc={this.rootDoc}
+ scrollTop={0}
down={this._marqueeing}
- docView={this.props.docViewPath().lastElement()}
scaling={this.marqueeFitScaling}
+ docView={this.props.docViewPath().lastElement()}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
savedAnnotations={this._savedAnnotations}
- annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current}
+ annotationLayer={this._annotationLayer.current}
+ mainCont={this._mainCont.current}
/>}
</div>
</div >);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ab178c60b..fc6f9ceab 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -2,20 +2,22 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
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, StrListCast, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { HtmlField } from "../../../fields/HtmlField";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
-import { makeInterface, listSpec } from "../../../fields/Schema";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { ComputedField } from "../../../fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { WebField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, getWordAtPoint, OmitKeys, returnOne, smoothScroll, Utils, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { Scripting } from "../../util/Scripting";
import { SnappingManager } from "../../util/SnappingManager";
import { undoBatch } from "../../util/UndoManager";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -79,7 +81,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
runInAction(() => {
this._url = this.webField?.toString() || "";
- this._annotationKey = "annotations-" + this.urlHash(this._url);
+ this._annotationKey = "annotations-" + WebBox.urlHash(this._url);
+ // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
+ this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-annotations-"+urlHash(this["${this.fieldKey}"]?.url?.toString()))`);
});
this._disposers.selection = reaction(() => this.props.isSelected(),
@@ -289,7 +293,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (future.length) {
history.push(this._url);
this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!));
- this._annotationKey = "annotations-" + this.urlHash(this._url);
+ this._annotationKey = "annotations-" + WebBox.urlHash(this._url);
return true;
}
return false;
@@ -303,13 +307,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
else future.push(this._url);
this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!));
- this._annotationKey = "annotations-" + this.urlHash(this._url);
+ this._annotationKey = "annotations-" + WebBox.urlHash(this._url);
return true;
}
return false;
}
- urlHash = (s: string) => {
+ static urlHash = (s: string) => {
return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
}
@@ -331,7 +335,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
future && (future.length = 0);
}
this._url = newUrl;
- this._annotationKey = "annotations-" + this.urlHash(this._url);
+ this._annotationKey = "annotations-" + WebBox.urlHash(this._url);
this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
} catch (e) {
console.log("WebBox URL error:" + this._url);
@@ -507,7 +511,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
PanelHeight={this.panelHeight}
dropAction={"alias"}
select={emptyFunction}
- isContentActive={this.isContentActive}
+ isContentActive={returnFalse}
ContentScaling={returnOne}
bringToFront={emptyFunction}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
@@ -555,4 +559,5 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</button>
</div>);
}
-} \ No newline at end of file
+}
+Scripting.addGlobal(function urlHash(url: string) { return WebBox.urlHash(url); }); \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 208accfc7..b98ec870a 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 } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap, selectAll } from "prosemirror-commands";
import { history } from "prosemirror-history";
@@ -68,8 +68,8 @@ const translateGoogleApi = require("translate-google-api");
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
- xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
- yMargin?: number;
+ xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
+ yPadding?: number;
noSidebar?: boolean;
dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded
}
@@ -297,7 +297,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView.updateState(EditorState.fromJSON(this.config, json));
}
}
- if (window.getSelection()?.isCollapsed) AnchorMenu.Instance.fadeOut(true);
+ if (window.getSelection()?.isCollapsed && this.props.isSelected()) {
+ AnchorMenu.Instance.fadeOut(true);
+ }
}
}
@@ -669,7 +671,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (this._break) {
const textanchor = Docs.Create.TextanchorDocument({ title: "dictation anchor" });
this.addDocument(textanchor);
- const link = DocUtils.MakeLinkToActiveAudio(textanchor, false).lastElement();
+ const link = DocUtils.MakeLinkToActiveAudio(() => textanchor, false).lastElement();
link && (Doc.GetProto(link).isDictation = true);
if (!link) return;
const audioanchor = Cast(link.anchor2, Doc, null);
@@ -1062,8 +1064,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
});
}
setupEditor(config: any, fieldKey: string) {
- const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null);
- const rtfField = Cast((!curText?.Text && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
+ const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.props.fieldKey]);
+ const rtfField = Cast((!curText && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
const self = this;
this._editorView?.destroy();
@@ -1116,7 +1118,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
FormattedTextBox.SelectOnLoadChar = "";
- } else if (curText?.Text && !FormattedTextBox.DontSelectInitialText) {
+ } else if (curText && !FormattedTextBox.DontSelectInitialText) {
selectAll(this._editorView!.state, this._editorView?.dispatch);
this.startUndoTypingBatch();
}
@@ -1130,6 +1132,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentWillUnmount() {
+ FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
this.unhighlightSearchTerms();
@@ -1234,8 +1237,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@action
onFocused = (e: React.FocusEvent): void => {
//applyDevTools.applyDevTools(this._editorView);
+ FormattedTextBox.Focused = this;
this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
}
+ @observable public static Focused: FormattedTextBox | undefined;
onClick = (e: React.MouseEvent): void => {
if (Math.abs(e.clientX - this._downX) > 4 || Math.abs(e.clientY - this._downY) > 4) {
@@ -1337,7 +1342,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._undoTyping = undefined;
return wasUndoing;
}
+ @action
onBlur = (e: any) => {
+ FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
}
@@ -1470,8 +1477,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
NativeHeight={returnZero}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.sidebarWidth}
- xMargin={0}
- yMargin={0}
+ xPadding={0}
+ yPadding={0}
scaleField={this.SidebarKey + "-scale"}
isAnnotationOverlay={false}
select={emptyFunction}
@@ -1503,7 +1510,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
const minimal = this.props.ignoreAutoHeight;
- const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
+ const margins = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
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" : "";
@@ -1522,8 +1529,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
style={{
overflow: this.autoHeight ? "hidden" : undefined,
height: this.props.height || (this.autoHeight && this.props.renderDepth ? "max-content" : undefined),
- background: this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)),
- color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color)),
+ background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+ color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
pointerEvents: interactive ? undefined : "none",
fontSize: this.props.fontSize || Cast(this.layoutDoc._fontSize, "string", null),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
@@ -1549,7 +1556,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onScroll={this.onScroll} onDrop={this.ondrop} >
<div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
style={{
- padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
+ padding: StrCast(this.layoutDoc._textBoxPadding, `${padding}px`),
pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? "none" : undefined) : undefined
}}
/>
@@ -1558,7 +1565,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
{(this.props.noSidebar || this.Document._noSidebar) || this.Document._singleLine ? (null) : this.sidebarHandle}
{!this.layoutDoc._showAudio ? (null) : this.audioHandle}
</div>
- </div>
+ </div >
);
}
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index fbc468292..1d24d6833 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -95,9 +95,10 @@
.color-preview-button {
.color-preview {
- width: 100%;
- height: 3px;
- margin-top: 3px;
+ width: 60%;
+ top: 80%;
+ position: absolute;
+ height: 4px;
}
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 5da868281..071491463 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -976,7 +976,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const row2 = <div className="antimodeMenu-row row-2" key="row2">
{this.collapsed ? this.getDragger() : (null)}
<div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
- <div className="richTextMenu-divider" key="divider 3" />,
+ <div className="richTextMenu-divider" key="divider 3" />
{[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
this.activeFontSize = val;
SelectionManager.Views().map(dv => dv.props.Document._fontSize = val);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4c181e49a..a2e7b0600 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -9,7 +9,7 @@ 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 } from "../../../Utils";
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils, returnFalse } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { Networking } from "../../Network";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -516,6 +516,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
}}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
isAnnotationOverlay={true}
+ isContentActive={returnFalse}
fieldKey={this.props.fieldKey + "-annotations"}
setPreviewCursor={this.setPreviewCursor}
PanelHeight={this.panelHeight}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index a1fc77a92..f15d51764 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -57,6 +57,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
@computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
componentDidMount() {
+ this.layoutDoc.hideLinkButton = true;
this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
}
@@ -114,6 +115,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
+ hideLinkButton={true}
/>
<div className="presItem-embeddedMask" />
</div>;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5b3e21e34..bd0ba3ad7 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -9,7 +9,6 @@ import { Scripting, scriptingGlobal } from "../client/util/Scripting";
import { SelectionManager } from "../client/util/SelectionManager";
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
import { UndoManager } from "../client/util/UndoManager";
-import { CollectionDockingView } from "../client/views/collections/CollectionDockingView";
import { intersectRect, Utils } from "../Utils";
import { DateField } from "./DateField";
import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
@@ -201,7 +200,7 @@ export class Doc extends RefField {
public [FieldsSym] = () => this[Self].___fields; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
public [WidthSym] = () => NumCast(this[SelfProxy]._width);
public [HeightSym] = () => NumCast(this[SelfProxy]._height);
- public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`;
+ public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? "-inaccessible-" : this[SelfProxy].title})`;
public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
public get [DataSym]() {
@@ -911,8 +910,8 @@ export namespace Doc {
}
export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) { return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeWidth"], useWidth ? doc[WidthSym]() : 0)); }
export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) { return !doc ? 0 : NumCast(doc._nativeHeight, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeHeight"], useHeight ? doc[HeightSym]() : 0)); }
- export function SetNativeWidth(doc: Doc, width: number | undefined) { doc[Doc.LayoutFieldKey(doc) + "-nativeWidth"] = width; }
- export function SetNativeHeight(doc: Doc, height: number | undefined) { doc[Doc.LayoutFieldKey(doc) + "-nativeHeight"] = height; }
+ export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeWidth"] = width; }
+ export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeHeight"] = height; }
const manager = new DocData();
@@ -1059,8 +1058,8 @@ export namespace Doc {
prevLayout === "icon" && (doc.deiconifyLayout = undefined);
doc.layoutKey = deiconify || "layout";
}
- export function setDocFilterRange(target: Doc, key: string, range?: number[]) {
- const container = target ?? CollectionDockingView.Instance.props.Document;
+ export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
+ if (!container) return;
const docRangeFilters = Cast(container._docRangeFilters, listSpec("string"), []);
for (let i = 0; i < docRangeFilters.length; i += 3) {
if (docRangeFilters[i] === key) {
@@ -1079,9 +1078,9 @@ 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(target: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x", toggle?: boolean, fieldPrefix?: string, append: boolean = true) {
- const container = target ?? CollectionDockingView.Instance.props.Document;
- const filterField = "_" + (fieldPrefix ? fieldPrefix + "-" : "") + "docFilters";
+ export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
+ if (!container) return;
+ const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters";
const docFilters = Cast(container[filterField], listSpec("string"), []);
runInAction(() => {
for (let i = 0; i < docFilters.length; i++) {
@@ -1160,12 +1159,12 @@ export namespace Doc {
return ndoc;
}
- export function toIcon(doc: Doc) {
- switch (StrCast(doc.type)) {
+ export function toIcon(doc?: Doc, isOpen?: boolean) {
+ switch (StrCast(doc?.type)) {
case DocumentType.IMG: return "image";
case DocumentType.COMPARISON: return "columns";
case DocumentType.RTF: return "sticky-note";
- case DocumentType.COL: return !doc.isFolder ? "folder" : "chevron-right";
+ case DocumentType.COL: return !doc?.isFolder ? "folder" + (isOpen ? "-open" : "") : "chevron-" + (isOpen ? "down" : "right");
case DocumentType.WEB: return "globe-asia";
case DocumentType.SCREENSHOT: return "photo-video";
case DocumentType.WEBCAM: return "video";
@@ -1325,6 +1324,7 @@ export namespace Doc {
}
+Scripting.addGlobal(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
@@ -1353,4 +1353,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
return docs.length ? new List(docs) : prevValue;
});
Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
+Scripting.addGlobal(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });