aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts25
-rw-r--r--src/client/util/CurrentUserUtils.ts3
-rw-r--r--src/client/views/DocComponent.tsx3
-rw-r--r--src/client/views/DocumentButtonBar.tsx4
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx1
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx42
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx15
-rw-r--r--src/client/views/nodes/FilterBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss18
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx61
-rw-r--r--src/client/views/pdf/Annotation.tsx8
-rw-r--r--src/client/views/pdf/PDFViewer.scss1
-rw-r--r--src/client/views/pdf/PDFViewer.tsx11
20 files changed, 138 insertions, 73 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1d7e4f386..ad30e7d33 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -889,6 +889,27 @@ export namespace Docs {
}
export namespace DocUtils {
+ export function Excluded(d: Doc, docFilters: string[]) {
+ const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
+ for (let i = 0; i < docFilters.length; i += 3) {
+ const [key, value, modifiers] = docFilters.slice(i, i + 3);
+ if (!filterFacets[key]) {
+ filterFacets[key] = {};
+ }
+ filterFacets[key][value] = modifiers;
+ }
+
+ if (d.z) return false;
+ for (const facetKey of Object.keys(filterFacets)) {
+ const facet = filterFacets[facetKey];
+ const xs = Object.keys(facet).filter(value => facet[value] === "x");
+ const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
+ if (failsNotEqualFacets) {
+ return true;
+ }
+ }
+ return false;
+ }
export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField) {
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
@@ -977,9 +998,9 @@ export namespace DocUtils {
DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
}
- export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string) {
+ export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean) {
const sv = DocumentManager.Instance.getDocumentView(source.doc);
- if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
+ if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
if (target.doc === Doc.UserDoc()) return undefined;
const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship, layoutKey: "layout_linkView", description }, id);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 342954a4e..3e76b189a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1127,8 +1127,9 @@ export class CurrentUserUtils {
CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
}
- public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number) {
+ public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean) {
const tbox = Docs.Create.TextDocument("", {
+ _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined,
_width: width || 200, _height: height || 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
_fontFamily: StrCast(Doc.UserDoc().fontFamily), title
});
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index b3c90a68f..893b74d75 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -123,7 +123,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
@action.bound
removeDocument(doc: Doc | Doc[]): boolean {
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
- if (effectiveAcl === AclAdmin || effectiveAcl === AclEdit) {
+ const docAcl = GetEffectiveAcl(doc);
+ if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || docAcl === AclAdmin) {
const docs = doc instanceof Doc ? [doc] : doc;
docs.map(doc => doc.isPushpin = doc.annotationOn = undefined);
const targetDataDoc = this.dataDoc;
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c5fd3c777..5ec43319b 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -363,9 +363,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
{![DocumentType.VID, DocumentType.WEB].includes(StrCast(this.view0.props.Document.type) as DocumentType) ? (null) : <div className="documentButtonBar-button">
{this.annotateButton}
</div>}
- <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
+ {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
{this.considerGoogleDocsPush}
- </div>
+ </div>}
<div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
{this.considerGoogleDocsPull}
</div>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4299dba86..5469c2358 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -86,7 +86,7 @@ export class MainView extends React.Component {
document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
new InkStrokeProperties();
this._sidebarContent.proto = undefined;
- DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
+ DocServer.setPlaygroundFields(["dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 462091f63..9de4f9c67 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -529,7 +529,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
{this.clustersButton}
</div>
- <div className="propertiesButtons-button" style={{ display: !isFreeForm ? "none" : "" }}>
+ <div className="propertiesButtons-button" style={{ display: !isFreeForm && !isText ? "none" : "" }}>
{this.fitContentButton}
</div>
<div className="propertiesButtons-button" style={{ display: !isInk ? "none" : "" }}>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4ee00e0f4..423d39951 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -176,7 +176,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
smoothScroll(doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500, this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
}
- afterFocus && setTimeout(() => afterFocus?.(), 500);
+ afterFocus && setTimeout(afterFocus, 500);
}
getDisplayDoc(doc: Doc, dxf: () => Transform, width: () => number) {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 26fe00657..649b8c175 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -142,6 +142,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const docsforFilter: Doc[] = [];
childDocs.forEach((d) => {
+ if (DocUtils.Excluded(d, docFilters)) return;
let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0));
const fieldKey = Doc.LayoutFieldKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 072cd23db..06c35de03 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -664,7 +664,7 @@ export class TreeView extends React.Component<TreeViewProps> {
ignoreFields: string[] | undefined,
firstLevel: boolean,
whenActiveChanged: (isActive: boolean) => void,
- dontRegisterView: boolean | undeifned) {
+ dontRegisterView: boolean | undefined) {
const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 75ad8e5b0..a3f76839d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -104,28 +104,32 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@observable canPanY: boolean = true;
@computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; }
- @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; }
- @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; }
+ @computed get fitToContent() { return this.props.fitToBox || (this.Document._fitToBox && !this.isAnnotationOverlay); }
+ @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent ? this.props.ContentScaling() : 1; }
@computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
@computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), NumCast(this.Document._nativeWidth)); }
@computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), NumCast(this.Document._nativeHeight)); }
private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
- private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0;
- private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0;
- private zoomScaling = () => (this.fitToContentScaling / this.parentScaling) * (this.fitToContent ?
- Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y),
- this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) :
- NumCast(this.Document[this.scaleFieldKey], 1))
-
+ private panX = () => this.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0;
+ private panY = () => this.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0;
+ private zoomScaling = () => {
+ const mult = this.fitToContentScaling / this.parentScaling;
+ if (this.fitToContent) {
+ const zs = !this.childDocs.length ? 1 :
+ Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x));
+ return mult * (this.props.isAnnotationOverlay ? Math.min(zs, 1) : zs);
+ }
+ return mult * NumCast(this.Document[this.scaleFieldKey], 1);
+ }
@computed get cachedCenteringShiftX(): number {
const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
- return !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections
+ return !this.props.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
- return !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections
+ return !this.props.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections
}
@computed get cachedGetLocalTransform(): Transform {
return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
@@ -776,9 +780,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
- if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) {
- deltaScale = 1 / this.zoomScaling();
- }
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.getTransform().transformPoint(pointX, pointY);
const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
@@ -1106,7 +1107,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
});
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
- const elements: ViewDefResult[] = computedElementData.slice();
+ const elements = computedElementData.slice();
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
elements.push({
@@ -1127,8 +1128,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
}));
- if (this.props.isAnnotationOverlay) {
- this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
+ if (this.props.isAnnotationOverlay) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+ if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, NumCast(this.props.Document[this.scaleFieldKey], 1));
+ else this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
}
this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1623,6 +1625,12 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const pany = -this.props.panY();
const zoom = this.props.zoomScaling();
return <div className={freeformclass}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target.parentElement)?.overflow === "visible") { // if collection is visible, then scrolling will mess things up since there are no scroll bars
+ target.scrollTop = target.scrollLeft = 0;
+ }
+ }}
style={{
transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
transition: this.props.transition,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e1ebb4eee..7c64fd429 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -141,7 +141,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else if (!e.ctrlKey && !e.metaKey) {
FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !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.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0));
e.stopPropagation();
}
}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 0cb5b94f4..8b48acf23 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -161,7 +161,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
} else if (startLink !== endLink) {
- const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true);
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
if (endLinkView) {
endLinkView._link = linkDoc;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0b2e55294..d7bd75cf4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,20 +1,22 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
+import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, returnVal, returnEmptyString } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
@@ -39,8 +41,6 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { RadialMenu } from './RadialMenu';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require("react");
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { RichTextField } from '../../../fields/RichTextField';
export type DocFocusFunc = () => boolean;
@@ -1045,9 +1045,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
- contents={this.ShowTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
- display={"block"} fontSize={10}
- GetValue={returnEmptyString}
+ contents={this.ShowTitle === "title" ? StrCast((this.dataDoc || this.props.Document).title) : this.ShowTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
+ display={"block"}
+ fontSize={10}
+ GetValue={() => Field.toString((this.dataDoc || this.props.Document)[this.ShowTitle.split(";")[0]] as any as Field)}
SetValue={undoBatch((value: string) => {
this.ShowTitle.includes("Date") ? true : (Doc.GetProto(this.dataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true;
})}
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index c1978e331..d5b50122b 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -48,7 +48,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
const keys = new Set<string>(noviceFields);
this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
- return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("acl")) || noviceFields.includes(key)).sort();
+ return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
}
gatherFieldValues(dashboard: Doc, facetKey: string) {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2517943d7..421aac69f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -33,6 +33,7 @@ import "../pdf/PDFViewer.scss";
import React = require("react");
import { Tooltip } from '@material-ui/core';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -457,7 +458,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
TraceMobx();
return <div className="webBox-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight) }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
- <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
+ <Annotation {...this.props} showInfo={emptyFunction} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
}
</div>;
}
@@ -499,6 +500,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
e.stopPropagation();
const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 125, 125);
+ FormattedTextBox.SelectOnLoad = targetDoc[Id];
const annotationDoc = this.highlight("rgba(173, 216, 230, 0.35)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
@@ -506,6 +508,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) {
e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
annotationDoc.isLinkButton = true;
+ annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document;
}
}
});
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index d1109b388..dbf98a5e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -106,6 +106,15 @@
border-width: 1px;
}
}
+.formattedTextBox-inner-rounded-selected,
+.formattedTextBox-inner-selected {
+ .ProseMirror {
+ padding:10px;
+ }
+ .ProseMirror:hover {
+ background: unset;
+ }
+}
// .menuicon {
// display: inline-block;
@@ -332,15 +341,6 @@ footnote::after {
.multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; vertical-align: top; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
}
-.formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner-selected {
- .ProseMirror {
- padding:10px;
- }
- .ProseMirror:hover {
- background: unset;
- }
-}
@media only screen and (max-width: 1000px) {
@import "../../globalCssVariables";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e7b4b72f4..1148087c6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -557,7 +557,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
sidebarDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction,
- () => this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%")) !== "0%");
+ () => setTimeout(action(() => {
+ const prevWidth = this.sidebarWidth();
+ this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+ this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+ })), false);
}
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
const bounds = this.CurrentDiv.getBoundingClientRect();
@@ -821,8 +825,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
() => this.props.makeLink?.(),
(linkDoc: Opt<Doc>) => {
if (linkDoc) {
- const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
- const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
+ const a1 = Cast(linkDoc.anchor1, Doc, null);
+ const a2 = Cast(linkDoc.anchor2, Doc, null);
+ const otherAnchor = Doc.AreProtosEqual(a1, this.rootDoc) ? a2 : a1;
+ const anchor2Title = StrCast(otherAnchor.title, "-untitled-");
+ const anchor2Id = otherAnchor?.[Id] || "";
this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
}
},
@@ -906,15 +913,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
async (scrollToLinkID) => {
const findLinkFrag = (frag: Fragment, editor: EditorView) => {
const nodes: Node[] = [];
- let offset = 0;
frag.forEach((node, index) => {
const examinedNode = findLinkNode(node, editor);
if (examinedNode?.textContent) {
nodes.push(examinedNode);
- offset = index;
+ !start && (start = index);
}
});
- return { frag: Fragment.fromArray(nodes), start: start + offset };
+ return { frag: Fragment.fromArray(nodes), start };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
@@ -926,7 +932,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined;
};
- const start = 0;
+ let start = 0;
if (this._editorView && scrollToLinkID) {
const editor = this._editorView;
const ret = findLinkFrag(editor.state.doc.content, editor);
@@ -1518,16 +1524,37 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
- return1000 = () => 1000;
+ @computed get audioHandle() {
+ return !this.layoutDoc._showAudio ? (null) :
+ <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
+ <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
+ </div>;
+ }
+
+ @computed get sidebarHandle() {
+ const annotated = DocListCast(this.dataDoc[this.annotationKey]).length;
+ return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) :
+ <div className="formattedTextBox-sidebar-handle"
+ style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : undefined }}
+ onPointerDown={this.sidebarDown}
+ onClick={e => {
+ console.log(e);
+ }}
+ />;
+ }
+
@computed get sidebarCollection() {
+ const fitToBox = this.props.Document._fitToBox;
return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
<div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- PanelHeight={this.active() ? this.return1000 : this.props.PanelHeight}
+ PanelHeight={this.props.PanelHeight}
PanelWidth={this.sidebarWidth}
+ xMargin={0}
scaleField={this.annotationKey + "-scale"}
annotationsKey={this.annotationKey}
isAnnotationOverlay={true}
+ fitToBox={fitToBox}
focus={this.props.focus}
isSelected={this.props.isSelected}
select={emptyFunction}
@@ -1558,8 +1585,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected
if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); }
const minimal = this.props.ignoreAutoHeight;
- const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? -10 : 0;
- const selclass = selected && !this.layoutDoc._singleLine ? "-selected" : "";
+ const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
+ const selPad = Math.min(margins, 10);
+ const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
+ const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
return (
<div className={"formattedTextBox-cont"} ref={this._boxRef}
style={{
@@ -1607,18 +1636,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
<div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
style={{
overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
- padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) :
- `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
+ padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
pointerEvents: !active ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
}}
/>
</div>
{this.sidebarCollection}
- {selected ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
- {!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
- <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
- </div>}
+ {this.sidebarHandle}
+ {this.audioHandle}
</div>
</div>
);
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index b7c7dae38..84b14cd61 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym, Field } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { Cast, FieldValue, BoolCast, NumCast, StrCast, PromiseValue } from "../../../fields/Types";
@@ -17,7 +17,7 @@ interface IAnnotationProps {
focus: (doc: Doc) => void;
dataDoc: Doc;
fieldKey: string;
- showInfo: (anno: Doc) => void;
+ showInfo: (anno: Opt<Doc>) => void;
}
@observer
@@ -25,7 +25,7 @@ export
class Annotation extends React.Component<IAnnotationProps> {
render() {
return DocListCast(this.props.anno.annotations).map(a =>
- <RegionAnnotation {...this.props} pinToPres={this.props.pinToPres} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />);
+ <RegionAnnotation {...this.props} showInfo={this.props.showInfo} pinToPres={this.props.pinToPres} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />);
}
}
@@ -40,7 +40,7 @@ interface IRegionAnnotationProps {
document: Doc;
dataDoc: Doc;
fieldKey: string;
- showInfo: (anno: Doc) => void;
+ showInfo: (anno: Opt<Doc>) => void;
}
@observer
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 8a38c439d..0ecb4dba4 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -59,6 +59,7 @@
left: 0px;
display: inline-block;
width:100%;
+ pointer-events: all;
}
.pdfViewerDash-overlay {
pointer-events: none;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8da3d2026..77dd40f2a 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -35,6 +35,7 @@ import { LinkDocPreview } from "../nodes/LinkDocPreview";
import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { SharingManager } from "../../util/SharingManager";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
const pdfjsLib = require("pdfjs-dist");
const _global = (window /* browser */ || global /* node */) as any;
@@ -624,6 +625,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
clipDoc._height = this.marqueeHeight();
clipDoc._scrollTop = this.marqueeY();
const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 100, 100);
+ FormattedTextBox.SelectOnLoad = targetDoc[Id];
Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
clipDoc.rootDocument = targetDoc;
// DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined);
@@ -640,6 +642,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
}
annotationDoc.isLinkButton = true; // prevents link button fro showing up --- maybe not a good thing?
+ annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document;
e.annoDragData && e.annoDragData.linkDocument && e.annoDragData?.linkDropCallback?.({ linkDocument: e.annoDragData.linkDocument });
}
});
@@ -701,15 +704,15 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
<div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
{this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
</div>
- </div>
+ </div>;
}
- showInfo = action((anno: Doc) => this._overlayAnnoInfo = anno);
+ showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
panelWidth = () => (this.Document.scrollHeight || this.Document._nativeHeight || 0);
panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0);
@computed get overlayLayer() {
- return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay"
+ return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath}
@@ -731,7 +734,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
addDocument={this.addDocument}
- docFilters={this.props.docRangeFilters}
+ docFilters={this.props.docFilters}
docRangeFilters={this.props.docRangeFilters}
CollectionView={undefined}
ScreenToLocalTransform={this.overlayTransform}