aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx19
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx8
-rw-r--r--src/client/views/nodes/DocumentView.scss8
-rw-r--r--src/client/views/nodes/DocumentView.tsx241
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/LinkBox.tsx139
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx84
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss9
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx32
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx199
13 files changed, 509 insertions, 238 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 4a438f826..085f9f023 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -12,7 +12,6 @@ import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { DocComponent } from '../DocComponent';
-import { InkingStroke } from '../InkingStroke';
import { StyleProp } from '../StyleProvider';
import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
@@ -183,6 +182,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
};
+ dragEnding = () => this.props.CollectionFreeFormView?.dragEnding();
+ dragStarting = () => this.props.CollectionFreeFormView?.dragStarting(false, true);
+
nudge = (x: number, y: number) => {
this.props.Document.x = NumCast(this.props.Document.x) + x;
this.props.Document.y = NumCast(this.props.Document.y) + y;
@@ -191,6 +193,17 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.();
screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y);
returnThis = () => this;
+
+ /// this indicates whether the doc view is activated because of its relationshop to a group
+ // 'group' - this is a group that is activated because it's on an active canvas, but is not part of some other group
+ // 'child' - this is a group child that is activated because its containing group is activated
+ // 'inactive' - this is a group child but it is not active
+ // undefined - this is not activated by a group
+ isGroupActive = () => {
+ if (this.props.CollectionFreeFormView.isAnyChildContentActive()) return undefined;
+ const isGroup = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
+ return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined;
+ };
render() {
TraceMobx();
const divProps: DocumentViewProps = {
@@ -200,8 +213,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
ScreenToLocalTransform: this.screenToLocalTransform,
PanelWidth: this.panelWidth,
PanelHeight: this.panelHeight,
+ isGroupActive: this.isGroupActive,
};
- const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString && !this.layoutDoc._stroke_isInkMask;
return (
<div
className="collectionFreeFormDocumentView-container"
@@ -213,7 +226,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
zIndex: this.ZInd,
display: this.sizeProvider?.width ? undefined : 'none',
- pointerEvents: isInk ? 'none' : undefined,
+ pointerEvents: 'none',
}}>
{this.props.renderCutoffProvider(this.props.Document) ? (
<div style={{ position: 'absolute', width: this.panelWidth(), height: this.panelHeight(), background: 'lightGreen' }} />
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 4db0bf5fa..50a7f5d7b 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -2,21 +2,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc } from '../../../fields/Doc';
import { StrCast } from '../../../fields/Types';
import { emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
-import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
import './DocumentLinksButton.scss';
import { DocumentView } from './DocumentView';
import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';
+import { PinProps } from './trails';
import React = require('react');
import _ = require('lodash');
-import { PinProps } from './trails';
interface DocumentLinksButtonProps {
View: DocumentView;
@@ -27,6 +27,7 @@ interface DocumentLinksButtonProps {
StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
ShowCount?: boolean;
scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
+ hideCount?: () => boolean;
}
@observer
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -239,6 +240,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
render() {
+ if (this.props.hideCount?.()) return null;
const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
const buttonTitle = 'Tap to view links; double tap to open link collection';
const title = this.props.ShowCount ? buttonTitle : menuTitle;
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index b25540dd3..931594568 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -24,6 +24,8 @@
width: 100%;
height: 100%;
border-radius: inherit;
+ // bcz: can't clip the title this way because groups need to be able to render outside of overflow region to support drag/drop-extending the group borders
+ // overflow: hidden; // need this so that title will be clipped when borderRadius is set
// transition: outline 0.3s linear;
// background: $white; //overflow: hidden;
@@ -118,7 +120,7 @@
display: flex;
justify-content: center;
align-items: center;
-
+
.sharingIndicator {
height: 30px;
width: 30px;
@@ -165,6 +167,7 @@
height: 100%;
border-radius: inherit;
white-space: normal;
+ overflow: hidden; // so that titles will clip when borderRadius is set
.documentView-styleContentWrapper {
width: 100%;
@@ -185,6 +188,7 @@
text-overflow: ellipsis;
white-space: pre;
position: absolute;
+ display: flex; // this allows title field dropdown to be inline with editable title
}
.documentView-titleWrapper-hover {
@@ -214,7 +218,7 @@
.documentView-node:hover {
> .documentView-styleWrapper {
> .documentView-titleWrapper-hover {
- display: inline-block;
+ display: flex;
}
// > .documentView-contentsView {
// opacity: 0.5;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index da665a502..98b13f90f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,5 +1,6 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { Dropdown, DropdownType, Type } from 'browndash-components';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -17,7 +18,7 @@ import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from '../../DocServer';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocOptions, Docs, DocUtils, FInfo } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
import { DictationManager } from '../../util/DictationManager';
@@ -38,7 +39,6 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
import { GestureOverlay } from '../GestureOverlay';
-import { InkingStroke } from '../InkingStroke';
import { LightboxView } from '../LightboxView';
import { StyleProp } from '../StyleProvider';
import { UndoStack } from '../UndoStack';
@@ -48,11 +48,11 @@ import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { PresEffect, PresEffectDirection } from './trails';
import { PinProps, PresBox } from './trails/PresBox';
import React = require('react');
-import { KeyValueBox } from './KeyValueBox';
const { Howl } = require('howler');
interface Window {
@@ -119,8 +119,7 @@ export interface DocComponentView {
getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
- reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
- shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views
+ ignoreNativeDimScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
select?: (ctrlKey: boolean, shiftKey: boolean) => void;
focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>;
menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
@@ -137,11 +136,12 @@ export interface DocComponentView {
componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
incrementalRendering?: () => void;
layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
- overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document
+ overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document (e.g, KeyValueBox always allows pointer events)
fieldKey?: string;
annotationKey?: string;
getTitle?: () => string;
getCenter?: (xf: Transform) => { X: number; Y: number };
+ screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } };
ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
@@ -154,8 +154,8 @@ export interface DocumentViewSharedProps {
renderDepth: number;
Document: Doc;
DataDoc?: Doc;
- contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
+ isGroupActive?: () => string | undefined; // is this document part of a group that is active
suppressSetHeight?: boolean;
setContentView?: (view: DocComponentView) => any;
CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
@@ -219,7 +219,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
hideLinkAnchors?: boolean;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
- 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
+ contentPointerEvents?: 'none' | 'all' | undefined; // 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
radialMenu?: String[];
LayoutTemplateString?: string;
dontCenter?: 'x' | 'y' | 'xy';
@@ -272,8 +272,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return this._animateScaleTime ?? 100;
}
public get displayName() {
- return 'DocumentView(' + this.props.Document.title + ')';
+ return 'DocumentViewInternal(' + this.props.Document.title + ')';
} // this makes mobx trace() statements more descriptive
+
public get ContentDiv() {
return this._mainCont.current;
}
@@ -317,8 +318,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get titleHeight() {
return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
}
+ @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined;
@computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
- return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents);
+ TraceMobx();
+ return this._pointerEvents;
}
@computed get finalLayoutKey() {
return StrCast(this.Document.layout_fieldKey, 'layout');
@@ -330,6 +333,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
return this.props.NativeHeight();
}
@computed get disableClickScriptFunc() {
+ TraceMobx();
const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
// prettier-ignore
return (
@@ -356,6 +360,25 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
componentDidMount() {
this.setupHandlers();
+ this._disposers.contentActive = reaction(
+ () => {
+ // true - if the document has been activated directly or indirectly (by having its children selected)
+ // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
+ // undefined - it is not active, but it should be responsive to actions that might activate it or its contents (eg clicking)
+ return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
+ ? false
+ : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
+ ? true
+ : undefined;
+ },
+ active => (this._isContentActive = active),
+ { fireImmediately: true }
+ );
+ this._disposers.pointerevents = reaction(
+ () => this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents),
+ pointerevents => (this._pointerEvents = pointerevents),
+ { fireImmediately: true }
+ );
}
preDropFunc = (e: Event, de: DragManager.DropEvent) => {
const dropAction = this.layoutDoc.dropAction as dropActionType;
@@ -425,7 +448,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse;
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
- if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
+ if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return;
+ if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) {
let stopPropagate = true;
let preventDefault = true;
!this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc);
@@ -496,7 +520,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// prettier-ignore
clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) :
this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ??
- this.props.select(e.ctrlKey || e.metaKey || e.shiftKey)));
+ this.props.select(e.ctrlKey||e.shiftKey, e.metaKey)));
const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
@@ -513,6 +537,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@action
onPointerDown = (e: React.PointerEvent): void => {
+ if (this.props.isGroupActive?.() === 'child' && !this.props.isDocumentActive?.()) return;
this._longPressSelector = setTimeout(() => {
if (DocumentView.LongPress) {
if (this.rootDoc.undoIgnoreFields) {
@@ -536,7 +561,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!this.props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
e.button === 0 &&
- this.pointerEvents !== 'none' &&
!Doc.IsInMyOverlay(this.layoutDoc)
) {
e.stopPropagation();
@@ -622,7 +646,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (this.props.Document === Doc.ActiveDashboard) {
e.stopPropagation();
e.preventDefault();
- 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.');
+ alert(
+ (e.target as any)?.closest?.('*.lm_content')
+ ? "You can't perform this move most likely because you didn't drag the document's title bar to enable embedding in a different document."
+ : 'Linking to document tabs not yet supported. Drop link on document content.'
+ );
return true;
}
const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
@@ -635,6 +663,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.embedContainer) {
const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
+ if (de.complete.linkDocument) {
+ de.complete.linkDocument.layout_isSvg = true;
+ this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.addDocument(de.complete.linkDocument);
+ }
}
e.stopPropagation();
return true;
@@ -874,32 +906,33 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
setHeight = (height: number) => (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
- @computed get _isContentActive() {
- // true - if the document has been activated directly or indirectly (by having its children selected)
- // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
- // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking)
- return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
- ? false
- : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
- ? true
- : undefined;
- }
+ @observable _isContentActive: boolean | undefined;
+
isContentActive = (): boolean | undefined => this._isContentActive;
childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
/// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
@computed get _contentPointerEvents() {
- return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents;
+ TraceMobx();
+ return this.props.contentPointerEvents ??
+ ((!this.disableClickScriptFunc && //
+ this.onClickHandler &&
+ !this.props.onBrowseClick?.() &&
+ this.isContentActive() !== true) ||
+ this.isContentActive() === false)
+ ? 'none'
+ : this.pointerEvents;
}
contentPointerEvents = () => this._contentPointerEvents;
@computed get contents() {
TraceMobx();
- const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
+ const isInk = this.layoutDoc._layout_isSvg && !this.props.LayoutTemplateString;
+ const noBackground = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent');
return (
<div
className="documentView-contentsView"
style={{
- pointerEvents: (isInk ? 'none' : this.contentPointerEvents()) ?? 'all',
+ pointerEvents: (isInk || noBackground ? 'none' : this.contentPointerEvents()) ?? 'all',
height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
}}>
<DocumentContentsView
@@ -1063,6 +1096,41 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
};
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
+ @observable _changingTitleField = false;
+ @observable _dropDownInnerWidth = 0;
+ fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
+ const filteredOptions = new Set(inputOptions);
+ const scaling = this.titleHeight / 30; /* height of Dropdown */
+ Object.entries(DocOptions)
+ .filter(opts => opts[1].filterable)
+ .forEach((pair: [string, FInfo]) => filteredOptions.add(pair[0]));
+ filteredOptions.add(StrCast(this.layoutDoc.layout_showTitle));
+ const options = Array.from(filteredOptions)
+ .filter(f => f)
+ .map(facet => ({ val: facet, text: facet }));
+ return (
+ <div style={{ width: dropdownWidth }}>
+ <div
+ ref={action((r: any) => r && (this._dropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
+ onPointerDown={action(e => (this._changingTitleField = true))}
+ style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${scaling})` }}>
+ <Dropdown
+ activeChanged={action(isOpen => !isOpen && (this._changingTitleField = false))}
+ selectedVal={placeholder}
+ setSelectedVal={onChange}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ closeOnSelect={true}
+ dropdownType={DropdownType.SELECT}
+ items={options}
+ width={100}
+ fillWidth
+ />
+ </div>
+ </div>
+ );
+ };
@computed get innards() {
TraceMobx();
const showTitle = this.layout_showTitle?.split(':')[0];
@@ -1090,8 +1158,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>
);
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
- const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor));
+ const background = StrCast(
+ this.layoutDoc.layout_headingColor,
+ StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(this.layoutDoc.layout_headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor)))
+ );
+ const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._dropDownInnerWidth * this.titleHeight) / 30) : 0;
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
+ // displays a 'title' at the top of a document. The title contents default to the 'title' field, but can be changed to one or more fields by
+ // setting layout_showTitle using the format: field1[;field2[...][:hover]]
+ // from the UI, this is done by clicking the title field and prefixin the format with '#'. eg., #field1[;field2;...][:hover]
const titleView = !showTitle ? null : (
<div
className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
@@ -1099,39 +1174,58 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
style={{
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
- width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button
+ width: 100 - sidebarWidthPercent + '%',
color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background),
background,
pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
}}>
- <EditableView
- ref={this._titleRef}
- contents={showTitle
- .split(';')
- .map(field => field.trim())
- .map(field => targetDoc[field]?.toString())
- .join('\\')}
- display={'block'}
- fontSize={10}
- GetValue={() => {
- return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
- }}
- SetValue={undoBatch((input: string) => {
- if (input?.startsWith('#')) {
- if (this.rootDoc.layout_showTitle) {
- this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
- } else if (!this.props.layout_showTitle) {
- Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'author_date';
+ {!dropdownWidth
+ ? null
+ : this.fieldsDropdown(
+ [],
+ dropdownWidth,
+ StrCast(this.layoutDoc.layout_showTitle).split(':')[0],
+ action((field: string | number) => {
+ if (this.rootDoc.layout_showTitle) {
+ this.rootDoc._layout_showTitle = field;
+ } else if (!this.props.layout_showTitle) {
+ Doc.UserDoc().layout_showTitle = field;
+ }
+ this._changingTitleField = false;
+ }),
+ action(() => (this._changingTitleField = false))
+ )}
+ <div
+ style={{
+ width: `calc(100% - ${dropdownWidth}px)`,
+ minWidth: '100px',
+ color: this._titleRef.current?._editing || this._changingTitleField ? 'black' : undefined,
+ background: this._titleRef.current?._editing || this._changingTitleField ? 'yellow' : undefined,
+ }}>
+ <EditableView
+ ref={this._titleRef}
+ contents={showTitle
+ .split(';')
+ .map(field => targetDoc[field.trim()]?.toString())
+ .join(' \\ ')}
+ display="block"
+ oneLine={true}
+ fontSize={(this.titleHeight / 15) * 10}
+ GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.rootDoc, showTitle.split(';')[0]))}
+ SetValue={undoBatch((input: string) => {
+ if (input?.startsWith('#')) {
+ if (this.rootDoc.layout_showTitle) {
+ this.rootDoc._layout_showTitle = input?.substring(1);
+ } else if (!this.props.layout_showTitle) {
+ Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date';
+ }
+ } else if (showTitle && !showTitle.includes('Date') && showTitle !== 'author') {
+ KeyValueBox.SetField(targetDoc, showTitle, input);
}
- } else {
- var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
- if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
- if (showTitle.includes('Date') || showTitle === 'author') return true;
- Doc.SetInPlace(targetDoc, showTitle, value, true);
- }
- return true;
- })}
- />
+ return true;
+ })}
+ />
+ </div>
</div>
);
return this.props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
@@ -1232,15 +1326,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
onClick={this.onClick}
- onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
- onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
+ onPointerEnter={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)}
+ onPointerOver={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)}
onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)}
style={{
borderRadius: this.borderRounding,
- pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents,
+ pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here)
}}>
<>
- {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
+ {this._componentView instanceof KeyValueBox ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[Animation], this.rootDoc)}
{borderPath?.jsx}
</>
</div>
@@ -1354,9 +1448,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get hideLinkButton() {
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : ''));
}
+ hideLinkCount = () => this.props.renderDepth === -1 || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
@computed get linkCountView() {
- const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
- return hideCount ? null : <DocumentLinksButton View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
+ return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
}
@computed get docViewPath(): DocumentView[] {
return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
@@ -1365,10 +1459,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
}
@computed get nativeWidth() {
- return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+ return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
}
@computed get nativeHeight() {
- return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+ return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
}
@computed get shouldNotScale() {
return this.props.shouldNotScale?.() || (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._type_collection as any);
@@ -1414,16 +1508,20 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
}
- public toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
+ public toggleNativeDimensions = () => this.docView && this.rootDoc.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight());
public getBounds = () => {
if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
+ if (this.docView._componentView?.screenBounds) {
+ return this.docView._componentView.screenBounds();
+ }
const xf = this.docView.props
.ScreenToLocalTransform()
.scale(this.trueNativeWidth() ? this.nativeScaling : 1)
.inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
+
if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
@@ -1482,7 +1580,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
docViewPathFunc = () => this.docViewPath;
isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
- select = (extendSelection: boolean) => SelectionManager.SelectView(this, extendSelection);
+ select = (extendSelection: boolean, focusSelection?: boolean) => {
+ SelectionManager.SelectView(this, extendSelection);
+ if (focusSelection) {
+ DocumentManager.Instance.showDocument(this.rootDoc, {
+ willZoomCentered: true,
+ zoomScale: 0.9,
+ zoomTime: 500,
+ });
+ }
+ };
NativeWidth = () => this.effectiveNativeWidth;
NativeHeight = () => this.effectiveNativeHeight;
PanelWidth = () => this.panelWidth;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 673f711be..95aeb5331 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -41,7 +41,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
componentDidMount() {
this.props.setContentView?.(this);
}
- reverseNativeScaling = returnTrue;
+ ignoreNativeDimScaling = returnTrue;
able = returnAlways;
layout_fitWidth = returnTrue;
overridePointerEvents = returnAll;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index efb949a47..e66fed84b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,6 +1,14 @@
import React = require('react');
+import { Bezier } from 'bezier-js';
+import { computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { emptyFunction, returnAlways, returnFalse, returnTrue } from '../../../Utils';
+import { Height, Width } from '../../../fields/DocSymbols';
+import { Id } from '../../../fields/FieldSymbols';
+import { DocCast, NumCast, StrCast } from '../../../fields/Types';
+import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
+import { DocumentManager } from '../../util/DocumentManager';
+import { Transform } from '../../util/Transform';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
import { ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { ComparisonBox } from './ComparisonBox';
@@ -14,11 +22,138 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
onClickScriptDisable = returnAlways;
+ @computed get anchor1() {
+ const anchor1 = DocCast(this.rootDoc.link_anchor_1);
+ const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
+ return DocumentManager.Instance.getDocumentView(anchor_1, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
+ }
+ @computed get anchor2() {
+ const anchor2 = DocCast(this.rootDoc.link_anchor_2);
+ const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2;
+ return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement());
+ }
+ screenBounds = () => {
+ if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse();
+ const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse();
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(this.anchor1.rootDoc[Width](), this.anchor1.rootDoc[Height]()) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(this.anchor2.rootDoc[Width](), this.anchor2.rootDoc[Height]()) };
+
+ const pts = [] as number[][];
+ pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]);
+ pts.push(Utils.getNearestPointInPerimeter(a_scrBds.tl[0], a_scrBds.tl[1], a_scrBds.br[0] - a_scrBds.tl[0], a_scrBds.br[1] - a_scrBds.tl[1], (b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2));
+ pts.push(Utils.getNearestPointInPerimeter(b_scrBds.tl[0], b_scrBds.tl[1], b_scrBds.br[0] - b_scrBds.tl[0], b_scrBds.br[1] - b_scrBds.tl[1], (a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2));
+ pts.push([(b_scrBds.tl[0] + b_scrBds.br[0]) / 2, (b_scrBds.tl[1] + b_scrBds.br[1]) / 2]);
+ const agg = aggregateBounds(
+ pts.map(pt => ({ x: pt[0], y: pt[1] })),
+ 0,
+ 0
+ );
+ return { left: agg.x, top: agg.y, right: agg.r, bottom: agg.b, center: undefined };
+ }
+ return { left: 0, top: 0, right: 0, bottom: 0, center: undefined };
+ };
+ disposer: IReactionDisposer | undefined;
componentDidMount() {
this.props.setContentView?.(this);
+ this.disposer = reaction(
+ () => {
+ if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+ const a = (this.anchor1 ?? this.anchor2)!;
+ const b = (this.anchor2 ?? this.anchor1)!;
+
+ const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
+ const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform();
+ const a_invXf = a.props.ScreenToLocalTransform().inverse();
+ const b_invXf = b.props.ScreenToLocalTransform().inverse();
+ const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) };
+ const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) };
+ const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
+ const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
+
+ const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2];
+ const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2);
+ const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2);
+ const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2];
+
+ const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]);
+ const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])];
+ return { pts, lx, rx, ty, by };
+ }
+ return undefined;
+ },
+ params => {
+ this.renderProps = params;
+ if (params) {
+ if (
+ Math.abs(params.lx - NumCast(this.layoutDoc.x)) > 1e-5 ||
+ Math.abs(params.ty - NumCast(this.layoutDoc.y)) > 1e-5 ||
+ Math.abs(params.rx - params.lx - NumCast(this.layoutDoc._width)) > 1e-5 ||
+ Math.abs(params.by - params.ty - NumCast(this.layoutDoc._height)) > 1e-5
+ ) {
+ this.layoutDoc.x = params?.lx;
+ this.layoutDoc.y = params?.ty;
+ this.layoutDoc._width = params.rx - params?.lx;
+ this.layoutDoc._height = params?.by - params?.ty;
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
}
+ componentWillUnmount(): void {
+ this.disposer?.();
+ }
+ @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined;
render() {
- if (this.dataDoc.treeView_Open === undefined) setTimeout(() => (this.dataDoc.treeView_Open = true));
+ if (this.renderProps) {
+ const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
+ const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
+
+ const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] })));
+ const text = bez.get(0.5);
+ const linkDesc = StrCast(this.rootDoc.link_description) || 'description';
+ const strokeWidth = NumCast(this.rootDoc.stroke_width, 4);
+ const dash = StrCast(this.rootDoc.stroke_dash);
+ const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined;
+ const { pts, lx, ty, rx, by } = this.renderProps;
+ return (
+ <div style={{ transition: 'inherit', pointerEvents: 'none', position: 'absolute', width: '100%', height: '100%' }}>
+ <svg width={Math.max(100, rx - lx)} height={Math.max(100, by - ty)} style={{ transition: 'inherit', overflow: 'visible' }}>
+ <defs>
+ <filter x="0" y="0" width="1" height="1" id={`${this.rootDoc[Id] + 'background'}`}>
+ <feFlood floodColor={`${StrCast(this.rootDoc._backgroundColor, 'lightblue')}`} result="bg" />
+ <feMerge>
+ <feMergeNode in="bg" />
+ <feMergeNode in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ </defs>
+ <path
+ className="collectionfreeformlinkview-linkLine"
+ style={{
+ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', //
+ stroke: highlightColor ?? 'lightblue',
+ strokeDasharray,
+ strokeWidth,
+ transition: 'inherit',
+ }}
+ d={`M ${pts[1][0] - lx} ${pts[1][1] - ty} C ${pts[1][0] + pts[1][0] - pts[0][0] - lx} ${pts[1][1] + pts[1][1] - pts[0][1] - ty},
+ ${pts[2][0] + pts[2][0] - pts[3][0] - lx} ${pts[2][1] + pts[2][1] - pts[3][1] - ty}, ${pts[2][0] - lx} ${pts[2][1] - ty}`}
+ />
+ <text
+ filter={`url(#${this.rootDoc[Id] + 'background'})`}
+ style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }}
+ x={text.x - lx}
+ y={text.y - ty}>
+ <tspan>&nbsp;</tspan>
+ <tspan dy="2">{linkDesc.substring(0, 50) + (linkDesc.length > 50 ? '...' : '')}</tspan>
+ <tspan dy="2">&nbsp;</tspan>
+ </text>
+ </svg>
+ </div>
+ );
+ }
return (
<div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}>
<ComparisonBox
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 537da5055..c068d9dd7 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -212,8 +212,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
}
- brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._pdfViewer?.brushView(view, transTime);
-
sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) {
this.toggleSidebar(false);
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 481e43feb..116069cbd 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -123,7 +123,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
Doc.AddToMyOverlay(value);
DocumentManager.Instance.AddViewRenderedCb(value, docView => {
Doc.UserDoc().currentRecording = docView.rootDoc;
- SelectionManager.SelectSchemaViewDoc(value);
+ docView.select(false);
RecordingBox.resumeWorkspaceReplaying(value);
});
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 58a765d61..2aca314da 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -10,13 +10,12 @@ import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { RefField } from '../../../fields/RefField';
import { listSpec } from '../../../fields/Schema';
-import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types';
+import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
import { ImageField, WebField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager } from '../../util/DragManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -51,7 +50,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
public static sidebarResizerWidth = 5;
static webStyleSheet = addStyleSheet();
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [name: string]: IReactionDisposer } = {};
@@ -275,8 +273,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this._savedAnnotations;
};
- setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func);
- brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime);
focus = (anchor: Doc, options: DocFocusOptions) => {
if (anchor !== this.rootDoc && this._outerRef.current) {
const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
@@ -864,7 +860,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
key="sidebar"
title="Toggle Sidebar"
style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
}}
@@ -953,43 +948,48 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@computed get SidebarShown() {
return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
}
+ renderAnnotations = (childFilters: () => string[]) => (
+ <CollectionFreeFormView
+ {...this.props}
+ setContentView={this.setInnerContent}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ originTopLeft={false}
+ isAnnotationOverlayScrollable={true}
+ renderDepth={this.props.renderDepth + 1}
+ isAnnotationOverlay={true}
+ fieldKey={this.annotationKey}
+ setPreviewCursor={this.setPreviewCursor}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ ScreenToLocalTransform={this.scrollXf}
+ NativeDimScaling={returnOne}
+ focus={this.focus}
+ childFilters={childFilters}
+ select={emptyFunction}
+ isAnyChildContentActive={returnFalse}
+ bringToFront={emptyFunction}
+ styleProvider={this.childStyleProvider}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocumentWrapper}
+ childPointerEvents={this.childPointerEvents}
+ pointerEvents={this.annotationPointerEvents}
+ />
+ );
+ @computed get renderOpaqueAnnotations() {
+ return this.renderAnnotations(this.opaqueFilter);
+ }
+ @computed get renderTransparentAnnotations() {
+ return this.renderAnnotations(this.transparentFilter);
+ }
childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
@computed get webpage() {
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
- const renderAnnotations = (childFilters: () => string[]) => (
- <CollectionFreeFormView
- {...this.props}
- setContentView={this.setInnerContent}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- originTopLeft={false}
- isAnnotationOverlayScrollable={true}
- renderDepth={this.props.renderDepth + 1}
- isAnnotationOverlay={true}
- fieldKey={this.annotationKey}
- setPreviewCursor={this.setPreviewCursor}
- setBrushViewer={this.setBrushViewer}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- ScreenToLocalTransform={this.scrollXf}
- NativeDimScaling={returnOne}
- focus={this.focus}
- childFilters={childFilters}
- select={emptyFunction}
- isAnyChildContentActive={returnFalse}
- bringToFront={emptyFunction}
- styleProvider={this.childStyleProvider}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocumentWrapper}
- childPointerEvents={this.childPointerEvents}
- pointerEvents={this.annotationPointerEvents}
- />
- );
return (
<div
className="webBox-outerContent"
@@ -1004,8 +1004,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
onPointerDown={this.onMarqueeDown}>
<div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight) || '100%', pointerEvents }}>
{this.content}
- {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>}
- {renderAnnotations(this.opaqueFilter)}
+ <div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
+ {this.renderOpaqueAnnotations}
{this.annotationLayer}
</div>
</div>
@@ -1054,7 +1054,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.IsOpaqueFilter()])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (this.inlineTextAnnotations.includes(doc)) return 'none';
@@ -1135,8 +1135,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
removeDocument={this.removeDocument}
/>
</div>
- {this.sidebarHandle}
- {!this.props.isContentActive() ? null : this.searchUI}
+ {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
+ {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
</div>
);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 348bdd79e..818c0cbe7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -5,6 +5,14 @@
height: 100%;
min-height: 100%;
}
+.formattedTextBox-inner.centered,
+.formattedTextBox-inner-rounded.centered {
+ align-items: center;
+ display: flex;
+ .ProseMirror {
+ min-height: unset;
+ }
+}
.ProseMirror:focus {
outline: none !important;
@@ -29,7 +37,6 @@ audiotag:hover {
background: inherit;
padding: 0;
border-width: 0px;
- border-radius: inherit;
border-color: $medium-gray;
box-sizing: border-box;
background-color: inherit;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fcdfbdf2a..41b1c59b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2149,7 +2149,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onScroll={this.onScroll}
onDrop={this.ondrop}>
<div
- className={`formattedTextBox-inner${rounded}`}
+ className={`formattedTextBox-inner${rounded} ${this.layoutDoc.layout_centered ? 'centered' : ''}`}
ref={this.createDropTarget}
style={{
padding: StrCast(this.layoutDoc._textBoxPadding),
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 1ec6d6e3f..2b0927148 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -9,7 +9,9 @@ import { NumCast } from '../../../../fields/Types';
import { Utils } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
import { Networking } from '../../../Network';
+import { DocumentManager } from '../../../util/DocumentManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
import { OpenWhereMod } from '../DocumentView';
import { ImageBox } from '../ImageBox';
import './GenerativeFill.scss';
@@ -303,19 +305,21 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
originalImg.current = currImg.current;
originalDoc.current = parentDoc.current;
const { urls } = res as APISuccess;
- const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height)));
- const imgRes = await Promise.all(
- imgUrls.map(async url => {
- const saveRes = await onSave(url);
- return [url, saveRes as Doc];
- })
- );
- setEdits(imgRes);
- const image = new Image();
- image.src = imgUrls[0];
- ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height);
- currImg.current = image;
- parentDoc.current = imgRes[0][1] as Doc;
+ if (res.status !== 'error') {
+ const imgUrls = await Promise.all(urls.map(url => ImageUtility.convertImgToCanvasUrl(url, canvasDims.width, canvasDims.height)));
+ const imgRes = await Promise.all(
+ imgUrls.map(async url => {
+ const saveRes = await onSave(url);
+ return [url, saveRes as Doc];
+ })
+ );
+ setEdits(imgRes);
+ const image = new Image();
+ image.src = imgUrls[0];
+ ImageUtility.drawImgToCanvas(image, canvasRef, canvasDims.width, canvasDims.height);
+ currImg.current = image;
+ parentDoc.current = imgRes[0][1] as Doc;
+ }
} catch (err) {
console.log(err);
}
@@ -418,7 +422,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
ImageBox.setImageEditorOpen(false);
ImageBox.setImageEditorSource('');
if (newCollectionRef.current) {
- newCollectionRef.current.fitContentOnce = true;
+ DocumentManager.Instance.AddViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce());
}
setEdits([]);
};
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 383b400c8..3d7c68bcd 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Animation } from '../../../../fields/DocSymbols';
import { Copy, Id } from '../../../../fields/FieldSymbols';
import { InkField } from '../../../../fields/InkField';
@@ -518,13 +518,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
changed = true;
}
}
+ if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.timecodeToShow !== undefined)) {
+ if (bestTarget._layout_currentTimecode !== activeItem.timecodeToShow) {
+ bestTarget._layout_currentTimecode = activeItem.timecodeToShow;
+ changed = true;
+ }
+ }
if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) {
if (bestTarget.fillColor !== activeItem.config_fillColor) {
- Doc.GetProto(bestTarget).fillColor = activeItem.config_fillColor;
+ Doc.GetProto(bestTarget).fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor));
changed = true;
}
if (bestTarget.color !== activeItem.config_color) {
- Doc.GetProto(bestTarget).color = activeItem.config_color;
+ Doc.GetProto(bestTarget).color = StrCast(activeItem.config_color, StrCast(bestTarget.color));
changed = true;
}
if (bestTarget.width !== activeItem.width) {
@@ -565,11 +571,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (bestTarget._layout_scrollTop !== activeItem.config_scrollTop) {
bestTarget._layout_scrollTop = activeItem.config_scrollTop;
changed = true;
- const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
- if (contentBounds) {
- const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView;
- dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }, transTime);
- }
}
}
if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.config_annotations !== undefined)) {
@@ -830,7 +831,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.childDocs.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = PresBox.targetRenderedDoc(curDoc);
- const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
+ const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, curDoc);
let opacity: Opt<number> = index === this.itemIndex ? 1 : undefined;
if (curDoc.presentation_hide) {
if (index !== this.itemIndex) {
@@ -864,7 +865,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
opacity = 0;
}
}
- opacity !== undefined && (tagDoc.opacity = opacity);
+ opacity !== undefined && (tagDoc.opacity = opacity === 1 ? undefined : opacity);
});
};
@@ -1301,71 +1302,55 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1);
// Adds the index in the pres path graphically
- @computed get order() {
+ orderedPathLabels = (collection: Doc) => {
const order: JSX.Element[] = [];
- const docs: Doc[] = [];
- const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement();
+ const docs = new Set<Doc>();
+ const presCollection = collection;
const dv = DocumentManager.Instance.getDocumentView(presCollection);
- this.childDocs
- .filter(doc => Cast(doc.presentation_targetDoc, Doc, null))
- .forEach((doc, index) => {
- const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
- const srcContext = Cast(tagDoc.embedContainer, Doc, null);
+ this.childDocs.forEach((doc, index) => {
+ const tagDoc = PresBox.targetRenderedDoc(doc);
+ const srcContext = Cast(tagDoc.embedContainer, Doc, null);
+ const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
+ <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
+ <div className="pathOrder-frame">{index + 1}</div>
+ </div>
+ );
+ if (presCollection === srcContext) {
+ const gap = 2;
const width = NumCast(tagDoc._width) / 10;
const height = Math.max(NumCast(tagDoc._height) / 10, 15);
const edge = Math.max(width, height);
const fontSize = edge * 0.8;
- const gap = 2;
- if (presCollection === srcContext) {
- // Case A: Document is contained within the collection
- if (docs.includes(tagDoc)) {
- const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
- docs.push(tagDoc);
- order.push(
- <div
- className="pathOrder"
- key={tagDoc.id + 'pres' + index}
- style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - edge / 2), left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
- onClick={() => this.selectElement(doc)}>
- <div className="pathOrder-frame">{index + 1}</div>
- </div>
- );
- } else {
- docs.push(tagDoc);
- order.push(
- <div
- className="pathOrder"
- key={tagDoc.id + 'pres' + index}
- style={{ top: NumCast(tagDoc.y) - edge / 2, left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
- onClick={() => this.selectElement(doc)}>
- <div className="pathOrder-frame">{index + 1}</div>
- </div>
- );
- }
- } else if (doc.config_pinView && presCollection === tagDoc && dv) {
- // Case B: Document is presPinView and is presCollection
- const scale: number = 1 / NumCast(doc.config_viewScale);
- const height: number = dv.props.PanelHeight() * scale;
- const width: number = dv.props.PanelWidth() * scale;
- const indWidth = width / 10;
- const indHeight = Math.max(height / 10, 15);
- const indEdge = Math.max(indWidth, indHeight);
- const indFontSize = indEdge * 0.8;
- const xLoc: number = NumCast(doc.config_panX) - width / 2;
- const yLoc: number = NumCast(doc.config_panY) - height / 2;
- docs.push(tagDoc);
- order.push(
- <>
- <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top: yLoc - indEdge / 2, left: xLoc - indEdge / 2, width: indEdge, height: indEdge, fontSize: indFontSize }} onClick={() => this.selectElement(doc)}>
- <div className="pathOrder-frame">{index + 1}</div>
- </div>
- <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
- </>
- );
+ // Case A: Document is contained within the collection
+ if (docs.has(tagDoc)) {
+ const prevOccurences = this.getAllIndexes(Array.from(docs), tagDoc).length;
+ order.push(labelCreator(NumCast(tagDoc.y) + (prevOccurences * (edge + gap) - edge / 2), NumCast(tagDoc.x) - edge / 2, edge, fontSize));
+ } else {
+ order.push(labelCreator(NumCast(tagDoc.y) - edge / 2, NumCast(tagDoc.x) - edge / 2, edge, fontSize));
}
- });
+ } else if (doc.config_pinView && presCollection === tagDoc && dv) {
+ // Case B: Document is presPinView and is presCollection
+ const scale = 1 / NumCast(doc.config_viewScale);
+ const viewBounds = NumListCast(doc.config_viewBounds, [0, 0, dv.props.PanelWidth(), dv.props.PanelHeight()]);
+ const height = (viewBounds[3] - viewBounds[1]) * scale;
+ const width = (viewBounds[2] - viewBounds[0]) * scale;
+ const indWidth = width / 10;
+ const indHeight = Math.max(height / 10, 15);
+ const indEdge = Math.max(indWidth, indHeight);
+ const indFontSize = indEdge * 0.8;
+ const left = NumCast(doc.config_panX) - width / 2;
+ const top = NumCast(doc.config_panY) - height / 2;
+ order.push(
+ <>
+ {labelCreator(top - indEdge / 2, left - indEdge / 2, indEdge, indFontSize)}
+ <div className="pathOrder-presPinView" style={{ top, left, width, height, borderWidth: indEdge / 10 }}></div>
+ </>
+ );
+ }
+ docs.add(tagDoc);
+ });
return order;
- }
+ };
/**
* Method called for viewing paths which adds a single line with
@@ -1375,41 +1360,57 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
* (Design needed for when documents in presentation trail are in another
* collection)
*/
- @computed get paths() {
+ pathLines = (collection: Doc) => {
let pathPoints = '';
- this.childDocs.forEach((doc, index) => {
- const tagDoc = PresBox.targetRenderedDoc(doc);
- if (tagDoc) {
- const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
- const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
- if ((index = 0)) pathPoints = n1x + ',' + n1y;
- else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
- } else if (doc.config_pinView) {
- const n1x = NumCast(doc.config_panX);
- const n1y = NumCast(doc.config_panY);
- if ((index = 0)) pathPoints = n1x + ',' + n1y;
- else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
- }
- });
+ this.childDocs
+ .filter(doc => PresBox.targetRenderedDoc(doc)?.embedContainer === collection)
+ .forEach((doc, index) => {
+ const tagDoc = PresBox.targetRenderedDoc(doc);
+ if (tagDoc) {
+ const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
+ const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
+ if ((index = 0)) pathPoints = n1x + ',' + n1y;
+ else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
+ } else if (doc.config_pinView) {
+ const n1x = NumCast(doc.config_panX);
+ const n1y = NumCast(doc.config_panY);
+ if ((index = 0)) pathPoints = n1x + ',' + n1y;
+ else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
+ }
+ });
return (
- <polyline
- points={pathPoints}
- style={{
- opacity: 1,
- stroke: '#69a6db',
- strokeWidth: 5,
- strokeDasharray: '10 5',
- boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
- }}
- fill="none"
- markerStart="url(#markerArrow)"
- markerMid="url(#markerSquare)"
- markerEnd="url(#markerSquareFilled)"
- />
+ <>
+ <div className="presPathLabels">{PresBox.Instance?.orderedPathLabels(collection)}</div>
+ <svg key="svg" className="presPaths">
+ <defs>
+ <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
+ </marker>
+ <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4" orient="auto" overflow="visible">
+ <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ </defs>
+ <polyline
+ points={pathPoints}
+ style={{
+ opacity: 1,
+ stroke: '#69a6db',
+ strokeWidth: 5,
+ strokeDasharray: '10 5',
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
+ }}
+ fill="none"
+ markerStart="url(#markerArrow)"
+ markerMid="url(#markerSquare)"
+ markerEnd="url(#markerSquareFilled)"
+ />
+ </svg>
+ </>
);
- }
- getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection
-
+ };
// Converts seconds to ms and updates presentation_transition
public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => {
let timeInMS = Number(number) * 1000;