aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx113
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx230
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx76
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss48
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx669
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx163
13 files changed, 853 insertions, 490 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
index 00505dbe3..99ee5ef4e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
import { Doc } from '../../../../fields/Doc';
import { NumCast } from '../../../../fields/Types';
import './CollectionFreeFormView.scss';
-import React = require('react');
+import * as React from 'react';
export interface CollectionFreeFormViewBackgroundGridProps {
panX: () => number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
new file mode 100644
index 000000000..d9f7ca6ea
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -0,0 +1,113 @@
+import { IconButton, Size, Type } from 'browndash-components';
+import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import './CollectionFreeFormView.scss';
+// import assets from './assets/link.png';
+
+/**
+ * An Fsa Arc. The first array element is a test condition function that will be observed.
+ * The second array element is a function that will be invoked when the first test function
+ * returns a truthy value
+ */
+export type infoArc = [() => any, (res?: any) => infoState];
+
+export const StateMessage = Symbol('StateMessage');
+export const StateEntryFunc = Symbol('StateEntryFunc');
+export const StateMessageGIF = Symbol('StateMessageGIF');
+export class infoState {
+ [StateMessage]: string = '';
+ [StateEntryFunc]?: () => any;
+ [StateMessageGIF]?: string = '';
+ [key: string]: infoArc;
+ constructor(message: string, arcs: { [key: string]: infoArc }, entryFunc?: () => any, messageGif?: string) {
+ this[StateMessage] = message;
+ Object.assign(this, arcs);
+ this[StateEntryFunc] = entryFunc;
+ this[StateMessageGIF] = messageGif;
+ }
+}
+
+/**
+ * Create an FSA state.
+ * @param msg the message displayed when in this state
+ * @param arcs an object with fields containing @infoArcs (an object with field names indicating the arc transition and
+ * field values being a tuple of an arc transition trigger function (that returns a truthy value when the arc should fire),
+ * and an arc transition action function (that sets the next state)
+ * @param entryFunc a function to call when entering the state
+ * @returns an FSA state
+ */
+export function InfoState(
+ msg: string, //
+ arcs: { [key: string]: infoArc },
+ entryFunc?: () => any,
+ gif?: string
+) {
+ return new infoState(msg, arcs, entryFunc, gif);
+}
+
+export interface CollectionFreeFormInfoStateProps {
+ infoState: infoState;
+ next: (state: infoState) => any;
+}
+
+@observer
+export class CollectionFreeFormInfoState extends ObservableReactComponent<CollectionFreeFormInfoStateProps> {
+ _disposers: IReactionDisposer[] = [];
+ @observable _hide = false;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ get State() {
+ return this._props.infoState;
+ }
+ get Arcs() {
+ return Object.keys(this.State ?? []).map(key => this.State?.[key]);
+ }
+
+ clearState = () => this._disposers.map(disposer => disposer());
+ initState = () =>
+ (this._disposers = this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map(arc => {
+ return reaction(
+ //
+ arc.test,
+ res => {
+ if (res) {
+ const next = arc.act(res);
+ this._props.next(next);
+ }
+ },
+ { fireImmediately: true }
+ );
+ }));
+
+ componentDidMount(): void {
+ this.initState();
+ }
+ componentDidUpdate(prevProps: Readonly<CollectionFreeFormInfoStateProps>) {
+ super.componentDidUpdate(prevProps);
+ this.clearState();
+ this.initState();
+ }
+ componentWillUnmount(): void {
+ this.clearState();
+ }
+ render() {
+ return (
+ <div className="collectionFreeform-infoUI" style={{display:this._hide ? 'none':undefined}}>
+ <div className="msg">{this.State?.[StateMessage]}</div>
+ <div className="gif-container" style={{ display: this.State?.[StateMessageGIF] ? undefined : 'none' }}>
+ <img className="gif" src={this.State?.[StateMessageGIF]} alt="state message gif"></img>
+ </div>
+ <div style={{position:"absolute", top:-10, left:-10}}>
+ <IconButton icon="x" color={SettingsManager.userColor} size={Size.XSMALL} type={Type.TERT} background={SettingsManager.userBackgroundColor} onClick={action(e => this._hide = true)} />
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
new file mode 100644
index 000000000..181f325f3
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -0,0 +1,230 @@
+import { IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Field, FieldResult } from '../../../../fields/Doc';
+import { InkTool } from '../../../../fields/InkField';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { LinkManager } from '../../../util/LinkManager';
+import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { CollectionFreeFormInfoState, InfoState, infoState, StateEntryFunc } from './CollectionFreeFormInfoState';
+import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
+
+export interface CollectionFreeFormInfoUIProps {
+ Document: Doc;
+ Freeform: CollectionFreeFormView;
+}
+
+@observer
+export class CollectionFreeFormInfoUI extends ObservableReactComponent<CollectionFreeFormInfoUIProps> {
+ _firstDocPos = { x: 0, y: 0 };
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ this.currState = this.setupStates();
+ }
+
+ @observable _currState: infoState | undefined = undefined;
+ get currState() { return this._currState!; } // prettier-ignore
+ set currState(val) { runInAction(() => (this._currState = val)); } // prettier-ignore
+
+ setCurrState = (state: infoState) => {
+ if (state) {
+ this.currState = state;
+ this.currState[StateEntryFunc]?.();
+ }
+ };
+
+ setupStates = () => {
+ // state entry functions
+ const setBackground = (colour: string) => () => (this._props.Freeform.layoutDoc.backgroundColor = colour);
+ const setOpacity = (opacity: number) => () => (this._props.Freeform.layoutDoc.opacity = opacity);
+ // arc transition trigger conditions
+ const firstDoc = () => (this._props.Freeform.childDocs.length ? this._props.Freeform.childDocs[0] : undefined);
+ const numDocs = () => this._props.Freeform.childDocs.length;
+
+ let docX: FieldResult<Field>;
+ let docY: FieldResult<Field>;
+
+ const docNewX = () => firstDoc()?.x;
+ const docNewY = () => firstDoc()?.y;
+
+ const linkStart = () => DocumentLinksButton.StartLink;
+
+ const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length;
+ const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView;
+
+ const activeTool = () => Doc.ActiveTool;
+
+ const pin = () => DocListCast(Doc.ActivePresentation?.data);
+
+ let trail: number;
+
+ const trailView = () => DocumentManager.Instance.DocumentViews.find(view => view.Document === Doc.MyTrails);
+ const presentationMode = () => Doc.ActivePresentation?.presentation_status;
+
+ // set of states
+ const start = InfoState('Click anywhere and begin typing to create your first text document.', {
+ docCreated: [() => numDocs(), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return oneDoc;
+ }],
+ }, setBackground("blue")); // prettier-ignore
+
+ const oneDoc = InfoState('Hello world! You can drag and drop to move your document around.', {
+ // docCreated: [() => numDocs() > 1, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc1;
+ }],
+ }, setBackground("red")); // prettier-ignore
+
+ const movedDoc1 = InfoState('Great moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc2;
+ }],
+ }, setBackground("yellow")); // prettier-ignore
+
+ const movedDoc2 = InfoState('Slick moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc3;
+ }],
+ }, setBackground("pink")); // prettier-ignore
+
+ const movedDoc3 = InfoState('Groovy moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc1;
+ }],
+ }, setBackground("green")); // prettier-ignore
+
+ const multipleDocs = InfoState('Let\'s create a new link. Click the link icon on one of your documents.', {
+ linkStarted: [() => linkStart(), () => startedLink],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ }, setBackground("purple")); // prettier-ignore
+
+ const startedLink = InfoState('Now click the highlighted link icon on your other document.', {
+ linkCreated: [() => numDocLinks(), () => madeLink],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ }, setBackground("orange")); // prettier-ignore
+
+ const madeLink = InfoState('You made your first link! You can view your links by selecting the blue dot.', {
+ linkCreated: [() => !numDocLinks(), () => multipleDocs],
+ linkViewed: [() => linkMenuOpen(), () => {
+ alert(numDocLinks() + " cheer for " + numDocLinks() + " link!");
+ return viewedLink;
+ }],
+ }, setBackground("blue")); // prettier-ignore
+
+ const viewedLink = InfoState('Great work. You are now ready to create your own hypermedia world.', {
+ linkDeleted: [() => !numDocLinks(), () => multipleDocs],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ docCreated: [() => numDocs() == 3, () => {
+ trail = pin().length;
+ return presentDocs;
+ }],
+ activePen: [() => activeTool() === InkTool.Pen, () => penMode],
+ }, setBackground("black")); // prettier-ignore
+
+ const presentDocs = InfoState('Another document! You could make a presentation. Click the pin icon in the top left corner.', {
+ docPinned: [() => pin().length > trail, () => {
+ trail = pin().length;
+ return pinnedDoc1;
+ }],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ }, setBackground("black"), "/assets/dash-pin-with-view.gif");
+
+ const penMode = InfoState('You\'re in pen mode. Click and drag to draw your first masterpiece.', {
+ // activePen: [() => activeTool() === InkTool.Eraser, () => eraserMode],
+ activePen: [() => activeTool() !== InkTool.Pen, () => viewedLink],
+ }); // prettier-ignore
+
+ // const eraserMode = InfoState('You\'re in eraser mode. Say goodbye to your first masterpiece.', {
+ // docsRemoved: [() => numDocs() == 3, () => demos],
+ // }); // prettier-ignore
+
+ const pinnedDoc1 = InfoState('You just pinned your doc.', {
+ docPinned: [() => pin().length > trail, () => {
+ trail = pin().length;
+ return pinnedDoc2;
+ }],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ const pinnedDoc2 = InfoState(`You pinned another doc.`, {
+ docPinned: [() => pin().length > trail, () => {
+ trail = pin().length;
+ return pinnedDoc3;
+ }],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ const pinnedDoc3 = InfoState(`You pinned yet another doc.`, {
+ docPinned: [() => pin().length > trail, () => {
+ trail = pin().length;
+ return pinnedDoc2;
+ }],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ // const openedTrail = InfoState('This is your trails tab.', {
+ // trailView: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // });
+
+ // const editPresentationMode = InfoState('You are editing your presentation.', {
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ // autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ // docRemoved: [() => numDocs() < 3, () => demos],
+ // docCreated: [() => numDocs() == 4, () => completed],
+ // });
+
+ const manualPresentationMode = InfoState('You\'re in manual presentation mode.', {
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ docCreated: [() => numDocs() == 4, () => completed],
+ });
+
+ const autoPresentationMode = InfoState('You\'re in auto presentation mode.', {
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ docCreated: [() => numDocs() == 4, () => completed],
+ });
+
+ const completed = InfoState('Eager to learn more? Click the ? icon in the top right corner to read our full documentation.', {
+ docRemoved: [() => numDocs() == 1, () => oneDoc],
+ }, setBackground("white")); // prettier-ignore
+
+ return start;
+ };
+
+ render() {
+ return <CollectionFreeFormInfoState next={this.setCurrState} infoState={this.currState} />;
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 403fba67b..b8c0967c1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -5,7 +5,7 @@ import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
-import React = require('react');
+import * as React from 'react';
export interface ViewDefBounds {
type: string;
@@ -37,6 +37,7 @@ export interface PoolData {
zIndex?: number;
width?: number;
height?: number;
+ autoDim?: number; // 1 for set to Panel dims, 0 for use width/height as entered
backgroundColor?: string;
color?: string;
opacity?: number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index aca6df3c9..6337c8d34 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,5 +1,6 @@
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, Field } from '../../../../fields/Doc';
import { Brushed, DocCss } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
@@ -12,8 +13,8 @@ import { SettingsManager } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../../nodes/DocumentView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormLinkView.scss';
-import React = require('react');
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -24,24 +25,29 @@ export interface CollectionFreeFormLinkViewProps {
// props.screentolocatransform
@observer
-export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
+export class CollectionFreeFormLinkView extends ObservableReactComponent<CollectionFreeFormLinkViewProps> {
@observable _opacity: number = 0;
@observable _start = 0;
_anchorDisposer: IReactionDisposer | undefined;
_timeout: NodeJS.Timeout | undefined;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentWillUnmount() {
this._anchorDisposer?.();
}
- @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
+ @action timeout: any = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
componentDidMount() {
this._anchorDisposer = reaction(
() => [
- this.props.A.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
- this.props.B.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+ this._props.A._props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+ this._props.B._props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
],
action(() => {
this._start = Date.now();
@@ -53,9 +59,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
);
}
placeAnchors = () => {
- const { A, B, LinkDocs } = this.props;
+ const { A, B, LinkDocs } = this._props;
const linkDoc = LinkDocs[0];
- if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
+ if (SnappingManager.IsDragging || !A.ContentDiv || !B.ContentDiv) return;
setTimeout(
action(() => (this._opacity = 0.75)),
0
@@ -85,9 +91,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
} else {
const m = targetAhyperlink.getBoundingClientRect();
- const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / A.props.PanelWidth();
- const mpy = mp[1] / A.props.PanelHeight();
+ const mp = A._props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ const mpx = mp[0] / A._props.PanelWidth();
+ const mpy = mp[1] / A._props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_1_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_1_y = mpy * 100;
if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
@@ -100,9 +106,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
} else {
const m = targetBhyperlink.getBoundingClientRect();
- const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / B.props.PanelWidth();
- const mpy = mp[1] / B.props.PanelHeight();
+ const mp = B._props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ const mpx = mp[0] / B._props.PanelWidth();
+ const mpy = mp[1] / B._props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_2_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_2_y = mpy * 100;
if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
@@ -115,18 +121,18 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
this,
e,
(e, down, delta) => {
- this.props.LinkDocs[0].link_relationship_OffsetX = NumCast(this.props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
- this.props.LinkDocs[0].link_relationship_OffsetY = NumCast(this.props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
+ this._props.LinkDocs[0].link_relationship_OffsetX = NumCast(this._props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
+ this._props.LinkDocs[0].link_relationship_OffsetY = NumCast(this._props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
return false;
},
emptyFunction,
action(() => {
SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
- LinkManager.currentLink = this.props.LinkDocs[0];
+ SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
+ LinkManager.currentLink = this._props.LinkDocs[0];
this.toggleProperties();
// OverlayView.Instance.addElement(
- // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+ // <LinkEditor sourceDoc={this._props.A._props.Document} linkDoc={this._props.LinkDocs[0]}
// showLinks={action(() => { })}
// />, { x: 300, y: 300 });
})
@@ -171,23 +177,23 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
@action
toggleProperties = () => {
- if ((SettingsManager.propertiesWidth ?? 0) < 100) {
- SettingsManager.propertiesWidth = 250;
+ if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
+ SettingsManager.Instance.propertiesWidth = 250;
}
};
@action
onClickLine = () => {
SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
- LinkManager.currentLink = this.props.LinkDocs[0];
+ SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
+ LinkManager.currentLink = this._props.LinkDocs[0];
this.toggleProperties();
};
@computed.struct get renderData() {
this._start;
- SnappingManager.GetIsDragging();
- const { A, B, LinkDocs } = this.props;
+ SnappingManager.IsDragging;
+ const { A, B, LinkDocs } = this._props;
if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
@@ -200,8 +206,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const atop = this.visibleY(adiv);
const btop = this.visibleY(bdiv);
if (!a.width || !b.width) return undefined;
- const aDocBounds = (A.props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
- const bDocBounds = (B.props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
+ const aDocBounds = (A._props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
+ const bDocBounds = (B._props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
const aleft = this.visibleX(adiv);
const bleft = this.visibleX(bdiv);
const aclipped = aleft !== a.left || atop !== a.top;
@@ -223,12 +229,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
- const aActive = A.isSelected() || A.rootDoc[Brushed];
- const bActive = B.isSelected() || B.rootDoc[Brushed];
+ const aActive = A.SELECTED || A.Document[Brushed];
+ const bActive = B.SELECTED || B.Document[Brushed];
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
- const link = this.props.LinkDocs[0];
+ const link = this._props.LinkDocs[0];
return {
a,
b,
@@ -250,7 +256,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
render() {
if (!this.renderData) return null;
- const link = this.props.LinkDocs[0];
+ const link = this._props.LinkDocs[0];
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 420e6a318..779a0bf96 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -5,7 +5,7 @@ import { DocumentManager } from '../../../util/DocumentManager';
import { LightboxView } from '../../LightboxView';
import './CollectionFreeFormLinksView.scss';
import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
-import React = require('react');
+import * as React from 'react';
@observer
export class CollectionFreeFormLinksView extends React.Component {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index 856e195a3..f54726a00 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -4,11 +4,11 @@ import { Doc } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { PresBox } from '../../nodes/trails/PresBox';
import './CollectionFreeFormView.scss';
-import React = require('react');
+import * as React from 'react';
import { CollectionFreeFormView } from './CollectionFreeFormView';
export interface CollectionFreeFormPannableContentsProps {
- rootDoc: Doc;
+ Document: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
transition?: string;
@@ -20,7 +20,7 @@ export interface CollectionFreeFormPannableContentsProps {
@observer
export class CollectionFreeFormPannableContents extends React.Component<CollectionFreeFormPannableContentsProps> {
@computed get presPaths() {
- return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null;
+ return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.Document) : null;
}
// rectangle highlight used when following trail/link to a region of a collection that isn't a document
showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
index 5fa01b102..7951aff65 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -1,14 +1,12 @@
-@import "global/globalCssVariables";
+@import 'global/globalCssVariables.module.scss';
.collectionFreeFormRemoteCursors-cont {
-
- position:absolute;
+ position: absolute;
z-index: $remoteCursors-zindex;
transform-origin: 'center center';
}
.collectionFreeFormRemoteCursors-canvas {
-
- position:absolute;
+ position: absolute;
width: 20px;
height: 20px;
opacity: 0.5;
@@ -21,4 +19,4 @@
// fontStyle: "italic",
margin-left: -12;
margin-top: 4;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9e8d92d7d..45e24bbb2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -9,8 +9,8 @@ import { listSpec } from '../../../../fields/Schema';
import { Cast } from '../../../../fields/Types';
import { CollectionViewProps } from '../CollectionView';
import './CollectionFreeFormView.scss';
-import React = require('react');
-import v5 = require('uuid/v5');
+import * as React from 'react';
+import * as uuid from 'uuid';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@@ -42,7 +42,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
if (el) {
const ctx = el.getContext('2d');
if (ctx) {
- ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22';
ctx.fillRect(0, 0, 20, 20);
ctx.fillStyle = 'black';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 250760bd5..7d3acaea7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.collectionfreeformview-none {
position: inherit;
@@ -255,3 +255,49 @@
background-color: rgba($color: #000000, $alpha: 0.4);
position: absolute;
}
+
+.collectionFreeform-infoUI {
+ position: absolute;
+ display: block;
+ top: 0;
+
+ color: white;
+ background-color: #5075ef;
+ border-radius: 5px;
+ box-shadow: 2px 2px 5px black;
+
+ margin: 15px;
+ padding: 10px;
+
+ .msg {
+ position: relative;
+ // display: block;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+
+ }
+
+ .gif-container {
+ position: relative;
+ margin-top: 5px;
+ // display: block;
+
+ justify-content: center;
+ align-items: center;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+
+
+ .gif {
+ background-color: transparent;
+ height: 300px;
+ }
+ }
+
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d53049e04..1f4688729 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,8 +1,9 @@
import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction, toJS } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
+import * as React from 'react';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
@@ -45,13 +46,12 @@ import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
+import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import React = require('react');
-import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -69,9 +69,13 @@ export type collectionFreeformViewProps = {
@observer
export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
public get displayName() {
- return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
+ return 'CollectionFreeFormView(' + this._props.Document.title?.toString() + ')';
} // this makes mobx trace() statements more descriptive
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@observable
public static ShowPresPaths = false;
@@ -90,19 +94,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _brushtimer1: any;
public get isAnnotationOverlay() {
- return this.props.isAnnotationOverlay;
+ return this._props.isAnnotationOverlay;
}
public get scaleFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_scale';
+ return (this._props.viewField ?? '') + '_freeform_scale';
}
private get panXFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_panX';
+ return (this._props.viewField ?? '') + '_freeform_panX';
}
private get panYFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_panY';
+ return (this._props.viewField ?? '') + '_freeform_panY';
}
private get autoResetFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_autoReset';
+ return (this._props.viewField ?? '') + '_freeform_autoReset';
}
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@@ -113,7 +117,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
- @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region
+ @observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined = undefined; // highlighted region of freeform canvas used by presentations to indicate a region
@observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
@computed get contentViews() {
@@ -123,19 +127,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return renderableEles;
}
@computed get fitToContentVals() {
+ const hgt = this.contentBounds.b - this.contentBounds.y;
+ const wid = this.contentBounds.r - this.contentBounds.x;
return {
- bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
+ bounds: { ...this.contentBounds, cx: this.contentBounds.x + wid / 2, cy: this.contentBounds.y + hgt / 2 },
scale:
- !this.childDocs.length || !Number.isFinite(this.contentBounds.b - this.contentBounds.y) || !Number.isFinite(this.contentBounds.r - this.contentBounds.x)
- ? 1
- : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)),
+ (!this.childDocs.length || !Number.isFinite(hgt) || !Number.isFinite(wid)
+ ? 1 //
+ : Math.min(this._props.PanelHeight() / hgt, this._props.PanelWidth() / wid)) / (this._props.NativeDimScaling?.() || 1),
};
}
@computed get fitContentsToBox() {
- return (this.props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
+ return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get contentBounds() {
- const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
+ const cb = Cast(this.dataDoc.contentBounds, listSpec('number'));
return cb
? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
: aggregateBounds(
@@ -145,29 +151,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
@computed get nativeWidth() {
- return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
+ return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
@computed get nativeHeight() {
- return this.props.NativeHeight?.() || (this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
+ return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
@computed get cachedCenteringShiftX(): number {
- const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
- return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling;
+ return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
- const dv = this.props.DocumentView?.();
- const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
+ const dv = this._props.DocumentView?.();
+ const fitWidth = this._props.layout_fitWidth?.(this.Document) ?? dv?.layoutDoc.layout_fitWidth;
+ const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling;
// if freeform has a native aspect, then the panel height needs to be adjusted to match it
- const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
- return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ const aspect = dv?.nativeWidth && dv?.nativeHeight && !fitWidth ? dv.nativeHeight / dv.nativeWidth : this._props.PanelHeight() / this._props.PanelWidth();
+ return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : (aspect * this._props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get panZoomXf() {
return new Transform(this.panX(), this.panY(), 1 / this.zoomScaling());
}
@computed get screenToLocalXf() {
- return this.props
+ return this._props
.ScreenToLocalTransform()
- .scale(this.props.isAnnotationOverlay ? 1 : 1)
+ .scale(this._props.isAnnotationOverlay ? 1 : 1)
.translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
.transform(this.panZoomXf);
}
@@ -182,15 +189,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const timecode = Math.round(time);
docs.forEach(doc => {
CollectionFreeFormDocumentView.animFields.forEach(val => {
- const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null);
+ const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null);
findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
});
CollectionFreeFormDocumentView.animStringFields.forEach(val => {
- const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null);
+ const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null);
findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
});
CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => {
- const findexed = Cast(doc[`${val}-indexed`], listSpec(InkField), null);
+ const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
findexed?.length <= timecode + 1 && findexed.push(undefined as any);
});
});
@@ -216,46 +223,44 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _keyframeEditing = false;
@action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
getKeyFrameEditing = () => this._keyframeEditing;
- onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
- onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
- onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+ onBrowseClickHandler = () => this._props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
+ onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
+ onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
fitContentOnce = () => {
- if (this.props.DocumentView?.().nativeWidth) return;
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
this.layoutDoc._freeform_panY = vals.bounds.cy;
this.layoutDoc._freeform_scale = vals.scale;
};
freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
- ignoreNativeDimScaling = () => (this.fitContentsToBox ? true : false);
// freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
// this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
PanZoomCenterXf = () =>
- this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
+ this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
ScreenToLocalXf = () => this.screenToLocalXf.copy();
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
- isAnyChildContentActive = () => this.props.isAnyChildContentActive();
- addLiveTextBox = (newBox: Doc) => {
- FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- this.addDocument(newBox);
+ isAnyChildContentActive = () => this._props.isAnyChildContentActive();
+ addLiveTextBox = (newDoc: Doc) => {
+ FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ this.addDocument(newDoc);
};
selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
- docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
+ docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
};
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
if (newBox instanceof Doc) {
- if ((retVal = this.props.addDocument?.(newBox) || false)) {
+ if ((retVal = this._props.addDocument?.(newBox) || false)) {
this.bringToFront(newBox);
this.updateCluster(newBox);
}
} else {
- retVal = this.props.addDocument?.(newBox) || false;
+ retVal = this._props.addDocument?.(newBox) || false;
// bcz: deal with clusters
}
if (retVal) {
@@ -263,13 +268,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (const newBox of newBoxes) {
if (newBox.activeFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]);
- CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}-indexed`]);
+ CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}_indexed`]);
CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]);
delete newBox.activeFrame;
CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i]));
}
}
- if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ if (this.Document._currentFrame !== undefined && !this._props.isAnnotationOverlay) {
CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
}
}
@@ -284,15 +289,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
groupFocus = (anchor: Doc, options: DocFocusOptions) => {
- options.docTransform = new Transform(-NumCast(this.rootDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.rootDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
- const res = this.props.focus(this.rootDoc, options);
+ options.docTransform = new Transform(-NumCast(this.layoutDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.layoutDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
+ const res = this._props.focus(this.Document, options);
options.docTransform = undefined;
return res;
};
focus = (anchor: Doc, options: DocFocusOptions) => {
if (this._lightboxDoc) return;
- if (anchor === this.rootDoc) {
+ if (anchor === this.Document) {
if (options.willZoomCentered && options.zoomScale) {
this.fitContentOnce();
options.didMove = true;
@@ -301,7 +306,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return;
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
- const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
+ const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined);
// focus on the document in the collection
@@ -323,11 +328,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
findDoc(dv => res(dv));
});
- @action
internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
if (!super.onInternalDrop(e, de)) return false;
const refDoc = docDragData.droppedDocuments[0];
- const [xpo, ypo] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ const [xpo, ypo] = this._props.ScreenToLocalTransform().transformPoint(de.x, de.y);
const z = NumCast(refDoc.z);
const x = (z ? xpo : xp) - docDragData.offset[0];
const y = (z ? ypo : yp) - docDragData.offset[1];
@@ -342,7 +346,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
- const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], this.props.ScreenToLocalTransform().Rotate);
+ const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], this._props.ScreenToLocalTransform().Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
@@ -397,18 +401,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
dropDoc.y = yp - annoDragData.offset[1];
this.bringToFront(dropDoc);
}
- return dropDoc || this.rootDoc;
+ return dropDoc || this.Document;
};
return true;
}
@undoBatch
internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
- if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
+ if (linkDragData.linkDragView.props.docViewPath().includes(this._props.docViewPath().lastElement())) {
let added = false;
// do nothing if link is dropped into any freeform view parent of dragged document
const source =
- !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc
+ !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.Document
? Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' })
: Docs.Create.FontIconDocument({
title: 'anchor',
@@ -425,7 +429,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_xPadding: 0,
onClick: FollowLinkScript(),
});
- added = this.props.addDocument?.(source) ? true : false;
+ added = this._props.addDocument?.(source) ? true : false;
de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
e.stopPropagation();
@@ -462,7 +466,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.childLayoutPairs
.map(pair => pair.layout)
.reduce((cluster, cd) => {
- const grouping = this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
+ const grouping = this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
if (grouping !== -1) {
const layoutDoc = Doc.Layout(cd);
const cx = NumCast(cd.x) - this._clusterDistance / 2;
@@ -479,11 +483,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (cluster !== -1) {
const ptsParent = e;
if (ptsParent) {
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
- const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
+ const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this._props.DocumentView?.())!);
const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
- de.moveDocument = this.props.moveDocument;
+ de.moveDocument = this._props.moveDocument;
de.offset = this.screenToLocalXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
DragManager.StartDocumentDrag(
clusterDocs.map(v => v.ContentDiv!),
@@ -501,7 +505,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
updateClusters(_freeform_useClusters: boolean) {
- this.props.Document._freeform_useClusters = _freeform_useClusters;
+ this._props.Document._freeform_useClusters = _freeform_useClusters;
this._clusterSets.length = 0;
this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
}
@@ -509,7 +513,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
updateClusterDocs(docs: Doc[]) {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document._freeform_useClusters) {
+ if (this._props.Document._freeform_useClusters) {
const docFirst = docs[0];
docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
const preferredInd = NumCast(docFirst.layout_cluster);
@@ -552,7 +556,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
updateCluster = (doc: Doc) => {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document._freeform_useClusters) {
+ if (this._props.Document._freeform_useClusters) {
this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
const preferredInd = NumCast(doc.layout_cluster);
doc.layout_cluster = -1;
@@ -582,35 +586,36 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
clusterStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
- let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
- switch (property) {
- case StyleProp.BackgroundColor:
- const cluster = NumCast(doc?.layout_cluster);
- if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
- if (this._clusterSets.length <= cluster) {
- setTimeout(() => doc && this.updateCluster(doc));
- } else {
- // choose a cluster color from a palette
- const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
- styleProp = colors[cluster % colors.length];
- const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
- // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
- set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+ let styleProp = this._props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
+ if (doc && this.childDocList?.includes(doc))
+ switch (property) {
+ case StyleProp.BackgroundColor:
+ const cluster = NumCast(doc?.layout_cluster);
+ if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
+ if (this._clusterSets.length <= cluster) {
+ setTimeout(() => doc && this.updateCluster(doc));
+ } else {
+ // choose a cluster color from a palette
+ const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+ styleProp = colors[cluster % colors.length];
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+ // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
+ set?.map(s => (styleProp = StrCast(s.backgroundColor)));
+ }
}
- }
- break;
- case StyleProp.FillColor:
- if (doc && this.Document._currentFrame !== undefined) {
- return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
- }
- }
+ break;
+ case StyleProp.FillColor:
+ if (doc && this.Document._currentFrame !== undefined) {
+ return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
+ }
+ }
return styleProp;
};
trySelectCluster = (addToSel: boolean) => {
if (this._hitCluster !== -1) {
!addToSel && SelectionManager.DeselectAll();
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this._props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
this.selectDocuments(eles);
return true;
}
@@ -623,8 +628,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
- if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) {
- if (!this.props.Document._isGroup) {
+ if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive(true)) {
+ if (!this.Document.isGroup) {
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
switch (Doc.ActiveTool) {
@@ -636,7 +641,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
break;
case InkTool.None:
- if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
+ if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
this._hitCluster = this.pickCluster(this.screenToLocalXf.transformPoint(e.clientX, e.clientY));
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, this._hitCluster !== -1 ? true : false, false);
}
@@ -659,7 +664,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.screenToLocalXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkWidth = ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale;
+ const inkWidth = ActiveInkWidth() * this._props.ScreenToLocalTransform().Scale;
const inkDoc = Docs.Create.InkDocument(
points,
{ title: ge.gesture.toString(),
@@ -698,7 +703,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
onEraserUp = (e: PointerEvent): void => {
- this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
+ this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.Document));
this._deleteList = [];
this._batch?.end();
};
@@ -708,7 +713,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (this._lightboxDoc) this._lightboxDoc = undefined;
if (Utils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) {
if (this.onBrowseClickHandler()) {
- this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
+ this.onBrowseClickHandler().script.run({ documentView: this._props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
e.stopPropagation();
e.preventDefault();
} else if (this.isContentActive() && e.shiftKey) {
@@ -731,9 +736,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const ctrlKey = e.ctrlKey && !e.shiftKey;
const shiftKey = e.shiftKey && !e.ctrlKey;
PresBox.Instance?.pauseAutoPres();
- this.props.DocumentView?.().clearViewTransition();
+ this._props.DocumentView?.().clearViewTransition();
const [dxi, dyi] = this.screenToLocalXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.props.ScreenToLocalTransform().Rotate);
+ const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this._props.ScreenToLocalTransform().Rotate);
this.setPan(NumCast(this.Document[this.panXFieldKey]) - (ctrlKey ? 0 : dx), NumCast(this.Document[this.panYFieldKey]) - (shiftKey ? 0 : dy), 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
@@ -753,8 +758,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
if (!this._deleteList.includes(intersect.inkView)) {
this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
+ SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
if (!e.shiftKey) {
this._eraserLock++;
@@ -786,7 +791,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
// pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
- if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.zoomScaling()) {
+ if (!this._props.isAnnotationOverlay || 1 - NumCast(this.layoutDoc._freeform_scale_min, 1) / this.zoomScaling()) {
this.pan(e);
e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
}
@@ -802,7 +807,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
return this.childDocs
- .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.()))
+ .map(doc => DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.()))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
.map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
.filter(
@@ -813,23 +818,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
eraserMax.X >= inkViewBounds.left &&
eraserMax.Y >= inkViewBounds.top
)
- .reduce((intersections, { inkStroke, inkView }) => {
- const { inkData } = inkStroke.inkScaledData();
- // Convert from screen space to ink space for the intersection.
- const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
- const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
- for (var i = 0; i < inkData.length - 3; i += 4) {
- const rawIntersects = InkField.Segment(inkData, i).intersects({
- // compute all unique intersections
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
- });
- const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
- // return tuples of the inkingStroke intersected, and the t value of the intersection
- intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
- }
- return intersections;
- }, [] as { t: number; inkView: DocumentView }[]);
+ .reduce(
+ (intersections, { inkStroke, inkView }) => {
+ const { inkData } = inkStroke.inkScaledData();
+ // Convert from screen space to ink space for the intersection.
+ const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
+ const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
+ for (var i = 0; i < inkData.length - 3; i += 4) {
+ const rawIntersects = InkField.Segment(inkData, i).intersects({
+ // compute all unique intersections
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
+ });
+ const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
+ // return tuples of the inkingStroke intersected, and the t value of the intersection
+ intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
+ }
+ return intersections;
+ },
+ [] as { t: number; inkView: DocumentView }[]
+ );
};
/**
@@ -896,7 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs
.filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
.forEach(doc => {
- const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())?.ComponentView as InkingStroke;
+ const otherInk = DocumentManager.Instance.getDocumentView(doc, this._props.DocumentView?.())?.ComponentView as InkingStroke;
const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point));
const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt));
@@ -928,7 +936,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
- if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
+ if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.screenToLocalXf.transformPoint(pointX, pointY);
@@ -936,33 +944,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (deltaScale * invTransform.Scale > 20) {
deltaScale = 20 / invTransform.Scale;
}
- if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc[this.scaleFieldKey + '_min'])) {
+ if (deltaScale < 1 && invTransform.Scale <= NumCast(this.Document[this.scaleFieldKey + '_min'])) {
this.setPan(0, 0);
return;
}
- if (deltaScale * invTransform.Scale > NumCast(this.rootDoc[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
- deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
+ if (deltaScale * invTransform.Scale > NumCast(this.Document[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
+ deltaScale = NumCast(this.Document[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
}
- if (deltaScale * invTransform.Scale < NumCast(this.rootDoc[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
- deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
+ if (deltaScale * invTransform.Scale < NumCast(this.Document[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
+ deltaScale = NumCast(this.Document[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
}
const localTransform = invTransform.scaleAbout(deltaScale, x, y);
if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) {
const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20);
- this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
- this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
+ this._props.Document[this.scaleFieldKey] = Math.abs(safeScale);
+ this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this._props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
}
};
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
+ if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
PresBox.Instance?.pauseAutoPres();
- if (this.layoutDoc._Transform || this.props.Document.treeView_OutlineMode === TreeViewType.outline) return;
+ if (this.layoutDoc._Transform || this._props.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
- const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
- const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
+ const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
switch (
!e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
Doc.UserDoc().freeformScrollMode : // no modifiers, do assigned mode
@@ -970,7 +978,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
freeformScrollMode.Zoom : freeformScrollMode.Pan // prettier-ignore
) {
case freeformScrollMode.Pan:
- if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this.props.isContentActive(true)) {
+ if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this._props.isContentActive(true)) {
const deltaX = e.shiftKey ? e.deltaX : e.ctrlKey ? 0 : e.deltaX;
const deltaY = e.shiftKey ? 0 : e.ctrlKey ? e.deltaY : e.deltaY;
this.scrollPan({ deltaX: -deltaX * this.screenToLocalXf.Scale, deltaY: e.shiftKey ? 0 : -deltaY * this.screenToLocalXf.Scale });
@@ -978,8 +986,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
default:
case freeformScrollMode.Zoom:
- if ((e.ctrlKey || !scrollable) && this.props.isContentActive(true)) {
- this.zoom(e.clientX, e.clientY, Math.max(-1, Math.min(1, e.deltaY))); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ if ((e.ctrlKey || !scrollable) && this._props.isContentActive(true)) {
+ this.zoom(e.clientX, e.clientY, Math.max(-1, Math.min(1, e.deltaY))); // if (!this._props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
e.preventDefault();
}
break;
@@ -995,7 +1003,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
const measuredDocs = docs
- .map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc.width), height: NumCast(doc.height) } }))
+ .map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc._width), height: NumCast(doc._height) } }))
.filter(({ pos, size }) => pos && size)
.map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
@@ -1008,45 +1016,45 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) },
}),
{
- xrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
- yrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ xrange: { min: this._props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ yrange: { min: this._props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
}
);
- const scaling = this.zoomScaling() * (this.props.NativeDimScaling?.() || 1);
- const panelWidMax = (this.props.PanelWidth() / scaling) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelWidMin = (this.props.PanelWidth() / scaling) * (this.props.originTopLeft ? 0 : 1);
- const panelHgtMax = (this.props.PanelHeight() / scaling) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelHgtMin = (this.props.PanelHeight() / scaling) * (this.props.originTopLeft ? 0 : 1);
- if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this.props.originTopLeft ? 0 : panelWidMax / 2);
- else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this.props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2);
- if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this.props.originTopLeft ? 0 : panelHgtMax / 2);
- else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this.props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2);
+ const scaling = this.zoomScaling() * (this._props.NativeDimScaling?.() || 1);
+ const panelWidMax = (this._props.PanelWidth() / scaling) * (this._props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelWidMin = (this._props.PanelWidth() / scaling) * (this._props.originTopLeft ? 0 : 1);
+ const panelHgtMax = (this._props.PanelHeight() / scaling) * (this._props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelHgtMin = (this._props.PanelHeight() / scaling) * (this._props.originTopLeft ? 0 : 1);
+ if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this._props.originTopLeft ? 0 : panelWidMax / 2);
+ else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this._props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2);
+ if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this._props.originTopLeft ? 0 : panelHgtMax / 2);
+ else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this._props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2);
}
}
if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc) {
this.setPanZoomTransition(panTime);
- const minScale = NumCast(this.rootDoc._freeform_scale_min, 1);
+ const minScale = NumCast(this.dataDoc._freeform_scale_min, 1);
const scale = 1 - minScale / this.zoomScaling();
- const minPanX = NumCast(this.rootDoc._freeform_panX_min, 0);
- const minPanY = NumCast(this.rootDoc._freeform_panY_min, 0);
- const maxPanX = NumCast(this.rootDoc._freeform_panX_max, this.nativeWidth);
+ const minPanX = NumCast(this.dataDoc._freeform_panX_min, 0);
+ const minPanY = NumCast(this.dataDoc._freeform_panY_min, 0);
+ const maxPanX = NumCast(this.dataDoc._freeform_panX_max, this.nativeWidth);
const newPanX = Math.min(minPanX + scale * maxPanX, Math.max(minPanX, panX));
- const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
- const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
- const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
+ const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this._props.PanelWidth() - this._props.PanelHeight()) * this._props.ScreenToLocalTransform().Scale) / minScale;
+ const nativeHeight = (this._props.PanelHeight() / this._props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
+ const maxScrollTop = this.nativeHeight / this._props.ScreenToLocalTransform().Scale - this._props.PanelHeight();
const maxPanY =
minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by layout_fitWidth
- scale * NumCast(this.rootDoc._panY_max, nativeHeight) +
- (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
+ scale * NumCast(this.dataDoc._panY_max, nativeHeight) +
+ (!this._props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
- if (false && NumCast(this.rootDoc.layout_scrollTop) && NumCast(this.rootDoc._freeform_scale, minScale) !== minScale) {
- const relTop = NumCast(this.rootDoc.layout_scrollTop) / maxScrollTop;
- this.rootDoc.layout_scrollTop = undefined;
+ if (false && NumCast(this.layoutDoc.layout_scrollTop) && NumCast(this.layoutDoc._freeform_scale, minScale) !== minScale) {
+ const relTop = NumCast(this.layoutDoc.layout_scrollTop) / maxScrollTop;
+ this.layoutDoc.layout_scrollTop = undefined;
newPanY = minPanY + relTop * (maxPanY - minPanY);
- } else if (fitYscroll > 2 && this.rootDoc.layout_scrollTop === undefined && NumCast(this.rootDoc._freeform_scale, minScale) === minScale) {
+ } else if (fitYscroll > 2 && this.layoutDoc.layout_scrollTop === undefined && NumCast(this.layoutDoc._freeform_scale, minScale) === minScale) {
const maxPanY = minPanY + fitYscroll;
const relTop = (panY - minPanY) / (maxPanY - minPanY);
- setTimeout(() => (this.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10);
+ setTimeout(() => (this.layoutDoc.layout_scrollTop = relTop * maxScrollTop), 10);
newPanY = minPanY;
}
!this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1056,11 +1064,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
nudge = (x: number, y: number, nudgeTime: number = 500) => {
- const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
- if (collectionDoc?._type_collection !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) {
+ const collectionDoc = this._props.docViewPath().lastElement().Document;
+ if (collectionDoc?._type_collection !== CollectionViewType.Freeform) {
this.setPan(
- NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
- NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
+ NumCast(this.layoutDoc[this.panXFieldKey]) + ((this._props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
+ NumCast(this.layoutDoc[this.panYFieldKey]) + ((this._props.PanelHeight() / 2) * -y) / this.zoomScaling(),
nudgeTime,
true
);
@@ -1105,7 +1113,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
- if (this.Document._isGroup) return;
+ if (this.Document.isGroup) return;
this.setPanZoomTransition(transitionTime);
const screenXY = this.screenToLocalXf.inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
@@ -1127,20 +1135,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newScale =
scale === 0
? NumCast(this.layoutDoc[this.scaleFieldKey])
- : Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+ : Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this._props.PanelWidth() / Math.abs(bounds.width), this._props.PanelHeight() / Math.abs(bounds.height)));
return {
- panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
- panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
+ panX: this._props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
+ panY: this._props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
scale: newScale,
};
}
- const panelWidth = this.props.isAnnotationOverlay ? this.nativeWidth : this.props.PanelWidth();
- const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight();
+ const panelWidth = this._props.isAnnotationOverlay ? this.nativeWidth : this._props.PanelWidth();
+ const panelHeight = this._props.isAnnotationOverlay ? this.nativeHeight : this._props.PanelHeight();
const pw = panelWidth / NumCast(this.layoutDoc._freeform_scale, 1);
const ph = panelHeight / NumCast(this.layoutDoc._freeform_scale, 1);
- const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
- const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
+ const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this._props.isAnnotationOverlay ? pw / 2 : 0);
+ const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this._props.isAnnotationOverlay ? ph / 2 : 0);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
const maxYShift = Math.max(0, screen.bot - screen.top - (bounds.bot - bounds.top));
const phborder = bounds.top < screen.top || bounds.bot > screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0;
@@ -1148,54 +1156,53 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return {
panX: (bounds.left + bounds.right) / 2,
panY: (bounds.top + bounds.bot) / 2,
- scale: Math.min(this.props.PanelHeight() / (bounds.bot - bounds.top), this.props.PanelWidth() / (bounds.right - bounds.left)) / 1.1,
+ scale: Math.min(this._props.PanelHeight() / (bounds.bot - bounds.top), this._props.PanelWidth() / (bounds.right - bounds.left)) / 1.1,
};
}
return {
- panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panXFieldKey]) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
- panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panYFieldKey]) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
+ panX: (this._props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panXFieldKey]) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
+ panY: (this._props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panYFieldKey]) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
};
};
- isContentActive = () => this.props.isContentActive();
+ isContentActive = () => this._props.isContentActive();
@undoBatch
- @action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
const docView = fieldProps.DocumentView?.();
- if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+ if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
const below = !e.altKey && e.key !== 'Tab';
const layout_fieldKey = StrCast(docView.LayoutFieldKey);
- const newDoc = Doc.MakeCopy(docView.rootDoc, true);
- const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+ const newDoc = Doc.MakeCopy(docView.Document, true);
+ const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)];
newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
- if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
- else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
- if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
- newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
+ if (below) newDoc.y = NumCast(docView.Document.y) + NumCast(docView.Document._height) + 10;
+ else newDoc.x = NumCast(docView.Document.x) + NumCast(docView.Document._width) + 10;
+ if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) {
+ newDoc[layout_fieldKey] = docView.Document[layout_fieldKey];
}
Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ FormattedTextBox.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
};
@computed get childPointerEvents() {
- const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
+ const engine = this._props.layoutEngine?.() || StrCast(this._props.Document._layoutEngine);
const pointerevents = DocumentView.Interacting
? 'none'
- : this.props.childPointerEvents?.() ??
- (this.props.viewDefDivClick || //
- (engine === computePassLayout.name && !this.props.isSelected()) ||
+ : this._props.childPointerEvents?.() ??
+ (this._props.viewDefDivClick || //
+ (engine === computePassLayout.name && !this._props.isSelected()) ||
this.isContentActive() === false
? 'none'
- : this.props.pointerEvents?.());
+ : this._props.pointerEvents?.());
return pointerevents;
}
- @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined;
+ @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
childPointerEventsFunc = () => this._childPointerEvents;
- childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
+ childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
@@ -1203,55 +1210,54 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
<CollectionFreeFormDocumentViewWrapper
{...OmitKeys(entry, ['replica', 'pair']).omit}
key={childLayout[Id] + (entry.replica || '')}
- DataDoc={childData}
Document={childLayout}
+ TemplateDataDocument={childData}
dragStarting={this.dragStarting}
dragEnding={this.dragEnding}
- isGroupActive={this.props.isGroupActive}
- renderDepth={this.props.renderDepth + 1}
+ isGroupActive={this._props.isGroupActive}
+ renderDepth={this._props.renderDepth + 1}
hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
RenderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
- LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
- LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
+ LayoutTemplate={childLayout.z ? undefined : this._props.childLayoutTemplate}
+ LayoutTemplateString={childLayout.z ? undefined : this._props.childLayoutString}
rootSelected={childData ? this.rootSelected : returnFalse}
- waitForDoubleClickToClick={this.props.waitForDoubleClickToClick}
+ waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
onClick={this.onChildClickHandler}
onKey={this.onKeyDown}
onDoubleClick={this.onChildDoubleClickHandler}
onBrowseClick={this.onBrowseClickHandler}
- ScreenToLocalTransform={childLayout.z ? this.props.ScreenToLocalTransform : this.ScreenToLocalXf}
+ ScreenToLocalTransform={childLayout.z ? this._props.ScreenToLocalTransform : this.ScreenToLocalXf}
PanelWidth={childLayout[Width]}
PanelHeight={childLayout[Height]}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+ isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive}
isContentActive={this.childContentsActive}
- focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
+ focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus}
addDocTab={this.addDocTab}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- pinToPres={this.props.pinToPres}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- docViewPath={this.props.docViewPath}
+ addDocument={this._props.addDocument}
+ removeDocument={this._props.removeDocument}
+ moveDocument={this._props.moveDocument}
+ pinToPres={this._props.pinToPres}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ docViewPath={this._props.docViewPath}
styleProvider={this.clusterStyleProvider}
- dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
+ dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
bringToFront={this.bringToFront}
- layout_showTitle={this.props.childlayout_showTitle}
- dontRegisterView={this.props.dontRegisterView}
+ layout_showTitle={this._props.childlayout_showTitle}
+ dontRegisterView={this._props.dontRegisterView}
pointerEvents={this.childPointerEventsFunc}
- //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
/>
);
}
addDocTab = action((doc: Doc, where: OpenWhere) => {
- if (this.props.isAnnotationOverlay) return this.props.addDocTab(doc, where);
+ if (this._props.isAnnotationOverlay) return this._props.addDocTab(doc, where);
switch (where) {
case OpenWhere.inParent:
- return this.props.addDocument?.(doc) || false;
+ return this._props.addDocument?.(doc) || false;
case OpenWhere.inParentFromScreen:
const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.embedContainer);
return (
@@ -1263,7 +1269,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return doc;
})
) &&
- (!docContext || this.props.removeDocument?.(docContext))) ||
+ (!docContext || this._props.removeDocument?.(docContext))) ||
false
);
case undefined:
@@ -1277,9 +1283,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
}
- return this.props.addDocTab(doc, where);
+ return this._props.addDocTab(doc, where);
});
- @observable _lightboxDoc: Opt<Doc>;
+ @observable _lightboxDoc: Opt<Doc> = undefined;
getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
@@ -1289,9 +1295,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
const { z, zIndex } = childDoc;
const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
- const { x, y, _width, _height, opacity, _rotation } =
- layoutFrameNumber === undefined
- ? { _width: Cast(childDocLayout._width, 'number'), _height: Cast(childDocLayout._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
+ const { x, y, autoDim, _width, _height, opacity, _rotation } =
+ layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
+ ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
// prettier-ignore
const rotation = Cast(_rotation,'number',
@@ -1301,22 +1307,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
+ autoDim,
rotation,
- color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
- backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, this.props, StyleProp.BackgroundColor),
- opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
+ color: Cast(color, 'string') ? StrCast(color) : this._props.styleProvider?.(childDoc, this._props, StyleProp.Color),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, this._props, StyleProp.BackgroundColor),
+ opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this._props.styleProvider?.(childDoc, this._props, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
transition: StrCast(childDocLayout.dataTransition),
- pair,
pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
+ pair,
replica: '',
};
}
onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
- (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
+ (this._props.viewDefDivClick || ScriptCast(this._props.Document.onViewDefDivClick))?.script.run({ this: this._props.Document, payload });
e.stopPropagation();
};
@@ -1371,7 +1378,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
poolData: Map<string, PoolData>,
engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
) {
- return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX, this.props.engineProps);
+ return engine(poolData, this._props.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps);
}
doFreeformLayout(poolData: Map<string, PoolData>) {
@@ -1380,7 +1387,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@computed get layoutEngine() {
- return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
}
@computed get doInternalLayoutComputation() {
TraceMobx();
@@ -1414,28 +1421,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
// create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
- const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.rootDoc });
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, type_collection: true, filters: true } }, this.rootDoc);
+ const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.Document });
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } }, this.Document);
if (addAsAnnotation) {
- if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
} else {
- this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
+ this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
}
return anchor;
};
+ infoUI = () => (this.Document.annotationOn ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} />);
+
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
super.componentDidMount?.();
setTimeout(
action(() => {
this._firstRender = false;
this._disposers.groupBounds = reaction(
() => {
- if (this.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+ if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) {
const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
}
@@ -1466,15 +1475,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.pointerevents = reaction(
() => {
- const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
+ const engine = this._props.layoutEngine?.() || StrCast(this._props.Document._layoutEngine);
return DocumentView.Interacting
? 'none'
- : this.props.childPointerEvents?.() ??
- (this.props.viewDefDivClick || //
- (engine === computePassLayout.name && !this.props.isSelected()) ||
+ : this._props.childPointerEvents?.() ??
+ (this._props.viewDefDivClick || //
+ (engine === computePassLayout.name && !this._props.isSelected()) ||
this.isContentActive() === false
? 'none'
- : this.props.pointerEvents?.());
+ : this._props.pointerEvents?.());
},
pointerevents => (this._childPointerEvents = pointerevents as any),
{ fireImmediately: true }
@@ -1482,7 +1491,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.active = reaction(
() => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
- active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
+ active => !SnappingManager.IsDragging && this.dataDoc[this.autoResetFieldKey] && active && this.resetView()
);
})
);
@@ -1529,11 +1538,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
updateIcon = () =>
CollectionFreeFormView.UpdateIcon(
this.layoutDoc[Id] + '-icon' + new Date().getTime(),
- this.props.docViewPath().lastElement().ContentDiv!,
+ this._props.docViewPath().lastElement().ContentDiv!,
NumCast(this.layoutDoc._width),
NumCast(this.layoutDoc._height),
- this.props.PanelWidth(),
- this.props.PanelHeight(),
+ this._props.PanelWidth(),
+ this._props.PanelHeight(),
0,
1,
false,
@@ -1576,7 +1585,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
componentWillUnmount() {
- this.rootDoc[this.autoResetFieldKey] && this.resetView();
+ this.dataDoc[this.autoResetFieldKey] && this.resetView();
Object.values(this._disposers).forEach(disposer => disposer?.());
}
@@ -1593,7 +1602,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.x = scr?.[0];
doc.y = scr?.[1];
});
- this.props.addDocTab(childDocs as any as Doc, OpenWhere.inParentFromScreen);
+ this._props.addDocTab(childDocs as any as Doc, OpenWhere.inParentFromScreen);
};
@undoBatch
@@ -1617,9 +1626,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
///
@undoBatch
resetView = () => {
- this.rootDoc[this.panXFieldKey] = NumCast(this.rootDoc[this.panXFieldKey + '_reset']);
- this.props.Document[this.panYFieldKey] = NumCast(this.rootDoc[this.panYFieldKey + '_reset']);
- this.rootDoc[this.scaleFieldKey] = NumCast(this.rootDoc[this.scaleFieldKey + '_reset'], 1);
+ this.layoutDoc[this.panXFieldKey] = NumCast(this.dataDoc[this.panXFieldKey + '_reset']);
+ this.layoutDoc[this.panYFieldKey] = NumCast(this.dataDoc[this.panYFieldKey + '_reset']);
+ this.layoutDoc[this.scaleFieldKey] = NumCast(this.dataDoc[this.scaleFieldKey + '_reset'], 1);
};
///
/// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
@@ -1627,37 +1636,37 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
///
@undoBatch
toggleResetView = () => {
- this.rootDoc[this.autoResetFieldKey] = !this.rootDoc[this.autoResetFieldKey];
- if (this.rootDoc[this.autoResetFieldKey]) {
- this.rootDoc[this.panXFieldKey + '_reset'] = this.rootDoc[this.panXFieldKey];
- this.rootDoc[this.panYFieldKey + '_reset'] = this.rootDoc[this.panYFieldKey];
- this.rootDoc[this.scaleFieldKey + '_reset'] = this.rootDoc[this.scaleFieldKey];
+ this.dataDoc[this.autoResetFieldKey] = !this.dataDoc[this.autoResetFieldKey];
+ if (this.dataDoc[this.autoResetFieldKey]) {
+ this.dataDoc[this.panXFieldKey + '_reset'] = this.dataDoc[this.panXFieldKey];
+ this.dataDoc[this.panYFieldKey + '_reset'] = this.dataDoc[this.panYFieldKey];
+ this.dataDoc[this.scaleFieldKey + '_reset'] = this.dataDoc[this.scaleFieldKey];
}
};
onContextMenu = (e: React.MouseEvent) => {
- if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
+ if (this._props.isAnnotationOverlay || !ContextMenu.Instance) return;
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
- !this.props.Document._isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
+ !this._props.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+ !this._props.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Toggle auto arrange',
event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
icon: 'compress-arrows-alt',
});
- if (this.props.setContentView === emptyFunction) {
+ if (this._props.setContentView === emptyFunction) {
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
return;
}
!Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
- appearanceItems.push({ description: `Pin View`, event: () => this.props.pinToPres(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
+ appearanceItems.push({ description: `Pin View`, event: () => this._props.pinToPres(this.Document, { pinViewport: MarqueeView.CurViewBounds(this.dataDoc, this._props.PanelWidth(), this._props.PanelHeight()) }), icon: 'map-pin' });
!Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
- this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
+ this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
- this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
+ this._props.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
!Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
!Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
@@ -1665,11 +1674,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const options = ContextMenu.Instance.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
- !this.props.isAnnotationOverlay &&
+ !this._props.isAnnotationOverlay &&
!Doc.noviceMode &&
optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
- this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
- this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
+ this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
+ this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
if (!Doc.noviceMode) {
optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
}
@@ -1680,10 +1689,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@undoBatch
- @action
transcribeStrokes = () => {
- if (this.props.Document._isGroup && this.props.Document.transcription) {
- const text = StrCast(this.props.Document.transcription);
+ if (this._props.Document.isGroup && this._props.Document.transcription) {
+ const text = StrCast(this._props.Document.transcription);
const lines = text.split('\n');
const height = 30 + 15 * lines.length;
@@ -1698,25 +1706,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
- if (visited.has(this.rootDoc)) return;
- visited.add(this.rootDoc);
- showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
+ if (visited.has(this.Document)) return;
+ visited.add(this.Document);
+ showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.isGroup));
const activeDocs = this.getActiveDocuments();
- const size = this.screenToLocalXf.transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+ const size = this.screenToLocalXf.transformDirection(this._props.PanelWidth(), this._props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
activeDocs
- .filter(doc => doc._isGroup && SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))
+ .filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))
.forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.screenToLocalXf.inverse();
snappableDocs
- .filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
+ .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = invXf.transformPoint(left, top);
@@ -1731,7 +1739,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
incrementalRender = action(() => {
- if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+ if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this._props.docViewPath())) {
const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = 5;
for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) {
@@ -1745,28 +1753,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// which will be a DocumentView. In this sitation, this freeform views acts as an annotation overlay for
// the underlying DocumentView and will pan and scoll with the underlying Documen tView.
@computed get underlayViews() {
- return this.props.children ? [this.props.children] : [];
+ return this._props.children ? [toJS(this._props.children)] : [];
}
@computed get placeholder() {
return (
- <div className="collectionfreeformview-placeholder" style={{ background: this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.props.Document.annotationOn ? '' : this.props.Document.title?.toString()}</span>
+ <div className="collectionfreeformview-placeholder" style={{ background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) }}>
+ <span className="collectionfreeformview-placeholderSpan">{this._props.Document.annotationOn ? '' : this._props.Document.title?.toString()}</span>
</div>
);
}
brushedView = () => this._brushedView;
gridColor = () =>
- DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)))
+ DashColor(lightOrDark(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor)))
.fade(0.5)
.toString();
@computed get backgroundGrid() {
return (
<div>
<CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render when taking snapshot of a dashboard and the background grid is on!!?
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
+ PanelWidth={this._props.PanelWidth}
+ PanelHeight={this._props.PanelHeight}
panX={this.panX}
panY={this.panY}
color={this.gridColor}
@@ -1784,15 +1792,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.incrementalRender();
return (
<CollectionFreeFormPannableContents
- rootDoc={this.rootDoc}
+ Document={this.Document}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.PanZoomCenterXf}
- transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
- viewDefDivClick={this.props.viewDefDivClick}>
+ transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this._props.DocumentView?.()?.Document._viewTransition, 'string', null))}
+ viewDefDivClick={this._props.viewDefDivClick}>
{this.underlayViews}
{this.contentViews}
- <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+ <CollectionFreeFormRemoteCursors {...this._props} key="remoteCursors" />
</CollectionFreeFormPannableContents>
);
}
@@ -1800,10 +1808,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
TraceMobx();
return (
<MarqueeView
- {...this.props}
+ {...this._props}
ref={this._marqueeViewRef}
- ungroup={this.rootDoc._isGroup ? this.promoteCollection : undefined}
- nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
+ ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
+ nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
trySelectCluster={this.trySelectCluster}
@@ -1811,25 +1819,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.props.ScreenToLocalTransform}
+ getContainerTransform={this._props.ScreenToLocalTransform}
getTransform={this.ScreenToLocalXf}
panXFieldKey={this.panXFieldKey}
panYFieldKey={this.panYFieldKey}
isAnnotationOverlay={this.isAnnotationOverlay}>
{this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
{this.pannableContents}
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : null}
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
</MarqueeView>
);
}
@computed get nativeDimScaling() {
- if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
+ if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 0;
const nw = this.nativeWidth;
const nh = this.nativeHeight;
- const hscale = nh ? this.props.PanelHeight() / nh : 1;
- const wscale = nw ? this.props.PanelWidth() / nw : 1;
- return wscale < hscale || this.layoutDoc.layout_fitWidth ? wscale : hscale;
+ const hscale = nh ? this._props.PanelHeight() / nh : 1;
+ const wscale = nw ? this._props.PanelWidth() / nw : 1;
+ return wscale < hscale || (this._props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
}
nativeDim = () => this.nativeDimScaling;
@@ -1846,13 +1854,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
transTime + 1
);
};
- lightboxPanelWidth = () => Math.max(0, this.props.PanelWidth() - 30);
- lightboxPanelHeight = () => Math.max(0, this.props.PanelHeight() - 30);
- lightboxScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-15, -15);
+ lightboxPanelWidth = () => Math.max(0, this._props.PanelWidth() - 30);
+ lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
+ lightboxScreenToLocal = () => this._props.ScreenToLocalTransform().translate(-15, -15);
onPassiveWheel = (e: WheelEvent) => {
- const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
- const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling;
- this.props.isSelected() && !scrollable && e.preventDefault();
+ const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
+ this._props.isSelected() && !scrollable && e.preventDefault();
};
_oldWheel: any;
render() {
@@ -1875,18 +1883,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onDragOver={e => e.preventDefault()}
onContextMenu={this.onContextMenu}
style={{
- pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any),
+ pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : (this._props.pointerEvents?.() as any),
textAlign: this.isAnnotationOverlay ? 'initial' : undefined,
transform: `scale(${this.nativeDimScaling || 1})`,
width: `${100 / (this.nativeDimScaling || 1)}%`,
- height: this.props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
+ height: this._props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
}}>
{this._lightboxDoc ? (
<div style={{ padding: 15, width: '100%', height: '100%' }}>
<DocumentView
- {...this.props}
+ {...this._props}
Document={this._lightboxDoc}
- DataDoc={undefined}
+ TemplateDataDocument={undefined}
PanelWidth={this.lightboxPanelWidth}
PanelHeight={this.lightboxPanelHeight}
NativeWidth={returnZero}
@@ -1898,8 +1906,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
- isContentActive={this.props.childContentsActive ?? emptyFunction}
+ isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive}
+ isContentActive={this._props.childContentsActive ?? emptyFunction}
addDocTab={this.addDocTab}
ScreenToLocalTransform={this.lightboxScreenToLocal}
fitContentsToBox={undefined}
@@ -1909,7 +1917,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
) : (
<>
{this._firstRender ? this.placeholder : this.marqueeView}
- {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
+ {this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
{!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />}
</>
)}
@@ -1929,16 +1937,16 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
const browseTransitionTime = 500;
SelectionManager.DeselectAll();
dv &&
- DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
+ DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
if (!focused) {
- const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
+ const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
let containers = dv.props.docViewPath();
let parFfview = dv.CollectionFreeFormView;
for (var cont of containers) {
parFfview = parFfview ?? cont.CollectionFreeFormView;
}
- while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
+ while (parFfview?.Document.isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
+ const ffview = selfFfview && selfFfview.layoutDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
ffview?.zoomSmoothlyAboutPt(ffview.screenToLocalXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
Doc.linkFollowHighlight(dv?.props.Document, false);
}
@@ -1946,68 +1954,31 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
}
ScriptingGlobals.add(CollectionBrowseClick);
ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+ !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
});
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+ !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
- const selView = SelectionManager.Views();
+ const selView = SelectionManager.Views;
if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
});
ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
- SelectionManager.Views().forEach(view =>
- view.props.pinToPres(view.rootDoc, {
- currentFrame: Cast(view.rootDoc.currentFrame, 'number', null),
+ SelectionManager.Views.forEach(view =>
+ view.props.pinToPres(view.Document, {
+ currentFrame: Cast(view.Document.currentFrame, 'number', null),
pinData: {
poslayoutview: pinContent,
dataview: pinContent,
},
- pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()),
+ pinViewport: MarqueeView.CurViewBounds(view.Document, view.props.PanelWidth(), view.props.PanelHeight()),
})
);
});
ScriptingGlobals.add(function bringToFront() {
- SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc));
+ SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
});
ScriptingGlobals.add(function sendToBack(doc: Doc) {
- SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc, true));
+ SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
});
-ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
- SelectionManager.Views().forEach(view => {
- if (!view.layoutDoc.schema_columnKeys){
- view.layoutDoc.schema_columnKeys = new List<string>(['title', 'type', 'author', 'author_date'])
- }
- const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key!="text");
- if (!keys) return;
-
- const children = DocListCast(view.rootDoc[Doc.LayoutFieldKey(view.rootDoc)]);
- let csvRows = [];
- csvRows.push(keys.join(','));
- for (let i=0; i < children.length; i++) {
- let eachRow = [];
- for (let j=0; j<keys.length; j++){
- var cell = children[i][keys[j]];
- if (cell && cell as string) cell = cell.toString().replace(/\,/g, '');
- eachRow.push(cell);
- }
- csvRows.push(eachRow);
- }
- const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' });
- const options = { x:0, y:-300, title: 'schemaTable', _width: 300, _height: 100, type: 'text/csv' };
- const file = new File([blob], 'schemaTable', options);
- const loading = Docs.Create.LoadingDocument(file, options);
- loading.presentation_openInLightbox = true;
- DocUtils.uploadFileToDoc(file, {}, loading);
-
- if (view.ComponentView?.addDocument) {
- // loading.dataViz_fromSchema = true;
- loading.dataViz_asSchema = view.layoutDoc;
- SchemaCSVPopUp.Instance.setView(view);
- SchemaCSVPopUp.Instance.setTarget(view.layoutDoc);
- SchemaCSVPopUp.Instance.setDataVizDoc(loading);
- SchemaCSVPopUp.Instance.setVisible(true);
- }
- })
-}); \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 607f9fb95..79cc534dc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,14 +1,11 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import { IconButton } from 'browndash-components';
+import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { unimplementedFunction } from '../../../../Utils';
-import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
-import { IconButton } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
-import { Doc } from '../../../../fields/Doc';
-import { computed } from 'mobx';
import { SettingsManager } from '../../../util/SettingsManager';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@observer
export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -21,9 +18,9 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public hideMarquee: () => void = unimplementedFunction;
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public isShown = () => this._opacity > 0;
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
-
+ makeObservable(this);
MarqueeOptionsMenu.Instance = this;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2092ecb7a..2c65726b2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,5 +1,7 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils, intersectRect, lightOrDark, returnFalse } from '../../../../Utils';
import { Doc, Opt } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
@@ -7,25 +9,24 @@ import { InkData, InkField, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
-import { ImageField, nullAudio } from '../../../../fields/URLField';
+import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
-import { intersectRect, lightOrDark, returnFalse, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
-import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocUtils, Docs, DocumentOptions } from '../../../documents/Documents';
import { SelectionManager } from '../../../util/SelectionManager';
+import { freeformScrollMode } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { PreviewCursor } from '../../PreviewCursor';
import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
-import { PreviewCursor } from '../../PreviewCursor';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { SubCollectionViewProps } from '../CollectionSubView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
-import React = require('react');
-import { freeformScrollMode } from '../../../util/SettingsManager';
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -51,12 +52,17 @@ export interface MarqueeViewBounds {
}
@observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
+export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps & MarqueeViewProps> {
public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
const ps = NumCast(pinDoc._freeform_scale, 1);
return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
}
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
private _commandExecuted = false;
@observable _lastX: number = 0;
@observable _lastY: number = 0;
@@ -67,7 +73,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@observable _lassoFreehand: boolean = false;
@computed get Transform() {
- return this.props.getTransform();
+ return this._props.getTransform();
}
@computed get Bounds() {
// nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
@@ -79,7 +85,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
componentDidMount() {
- this.props.setPreviewCursor?.(this.setPreviewCursor);
+ this._props.setPreviewCursor?.(this.setPreviewCursor);
}
@action
@@ -101,20 +107,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
if (e.key === '?') {
- cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
+ cm.setDefaultItem('?', (str: string) => this._props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
- } else if (e.key === 'u' && this.props.ungroup) {
+ } else if (e.key === 'u' && this._props.ungroup) {
e.stopPropagation();
- this.props.ungroup();
+ this._props.ungroup();
} else if (e.key === ':') {
- DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+ DocUtils.addDocumentCreatorMenuItems(this._props.addLiveTextDocument, this._props.addDocument || returnFalse, x, y);
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
} else if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
- this.props.selectDocuments(this.props.activeDocuments());
+ this._props.selectDocuments(this._props.activeDocuments());
e.stopPropagation();
} else if (e.key === 'q' && e.ctrlKey) {
e.preventDefault();
@@ -136,7 +142,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
ns.map(line => {
const indent = line.search(/\S|$/);
const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + (indent / 3) * 10, y: ypos, title: line });
- this.props.addDocument?.(newBox);
+ this._props.addDocument?.(newBox);
ypos += 40 * this.Transform.Scale;
});
})();
@@ -147,8 +153,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
pasteImageBitmap((data: any, error: any) => {
error && console.log(error);
data &&
- Utils.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
- this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
+ Utils.convertDataUri(data, this._props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+ this._props.Document['thumb-frozen'] = new ImageField(returnedfilename);
});
})
);
@@ -159,7 +165,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
slide.y = y;
FormattedTextBox.SelectOnLoad = slide[Id];
TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
- this.props.addDocument?.(slide);
+ this._props.addDocument?.(slide);
e.stopPropagation();
}*/ else if (e.key === 'p' && e.ctrlKey) {
e.preventDefault();
@@ -169,10 +175,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.pasteTable(ns, x, y);
})();
e.stopPropagation();
- } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
- FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+ } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) {
+ FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
- this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
+ this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this._props.xPadding === 0));
e.stopPropagation();
}
};
@@ -203,7 +209,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const file = new File([blob], 'droppedTable', options);
const loading = Docs.Create.LoadingDocument(file, options);
DocUtils.uploadFileToDoc(file, {}, loading);
- this.props.addDocument?.(loading);
+ this._props.addDocument?.(loading);
}
@action
@@ -214,9 +220,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
// allow marquee if right drag/meta drag, or pan mode
if (e.button === 2 || e.metaKey || scrollMode === freeformScrollMode.Pan) {
- this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
+ this.setPreviewCursor(e.clientX, e.clientY, true, false, this._props.Document);
e.preventDefault();
- } else PreviewCursor.Visible = false;
+ } else PreviewCursor.Instance.Visible = false;
};
@action
@@ -243,10 +249,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (this._visible) {
const mselect = this.marqueeSelect();
if (!e.shiftKey) {
- SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
+ SelectionManager.DeselectAll(mselect.length ? undefined : this._props.Document);
}
- const docs = mselect.length ? mselect : [this.props.Document];
- this.props.selectDocuments(docs);
+ const docs = mselect.length ? mselect : [this._props.Document];
+ this._props.selectDocuments(docs);
}
const hideMarquee = () => {
this.hideMarquee();
@@ -285,14 +291,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._downX = this._lastX = x;
this._downY = this._lastY = y;
this._commandExecuted = false;
- PreviewCursor.Visible = false;
- PreviewCursor.Doc = undefined;
+ PreviewCursor.Instance.Visible = false;
+ PreviewCursor.Instance.Doc = undefined;
} else if (drag) {
this._downX = this._lastX = x;
this._downY = this._lastY = y;
this._commandExecuted = false;
- PreviewCursor.Visible = false;
- PreviewCursor.Doc = undefined;
+ PreviewCursor.Instance.Visible = false;
+ PreviewCursor.Instance.Doc = undefined;
this.cleanupInteractions(true);
document.addEventListener('pointermove', this.onPointerMove, true);
document.addEventListener('pointerup', this.onPointerUp, true);
@@ -300,10 +306,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else {
this._downX = x;
this._downY = y;
- const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
+ const effectiveAcl = GetEffectiveAcl(this._props.Document[DocData]);
if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) {
- PreviewCursor.Doc = doc;
- PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge, this.props.slowLoadDocuments);
+ PreviewCursor.Instance.Doc = doc;
+ PreviewCursor.Show(x, y, this.onKeyPress, this._props.addLiveTextDocument, this._props.getTransform, this._props.addDocument, this._props.nudge, this._props.slowLoadDocuments);
}
this.clearSelection();
}
@@ -311,11 +317,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onClick = (e: React.MouseEvent): void => {
- if (this.props.pointerEvents?.() === 'none') return;
+ if (this._props.pointerEvents?.() === 'none') return;
if (Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
if (Doc.ActiveTool === InkTool.None) {
- if (!this.props.trySelectCluster(e.shiftKey)) {
- !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
+ if (!this._props.trySelectCluster(e.shiftKey)) {
+ !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document);
} else e.stopPropagation();
}
// let the DocumentView stopPropagation of this event when it selects this document
@@ -332,22 +338,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
hideMarquee = () => (this._visible = false);
@undoBatch
- @action
- delete = (e?: React.PointerEvent<Element> | KeyboardEvent | undefined, hide?: boolean) => {
+ delete = action((e?: React.PointerEvent<Element> | KeyboardEvent | undefined, hide?: boolean) => {
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
- selected.forEach(doc => (hide ? (doc.hidden = true) : this.props.removeDocument?.(doc)));
+ selected.forEach(doc => (hide ? (doc.hidden = true) : this._props.removeDocument?.(doc)));
this.cleanupInteractions(false);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
Doc.GetProto(doc).data = new List<Doc>(selected);
+ Doc.GetProto(doc).isGroup = makeGroup;
Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
@@ -355,7 +361,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
newCollection.isSystem = undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
- newCollection._isGroup = makeGroup;
newCollection._dragWhenActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
@@ -366,17 +371,16 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
});
@undoBatch
- @action
- pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ pileup = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
- selected.forEach(d => this.props.removeDocument?.(d));
+ selected.forEach(d => this._props.removeDocument?.(d));
const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2)!;
- this.props.addDocument?.(newCollection);
- this.props.selectDocuments([newCollection]);
+ this._props.addDocument?.(newCollection);
+ this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
/**
* This triggers the TabDocView.PinDoc method which is the universal method
@@ -385,35 +389,32 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
* This one is unique in that it includes the bounds associated with marquee view.
*/
@undoBatch
- @action
- pinWithView = () => {
- this.props.pinToPres(this.props.Document, { pinViewport: this.Bounds });
+ pinWithView = action(() => {
+ this._props.pinToPres(this._props.Document, { pinViewport: this.Bounds });
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
@undoBatch
- @action
- collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => {
+ collection = action((e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => {
const selected = selection ?? this.marqueeSelect(false);
const activeFrame = selected.reduce((v, d) => v ?? Cast(d._activeFrame, 'number', null), undefined as number | undefined);
if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
- this.props.removeDocument?.(selected);
+ this._props.removeDocument?.(selected);
}
const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
newCollection._freeform_panY = this.Bounds.top + this.Bounds.height / 2;
newCollection._currentFrame = activeFrame;
- this.props.addDocument?.(newCollection);
- this.props.selectDocuments([newCollection]);
+ this._props.addDocument?.(newCollection);
+ this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
@undoBatch
- @action
- syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ syntaxHighlight = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
const inks = selected.filter(s => s.type === DocumentType.INK);
@@ -472,15 +473,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
const lines = results.filter((r: any) => r.category === 'line');
const text = lines.map((l: any) => l.recognizedText).join('\r\n');
- this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
+ this._props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
});
}
- };
+ });
@undoBatch
summary = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false).map(d => {
- this.props.removeDocument?.(d);
+ this._props.removeDocument?.(d);
d.x = NumCast(d.x) - this.Bounds.left;
d.y = NumCast(d.y) - this.Bounds.top;
return d;
@@ -500,8 +501,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
portal.hidden = true;
- this.props.addDocument?.(portal);
- this.props.addLiveTextDocument(summary);
+ this._props.addDocument?.(portal);
+ this._props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
});
@@ -582,17 +583,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
(this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
}
};
- this.props
+ this._props
.activeDocuments()
.filter(doc => !doc.z && !doc._lockedPosition)
.map(selectFunc);
if (!selection.length && selectBackgrounds)
- this.props
+ this._props
.activeDocuments()
.filter(doc => doc.z === undefined)
.map(selectFunc);
if (!selection.length)
- this.props
+ this._props
.activeDocuments()
.filter(doc => doc.z !== undefined)
.map(selectFunc);
@@ -601,8 +602,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed get marqueeDiv() {
const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
- const p = this.props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
- const v = this._lassoFreehand ? [0, 0] : this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ const p = this._props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
+ const v = this._lassoFreehand ? [0, 0] : this._props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
return (
<div
className="marquee"
@@ -610,8 +611,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
transform: `translate(${p[0]}px, ${p[1]}px)`,
width: Math.abs(v[0]),
height: Math.abs(v[1]),
- color: lightOrDark(this.props.Document?.backgroundColor ?? 'white'),
- borderColor: lightOrDark(this.props.Document?.backgroundColor ?? 'white'),
+ color: lightOrDark(this._props.Document?.backgroundColor ?? 'white'),
+ borderColor: lightOrDark(this._props.Document?.backgroundColor ?? 'white'),
zIndex: 2000,
}}>
{' '}
@@ -620,7 +621,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
<polyline //
points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')}
fill="none"
- stroke={lightOrDark(this.props.Document?.backgroundColor ?? 'white')}
+ stroke={lightOrDark(this._props.Document?.backgroundColor ?? 'white')}
strokeWidth="1"
strokeDasharray="3"
/>
@@ -635,19 +636,19 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
- if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
+ if ((e as any).handlePan || this._props.isAnnotationOverlay) return;
(e as any).handlePan = true;
const bounds = this.MarqueeRef?.getBoundingClientRect();
- if (!this.props.Document._freeform_noAutoPan && !this.props.renderDepth && bounds) {
+ if (!this._props.Document._freeform_noAutoPan && !this._props.renderDepth && bounds) {
const dragX = e.detail.clientX;
const dragY = e.detail.clientY;
const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0;
const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0;
if (deltaX !== 0 || deltaY !== 0) {
- this.props.Document[this.props.panYFieldKey] = NumCast(this.props.Document[this.props.panYFieldKey]) + deltaY / 2;
- this.props.Document[this.props.panXFieldKey] = NumCast(this.props.Document[this.props.panXFieldKey]) + deltaX / 2;
+ this._props.Document[this._props.panYFieldKey] = NumCast(this._props.Document[this._props.panYFieldKey]) + deltaY / 2;
+ this._props.Document[this._props.panXFieldKey] = NumCast(this._props.Document[this._props.panXFieldKey]) + deltaX / 2;
}
}
e.stopPropagation();
@@ -661,7 +662,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.MarqueeRef = r;
}}
style={{
- overflow: StrCast(this.props.Document._overflow),
+ overflow: StrCast(this._props.Document._overflow),
cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}