From a8b19694ec902d4094914ba6ddd15e700fab117e Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 4 Feb 2023 21:04:09 -0500 Subject: image background color determines opacity of image (independently of annotations over image). enabled scrolling of images when in fitWidth mode. added disabling of text labels in ink shapes. --- src/client/views/nodes/ImageBox.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e2ecca0b6..bdd99528b 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -26,12 +26,13 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; +import { DocFocusOptions, DocumentViewProps } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; -import React = require('react'); import { PresBox } from './trails'; -import { DocFocusOptions, DocumentViewProps } from './DocumentView'; +import React = require('react'); +import Color = require('color'); export const pageSchema = createSchema({ googlePhotosUrl: 'string', @@ -345,8 +346,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent -
+
{fadepath === srcpath ? null : (
this.nativeSize.nativeHeight; @action finishMarquee = () => { this._getAnchor = AnchorMenu.Instance?.GetAnchor; @@ -434,6 +437,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent Date: Thu, 9 Feb 2023 21:15:58 -0500 Subject: fixed childLayoutString to work. made images capable of fitWidth. fixed animating data field pres changes. fixed lightbox to ignore annotations on collections. fixed double-click on icon to open in lightbox. added options for turning off ink labels, and opening ink in lightbox. fixed closing ink strokes by dragging. fixed drawing ink to use coord sys of starting point and to render ink the correct width and to honor GestureOverlay mode properly. . --- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 6 +- src/client/util/SettingsManager.tsx | 4 ++ src/client/views/DocumentDecorations.tsx | 13 ++-- src/client/views/GestureOverlay.tsx | 22 ++++--- src/client/views/GlobalKeyHandler.ts | 29 ++++++++- src/client/views/InkTranscription.tsx | 2 +- src/client/views/InkingStroke.tsx | 12 ++-- src/client/views/LightboxView.tsx | 64 +++++++++---------- src/client/views/PropertiesButtons.tsx | 10 +++ src/client/views/PropertiesView.tsx | 2 +- src/client/views/StyleProvider.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 14 ++--- src/client/views/collections/TreeView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 66 +++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 21 ++++--- src/client/views/nodes/ImageBox.tsx | 73 +++++++++++++++++----- src/client/views/nodes/LinkAnchorBox.tsx | 1 + src/client/views/nodes/MapBox/MapBox.tsx | 4 +- src/client/views/nodes/WebBox.tsx | 11 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/client/views/nodes/trails/PresBox.tsx | 4 ++ src/client/views/pdf/AnchorMenu.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 17 ++--- src/fields/util.ts | 2 +- 27 files changed, 257 insertions(+), 140 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 692d09629..3faa6e11d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -149,6 +149,8 @@ export class DocumentOptions { _height?: NUMt = new NumInfo('displayed height of document'); _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)'); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)'); + _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers'); + _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers'); _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); @@ -848,7 +850,7 @@ export namespace Docs { export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) { const imgField = url instanceof ImageField ? url : new ImageField(url); - return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc); + return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { _nativeDimModifiable: false, _nativeHeightUnfrozen: false, title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc); } export function PresDocument(options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5f183cf91..f678c8936 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -183,7 +183,7 @@ export class CurrentUserUtils { const allopts = {system: true, ...opts}; return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), - {onClick:"deiconifyView(documentView)"}); + {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView"}); }; const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts @@ -803,7 +803,9 @@ export class CurrentUserUtils { doc._showLabel ?? (doc._showLabel = true); doc.textAlign ?? (doc.textAlign = "left"); doc.activeTool = InkTool.None; - doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");; + doc.openInkInLightbox ?? (doc.openInkInLightbox = false); + doc.activeInkHideTextLabels ?? (doc.activeInkHideTextLabels = false); + doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)"); doc.activeInkWidth ?? (doc.activeInkWidth = 1); doc.activeInkBezier ?? (doc.activeInkBezier = "0"); doc.activeFillColor ?? (doc.activeFillColor = ""); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 179a1ac39..6c823e80a 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -199,6 +199,10 @@ export class SettingsManager extends React.Component<{}> { (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} checked={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} />
Hide Labels In Ink Shapes
+
+ (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} checked={BoolCast(Doc.UserDoc().openInkInLightbox)} /> +
Open Ink Docs in Lightbox
+
); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d1f0bf2ac..41f4a17fb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -581,7 +581,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } let actualdW = Math.max(width + dW * scale, 20); let actualdH = Math.max(height + dH * scale, 20); - const fixedAspect = nwidth && nheight && (!doc._fitWidth || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); + const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false; + const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { @@ -589,7 +590,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P doc._nativeWidth = (actualdW / (doc._width || 1)) * Doc.NativeWidth(doc); } } else { - if (!doc._fitWidth) { + if (!doc._fitWidth || preserveNativeDim) { actualdH = (nheight / nwidth) * actualdW; doc._height = actualdH; } else if (!modifyNativeDim || dragBotRight) doc._height = actualdH; @@ -597,11 +598,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P doc._width = actualdW; } else { if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { - // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) + // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight + // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match + // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) doc._nativeHeight = (actualdH / (doc._height || 1)) * Doc.NativeHeight(doc); doc._autoHeight = false; } else { - if (!doc._fitWidth) { + if (!doc._fitWidth || preserveNativeDim) { actualdW = (nwidth / nheight) * actualdH; doc._width = actualdW; } else if (!modifyNativeDim || dragBotRight) doc._width = actualdW; @@ -615,7 +618,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const rotCtr = [NumCast(doc._width) / 2, NumCast(doc._height) / 2]; const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI); - const maxHeight = doc.nativHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); + const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH); dW && (doc._width = actualdW); dH && (doc._autoHeight = false); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index e3328fb4c..6058eaaf9 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,9 +1,9 @@ import React = require('react'); import * as fitCurve from 'fit-curve'; -import { action, computed, observable, runInAction, trace } from 'mobx'; +import { action, computed, observable, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; import { Doc, Opt } from '../../fields/Doc'; import { InkData, InkTool } from '../../fields/InkField'; -import { List } from '../../fields/List'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, FieldValue, NumCast } from '../../fields/Types'; import MobileInkOverlay from '../../mobile/MobileInkOverlay'; @@ -14,7 +14,6 @@ import { CognitiveServices } from '../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../documents/Documents'; import { InteractionUtils } from '../util/InteractionUtils'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SelectionManager } from '../util/SelectionManager'; import { Transform } from '../util/Transform'; import './GestureOverlay.scss'; import { @@ -39,7 +38,6 @@ import { RadialMenu } from './nodes/RadialMenu'; import HorizontalPalette from './Palette'; import { Touchable } from './Touchable'; import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu'; -import { observer } from 'mobx-react'; interface GestureOverlayProps { isActive: boolean; @@ -47,6 +45,7 @@ interface GestureOverlayProps { @observer export class GestureOverlay extends Touchable { static Instance: GestureOverlay; + static Instances: GestureOverlay[] = []; @observable public InkShape: Opt; @observable public SavedColor?: string; @@ -66,6 +65,8 @@ export class GestureOverlay extends Touchable { @observable private _clipboardDoc?: JSX.Element; @observable private _possibilities: JSX.Element[] = []; + public static DownDocView: DocumentView | undefined; + @computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100); } @@ -89,7 +90,7 @@ export class GestureOverlay extends Touchable { constructor(props: any) { super(props); - GestureOverlay.Instance = this; + GestureOverlay.Instances.push(this); } static setupThumbButtons(doc: Doc) { @@ -154,7 +155,13 @@ export class GestureOverlay extends Touchable { } return Cast(userDoc.thumbDoc, Doc); } + + componentWillUnmount() { + GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1); + GestureOverlay.Instance = GestureOverlay.Instances.lastElement(); + } componentDidMount = () => { + GestureOverlay.Instance = this; this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc)); this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc)); }; @@ -627,6 +634,7 @@ export class GestureOverlay extends Touchable { } @action onPointerUp = (e: PointerEvent) => { + GestureOverlay.DownDocView = undefined; if (this._points.length > 1) { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); @@ -906,8 +914,8 @@ export class GestureOverlay extends Touchable { } @computed get elements() { - const selView = SelectionManager.Views().lastElement(); - const width = (Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1)) / (selView?.props.ScreenToLocalTransform().Scale || 1); + const selView = GestureOverlay.DownDocView; + const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1); const rect = this._overlayRef.current?.getBoundingClientRect(); const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true); B.left = B.left - width / 2; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 5e700e281..6c8a078ec 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -1,12 +1,12 @@ import { random } from 'lodash'; -import { action, observable, runInAction } from 'mobx'; +import { action, runInAction } from 'mobx'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; import { List } from '../../fields/List'; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, DocCast, PromiseValue } from '../../fields/Types'; +import { Cast, PromiseValue } from '../../fields/Types'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { DocumentType } from '../documents/DocumentTypes'; @@ -18,6 +18,7 @@ import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { CollectionFreeFormViewChrome } from './collections/CollectionMenu'; import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { ContextMenu } from './ContextMenu'; @@ -103,6 +104,20 @@ export class KeyManager { const groupings = SelectionManager.Views().slice(); const randomGroup = random(0, 1000); + const collectionView = groupings.reduce( + (col, g) => (col === null || g.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView === col ? g.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView : undefined), + null as null | undefined | CollectionFreeFormView + ); + if (collectionView) { + UndoManager.RunInBatch(() => { + collectionView._marqueeViewRef.current?.collection( + e, + true, + groupings.map(g => g.rootDoc) + ); + }, 'grouping'); + break; + } UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group'); SelectionManager.DeselectAll(); break; @@ -192,6 +207,16 @@ export class KeyManager { case 'arrowdown': UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), 'nudge down'); break; + case 'g': + if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') { + return { stopPropagation: false, preventDefault: false }; + } + + const groupings = SelectionManager.Views().slice(); + const randomGroup = random(0, 1000); + UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group'); + SelectionManager.DeselectAll(); + break; } return { diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index bf0e8081d..246b887a6 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -67,7 +67,7 @@ export class InkTranscription extends React.Component { : null; } - r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef)); + r?.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef)); return (this._mathRef = r); }; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 7a5151634..d7e8b1c05 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -54,7 +54,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { return FieldView.LayoutString(InkingStroke, fieldStr); } public static IsClosed(inkData: InkData) { - return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y; + return inkData?.length && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y; } private _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated private _disposers: { [key: string]: IReactionDisposer } = {}; @@ -264,9 +264,13 @@ export class InkingStroke extends ViewBoxBaseComponent() { .map(p => ({ X: p[0], Y: p[1] })); const { distance, nearestT, nearestSeg, nearestPt } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY }); - this._nearestT = nearestT; - this._nearestSeg = nearestSeg; - this._nearestScrPt = nearestPt; + if (distance < 40) { + this._nearestT = nearestT; + this._nearestSeg = nearestSeg; + this._nearestScrPt = nearestPt; + } else { + this._nearestT = this._nearestSeg = this._nearestScrPt = undefined; + } }; /** diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 3627aa783..e531bf71c 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -27,6 +27,13 @@ interface LightboxViewProps { maxBorder: number[]; } +type LightboxSavedState = { + panX: Opt; + panY: Opt; + scale: Opt; + scrollTop: Opt; + layoutKey: Opt; +}; @observer export class LightboxView extends React.Component { @computed public static get LightboxDoc() { @@ -34,21 +41,22 @@ export class LightboxView extends React.Component { } private static LightboxDocTemplate = () => LightboxView._layoutTemplate; @observable private static _layoutTemplate: Opt; + @observable private static _layoutTemplateString: Opt; @observable private static _doc: Opt; @observable private static _docTarget: Opt; @observable private static _docFilters: string[] = []; // filters - private static _savedState: Opt<{ panX: Opt; panY: Opt; scale: Opt; scrollTop: Opt }>; + private static _savedState: Opt; private static _history: Opt<{ doc: Doc; target?: Doc }[]> = []; @observable private static _future: Opt = []; @observable private static _docView: Opt; - private static openInTabFunc: any; - static path: { doc: Opt; target: Opt; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt; saved: Opt<{ panX: Opt; panY: Opt; scale: Opt; scrollTop: Opt }> }[] = []; - @action public static SetLightboxDoc(doc: Opt, target?: Doc, future?: Doc[], layoutTemplate?: Doc) { + static path: { doc: Opt; target: Opt; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt; saved: Opt }[] = []; + @action public static SetLightboxDoc(doc: Opt, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) { if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) { - this.LightboxDoc._panX = this._savedState.panX; - this.LightboxDoc._panY = this._savedState.panY; - this.LightboxDoc._scrollTop = this._savedState.scrollTop; - this.LightboxDoc._viewScale = this._savedState.scale; + if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX; + if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY; + if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop; + if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale; + this.LightboxDoc.layoutKey = this._savedState.layoutKey; } if (!doc) { this._docFilters && (this._docFilters.length = 0); @@ -69,10 +77,11 @@ export class LightboxView extends React.Component { this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { this._savedState = { - panX: Cast(doc._panX, 'number', null), - panY: Cast(doc._panY, 'number', null), - scale: Cast(doc._viewScale, 'number', null), - scrollTop: Cast(doc._scrollTop, 'number', null), + layoutKey: StrCast(doc.layoutKey), + panX: Cast(doc.panX, 'number', null), + panY: Cast(doc.panY, 'number', null), + scale: Cast(doc.viewScale, 'number', null), + scrollTop: Cast(doc.scrollTop, 'number', null), }; } } @@ -87,7 +96,10 @@ export class LightboxView extends React.Component { ]; } this._doc = doc; - this._layoutTemplate = layoutTemplate; + this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined; + if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) { + doc.layoutKey = layoutTemplate; + } this._docTarget = target || doc; return true; @@ -132,7 +144,7 @@ export class LightboxView extends React.Component { this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); } } - public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc, openInTabFunc?: any) => { + public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => { if (location !== OpenWhere.lightbox) { const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; if (inPlaceView) { @@ -140,12 +152,13 @@ export class LightboxView extends React.Component { return true; } } - LightboxView.openInTabFunc = openInTabFunc; SelectionManager.DeselectAll(); return LightboxView.SetLightboxDoc( doc, undefined, - [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']), ...(LightboxView._future ?? [])].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)), + [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort( + (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow) + ), layoutTemplate ); }; @@ -193,8 +206,7 @@ export class LightboxView extends React.Component { const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); if (docView) { LightboxView._docTarget = target; - if (!target) docView.ComponentView?.shrinkWrap?.(); - else docView.focus(target, { willPanZoom: true, zoomScale: 0.9 }); + target && docView.focus(target, { willPanZoom: true, zoomScale: 0.9 }); } else { LightboxView.SetLightboxDoc(doc, target); } @@ -218,7 +230,6 @@ export class LightboxView extends React.Component { .filter(doc => doc) .map(doc => doc!); LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links); - TabDocView.PinDoc(coll, { hidePresBox: true }); } }; @@ -251,19 +262,7 @@ export class LightboxView extends React.Component { { - LightboxView._docView = r !== null ? r : undefined; - r && - setTimeout( - action(() => { - const target = LightboxView._docTarget; - const doc = LightboxView._doc; - //const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); - //if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); - //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button - }) - ); - })} + ref={action((r: DocumentView | null) => (LightboxView._docView = r !== null ? r : undefined))} Document={LightboxView.LightboxDoc} DataDoc={undefined} PanelWidth={this.lightboxWidth} @@ -332,7 +331,6 @@ export class LightboxView extends React.Component { onClick={e => { e.stopPropagation(); CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none); - //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace"); SelectionManager.DeselectAll(); LightboxView.SetLightboxDoc(undefined); }}> diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 66c3ed439..65a950711 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -77,6 +77,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc) ); } + @computed get hideImageButton() { + return this.propertyToggleBtn( + 'Background', + '_hideImage', + on => (on ? 'Show Image' : 'Show Background'), + on => 'portrait' + ); + } @computed get clustersButton() { return this.propertyToggleBtn( 'Clusters', @@ -383,6 +391,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)]; const isText = layoutField instanceof RichTextField; const isInk = layoutField instanceof InkField; + const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; const isCollection = this.selectedDoc?.type === DocumentType.COL; //TODO: will likely need to create separate note-taking view type here @@ -410,6 +419,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.inPlaceContainerButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} + {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })} {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 411f51d84..a2bc37095 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -143,7 +143,7 @@ export class PropertiesView extends React.Component { : layoutDoc._fitWidth ? !Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) - : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height)) + : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height)) : NumCast(layoutDoc._height) || 50 ) ); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index ece224c68..3cb920ba0 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -269,9 +269,9 @@ export function DefaultStyleProvider(doc: Opt, props: Opt this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); @computed get childLayoutString() { - return StrCast(this.rootDoc.childLayoutString); + return StrCast(this.rootDoc.childLayoutString, this.props.childLayoutString); } isContentActive = (outsideReaction?: boolean) => this.props.isContentActive(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 25fccd89c..bf8d449ea 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -274,7 +274,7 @@ export class TabDocView extends React.Component { pinDoc.presStartTime = NumCast(doc.clipStart); pinDoc.presEndTime = NumCast(doc.clipEnd, duration); } - PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, pinDoc); + PresBox.pinDocView(pinDoc, pinProps.pinDocContent ? { ...pinProps, pinData: PresBox.pinDataTypes(doc) } : pinProps, DocCast(pinDoc.proto)); pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)'); Doc.AddDocToList(curPres, 'data', pinDoc, presSelected); //save position @@ -359,7 +359,7 @@ export class TabDocView extends React.Component { // prettier-ignore switch (whereFields[0]) { case OpenWhere.inPlace: // fall through to lightbox - case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); + case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods); @@ -382,14 +382,8 @@ export class TabDocView extends React.Component { }; @action focusFunc = (doc: Doc, options: DocFocusOptions) => { - const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; - if (options?.willPanZoom !== false && shrinkwrap && this._document) { - const focusSpeed = options.zoomTime ?? 500; - shrinkwrap(); - this._view?.setViewTransition('transform', focusSpeed, () => options?.afterFocus?.(false)); - } else { - options?.afterFocus?.(false); - } + options?.afterFocus?.(false); + if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index bd326f917..2398d8f58 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -410,7 +410,7 @@ export class TreeView extends React.Component { if (aspect) return this.embeddedPanelWidth() / (aspect || 1); return layoutDoc._fitWidth ? !Doc.NativeHeight(layoutDoc) - ? NumCast(layoutDoc._height) //this.props.containerCollection._height) + ? NumCast(layoutDoc._height) : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))) : (this.embeddedPanelWidth() * layoutDoc[HeightSym]()) / layoutDoc[WidthSym](); })() @@ -957,6 +957,7 @@ export class TreeView extends React.Component { ); }; + fitWidthFilter = (doc: Doc) => (doc.type === DocumentType.IMG ? false : undefined); renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => { return (
@@ -965,6 +966,7 @@ export class TreeView extends React.Component { ref={action((r: DocumentView | null) => (this._dref = r))} Document={this.doc} DataDoc={undefined} + fitWidth={this.fitWidthFilter} PanelWidth={this.embeddedPanelWidth} PanelHeight={this.embeddedPanelHeight} LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 51672578e..d6e95f97f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnTransparent, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -61,6 +61,7 @@ export type collectionFreeformViewProps = { scaleField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; + getScrollHeight?: () => number | undefined; dontScaleFilter?: (doc: Doc) => boolean; // whether this collection should scale documents to fit their panel vs just scrolling them dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not. // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents. @@ -363,10 +364,10 @@ export class CollectionFreeFormView extends CollectionSubView 20) { deltaScale = 20 / invTransform.Scale; } + if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) { + return; + } if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) { deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale; } @@ -1012,29 +1017,26 @@ export class CollectionFreeFormView extends CollectionSubView= 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, -localTransform.TranslateY / safeScale); + this.setPan(-localTransform.TranslateX / safeScale, NumCast(this.props.Document.scrollTop) * safeScale || -localTransform.TranslateY / safeScale); } }; @action onPointerWheel = (e: React.WheelEvent): void => { + if (this.Document._isGroup) return; // group style collections neither pan nor zoom PresBox.Instance?.pauseAutoPres(); if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; e.stopPropagation(); e.preventDefault(); switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) { case freeformScrollMode.Pan: - // if shift is selected then zoom + // if ctrl is selected then zoom if (e.ctrlKey) { - if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { - // things that can scroll vertically should do that instead of zooming - } else if (this.props.isContentActive(true) && !this.Document._isGroup) { + if (this.props.isContentActive(true)) { !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc? } - // otherwise pan - } else if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { - // things that can scroll vertically should do that instead of zooming - } else if (this.props.isContentActive(true) && !this.Document._isGroup) { + } // otherwise pan + else if (this.props.isContentActive(true)) { const dx = -e.deltaX; const dy = -e.deltaY; if (e.shiftKey) { @@ -1046,9 +1048,7 @@ export class CollectionFreeFormView extends CollectionSubView { + this.rootDoc.scrollTop = relTop * maxScrollTop; + }, 10); + newPanY = minPanY; + } !this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX); !this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY); } @@ -1169,7 +1188,7 @@ export class CollectionFreeFormView extends CollectionSubView {this._firstRender ? this.placeholder : this.marqueeView} {this.props.noOverlay ? null : } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 0b7854926..bc3b17cd9 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -431,8 +431,8 @@ export class MarqueeView extends React.Component { - const selected = this.marqueeSelect(false); + collection = (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); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b94db2c6b..36c0240f1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -56,6 +56,7 @@ import { ScriptingBox } from './ScriptingBox'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps } from './trails/PresBox'; import React = require('react'); +import { GestureOverlay } from '../GestureOverlay'; const { Howl } = require('howler'); interface Window { @@ -139,7 +140,6 @@ export interface DocComponentView { fieldKey?: string; annotationKey?: string; getTitle?: () => string; - getScrollHeight?: () => number; getCenter?: (xf: Transform) => { X: number; Y: number }; ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; @@ -170,7 +170,7 @@ export interface DocumentViewSharedProps { dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt styleProvider: Opt; focus: DocFocusFunc; - fitWidth?: (doc: Doc) => boolean; + fitWidth?: (doc: Doc) => boolean | undefined; docFilters: () => string[]; docRangeFilters: () => string[]; searchFilterDocs: () => Doc[]; @@ -637,8 +637,8 @@ export class DocumentViewInternal extends DocComponent (func().result?.select === true ? this.props.select(false) : ''), 'on double click'); - } else if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isLinkButton) { - UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap'); + } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) { + UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap'); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } @@ -669,7 +669,7 @@ export class DocumentViewInternal extends DocComponent { this._timeout = undefined; clickFunc(); - }, 350); + }, 150); } else clickFunc(); } else if (!this._longPress && this.allLinks.length && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { SelectionManager.DeselectAll(); @@ -698,6 +698,7 @@ export class DocumentViewInternal extends DocComponent { + if (!(e.nativeEvent as any).DownDocView) (e.nativeEvent as any).DownDocView = GestureOverlay.DownDocView = this.props.DocumentView(); if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return; // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document) if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool))) { @@ -1640,7 +1641,7 @@ export class DocumentView extends React.Component { return this.docView?.LayoutFieldKey || 'layout'; } get fitWidth() { - return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; + return this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth; } @computed get hideLinkButton() { @@ -1688,8 +1689,7 @@ export class DocumentView extends React.Component { } @computed get panelHeight() { if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) { - const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0; - return Math.min(this.props.PanelHeight(), Math.max(scrollHeight, this.effectiveNativeHeight) * this.nativeScaling); + return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling); } return this.props.PanelHeight(); } @@ -1902,6 +1902,11 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { documentView.select(false); }); +ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) { + //documentView.iconify(() => + LightboxView.AddDocTab(documentView.rootDoc, OpenWhere.lightbox, 'layout'); //, 0); +}); + ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout'); else dv.switchViews(true, detailLayoutKeySuffix, undefined, true); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index bdd99528b..540958941 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -11,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; +import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -26,13 +26,15 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, DocumentViewProps } from './DocumentView'; +import { DocFocusOptions, OpenWhere } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; import { PresBox } from './trails'; import React = require('react'); import Color = require('color'); +import { LinkDocPreview } from './LinkDocPreview'; +import { DocumentManager } from '../../util/DocumentManager'; export const pageSchema = createSchema({ googlePhotosUrl: 'string', @@ -51,6 +53,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent>, addAsAnnotation: boolean) => Opt = () => undefined; @@ -119,6 +123,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.layoutDoc._scrollTop, + s_top => { + this._forcedScroll = true; + !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top)); + this._mainCont.current?.scrollTo({ top: NumCast(s_top) }); + this._forcedScroll = false; + }, + { fireImmediately: true } + ); } componentWillUnmount() { @@ -155,6 +169,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']); + @undoBatch + setNativeSize = action(() => { + const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1); + const nscale = NumCast(this.props.PanelWidth()) / scaling; + const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']); + const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']); + this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh; + this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nw; + this.rootDoc._panX = nh * NumCast(this.rootDoc._panX); + this.rootDoc._panY = nw * NumCast(this.rootDoc._panY); + this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined; + this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined; + this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined; + this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined; + }); @undoBatch rotate = action(() => { const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']); @@ -189,6 +218,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); this.props.bringToFront(cropping); return cropping; }; @@ -216,6 +250,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(this.choosePath(field.url)), icon: 'expand-arrows-alt' }); if (!Doc.noviceMode) { @@ -282,6 +317,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent; }; + getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined); + @computed private get considerDownloadIcon() { const data = this.dataDoc[this.fieldKey]; @@ -346,9 +383,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent -
+
{fadepath === srcpath ? null : (
[this.content]; private _mainCont: React.RefObject = React.createRef(); @@ -394,6 +430,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent; } + screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale); marqueeDown = (e: React.PointerEvent) => { if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { setupMoveUpEvents( @@ -410,7 +447,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.nativeSize.nativeHeight; @action finishMarquee = () => { this._getAnchor = AnchorMenu.Instance?.GetAnchor; @@ -418,11 +454,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent this._savedAnnotations; - styleProvider = (doc: Opt, props: Opt, property: string): any => { - if (property === StyleProp.BoxShadow) return undefined; - return this.props.styleProvider?.(doc, props, property); - }; - render() { TraceMobx(); const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); @@ -432,25 +463,35 @@ export class ImageBox extends ViewBoxAnnotatableComponent { + if (!this._forcedScroll) { + if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) { + this._ignoreScroll = true; + this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop; + this._ignoreScroll = false; + } + } + })} style={{ width: this.props.PanelWidth() ? undefined : `100%`, height: this.props.PanelWidth() ? undefined : `100%`, pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined, borderRadius, - overflow: 'auto', + overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined, }}> () { } else { this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100; this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100; + this.rootDoc.linkAutoMove = false; } } return false; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 95cb49037..5940fc075 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -542,8 +542,8 @@ export class MapBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); - panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); + panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); + panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index d0d638e98..c4cca9679 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -295,12 +295,12 @@ export class WebBox extends ViewBoxAnnotatableComponent this._scrollHeight; - isFirefox = () => { return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1; }; @@ -969,8 +966,8 @@ export class WebBox extends ViewBoxAnnotatableComponent) => (this._searchString = e.currentTarget.value); showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); - panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); - panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); + panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; + panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 619c59f0e..80b18b8b9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -994,7 +994,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.scrollHeight; // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc. // Since we also monitor all component height changes, this will update the document's height. resetNativeHeight = (scrollHeight: number) => { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 427911bd3..0a96297b7 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -35,6 +35,7 @@ import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; +import { PresElementBox } from './PresElementBox'; const { Howl } = require('howler'); export interface PinProps { @@ -383,9 +384,11 @@ export class PresBox extends ViewBoxBaseComponent() { Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]); } if (pinDataTypes.dataview && activeItem.presData !== undefined) { + bestTarget._dataTransition = `all ${transTime}ms`; const fkey = Doc.LayoutFieldKey(bestTarget); Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt; + setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); } if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; if (pinDataTypes.poslayoutview) { @@ -2244,6 +2247,7 @@ export class PresBox extends ViewBoxBaseComponent() { ignoreUnrendered={true} //childFitWidth={returnTrue} childOpacity={returnOne} + //childLayoutString={PresElementBox.LayoutString('data')} childLayoutTemplate={this.childLayoutTemplate} childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined} filterAddDocument={this.addDocumentFilter} diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index c53cc608c..4a7f5d038 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -11,6 +11,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu'; import './AnchorMenu.scss'; +import { LightboxView } from '../LightboxView'; @observer export class AnchorMenu extends AntimodeMenu { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b0b7816b8..9610a71ac 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -144,6 +144,8 @@ export class PDFViewer extends React.Component { } }; + @observable _scrollHeight = 0; + @action initialLoad = async () => { if (this._pageSizes.length === 0) { @@ -164,8 +166,8 @@ export class PDFViewer extends React.Component { ) ) ); - this.props.Document.scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72; } + runInAction(() => (this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72)); }; _scrollStopper: undefined | (() => void); @@ -177,7 +179,7 @@ export class PDFViewer extends React.Component { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight)); + const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, this._scrollHeight); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); @@ -453,10 +455,6 @@ export class PDFViewer extends React.Component { } }; - scrollXf = () => { - return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform(); - }; - onClick = (e: React.MouseEvent) => { this._scrollStopper?.(); if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { @@ -504,10 +502,12 @@ export class PDFViewer extends React.Component { ); } + getScrollHeight = () => this._scrollHeight; showInfo = action((anno: Opt) => (this._overlayAnnoInfo = anno)); + scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform()); overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1)); - panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); - panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); + panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); + panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { @@ -533,6 +533,7 @@ export class PDFViewer extends React.Component { isAnnotationOverlay={true} fieldKey={this.props.fieldKey + '-annotations'} CollectionView={undefined} + getScrollHeight={this.getScrollHeight} setPreviewCursor={this.setPreviewCursor} setBrushViewer={this.setBrushViewer} PanelHeight={this.panelHeight} diff --git a/src/fields/util.ts b/src/fields/util.ts index dc0b41276..3a7484cfd 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -350,7 +350,7 @@ export function getter(target: any, prop: string | symbol, proxy: any): any { return target[prop]; case AclSym : return target[AclSym]; case $mobx: return target.__fields[prop]; - case LayoutSym: return target.__Layout__; + case LayoutSym: return target.__LAYOUT__; case HeightSym: case WidthSym: if (GetEffectiveAcl(target) === AclPrivate) return returnZero; default : if (typeof prop === 'symbol') return target[prop]; -- cgit v1.2.3-70-g09d2 From f303131b40fac4909ab2730b369ef3bdb8e00b1d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 21 Feb 2023 09:53:18 -0500 Subject: fixed explore mode zooming. don't show doc decorations until you move outside of the document, fixed dragging radius button over webbox to still get pointer events. fixed selection text in coments for web boxes.. fixed setting default background color for notes. fixed mode buttons to trigger click behavior before double click behavior. fixed events on nested text boxes that are linkAnchors (like text quotes in sidebar comments), --- src/Utils.ts | 9 +++--- src/client/documents/Documents.ts | 5 ++-- src/client/util/CurrentUserUtils.ts | 7 +++-- src/client/views/DocumentDecorations.tsx | 20 +++++++------ src/client/views/Main.tsx | 5 ++-- src/client/views/MarqueeAnnotator.tsx | 2 +- src/client/views/SidebarAnnos.tsx | 25 ++++++++-------- src/client/views/StyleProvider.tsx | 3 +- .../views/collections/CollectionStackingView.tsx | 8 ++++-- .../views/collections/CollectionTreeView.tsx | 5 +++- src/client/views/collections/TreeView.tsx | 33 +++++++++++++++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +++- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/ImageBox.tsx | 4 +-- src/client/views/nodes/WebBox.tsx | 11 ++++++-- src/client/views/nodes/button/FontIconBox.tsx | 15 +++------- .../views/nodes/formattedText/DashFieldView.tsx | 22 +++++++++++++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 22 ++++++--------- .../views/nodes/formattedText/RichTextMenu.tsx | 21 ++++++++------ src/client/views/topbar/TopBar.tsx | 2 +- 20 files changed, 145 insertions(+), 86 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 9d3b9eb2b..22c8cb902 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -686,8 +686,9 @@ export function DashColor(color: string) { try { return color ? Color(color.toLowerCase()) : Color('transparent'); } catch (e) { - console.log('COLOR error:', e); - return Color('red'); + if (color.includes('gradient')) console.log("using color 'white' in place of :" + color); + else console.log('COLOR error:', e); + return Color('white'); } } @@ -816,7 +817,7 @@ export function setupMoveUpEvents( (target as any)._noClick = clickEvent(e, (target as any)._doubleTap); } document.removeEventListener('pointermove', _moveEvent); - document.removeEventListener('pointerup', _upEvent); + document.removeEventListener('pointerup', _upEvent, true); }; const _clickEvent = (e: MouseEvent): void => { if ((target as any)._noClick) e.stopPropagation(); @@ -827,6 +828,6 @@ export function setupMoveUpEvents( e.preventDefault(); } document.addEventListener('pointermove', _moveEvent); - document.addEventListener('pointerup', _upEvent); + document.addEventListener('pointerup', _upEvent, true); document.addEventListener('click', _clickEvent, true); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3faa6e11d..debb11066 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -224,6 +224,7 @@ export class DocumentOptions { contextMenuScripts?: List; contextMenuLabels?: List; contextMenuIcons?: List; + allowClickBeforeDoubleClick?: boolean; // whether a click function can fire before the timeout for a double click has expired dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) description?: string; // added for links layout?: string | Doc; // default layout string for a document @@ -583,7 +584,7 @@ export namespace Docs { DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' }, + options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' }, }, ], [ @@ -1808,7 +1809,7 @@ export namespace DocUtils { _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, - backgroundColor: backgroundColor, + backgroundColor, _width: width || 200, _height: 35, x: x, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f678c8936..3d95cb947 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -612,6 +612,7 @@ export class CurrentUserUtils { btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_);}'}}, + { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scripts: {script: '{ return setFontHighlight(value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} }, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} }, @@ -635,9 +636,9 @@ export class CurrentUserUtils { { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts: {onClick:'{ return setActiveTool("write", false, _readOnly_);}'} }, { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts: {onClick:'{ return setActiveTool("eraser", false, _readOnly_);}' }}, // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, - { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", true, _readOnly_);}`} }, - { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", true, _readOnly_);}`} }, - { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", true, _readOnly_);}`} }, + { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Circle}", true, _readOnly_);}`} }, + { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Rectangle}", true, _readOnly_);}`} }, + { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool("${GestureUtils.Gestures.Line}", true, _readOnly_);}`} }, { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", scripts: {onClick:'{ return setIsInkMask(_readOnly_);}'} }, { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: '{ return setFillColor(value, _readOnly_);}'} }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 41f4a17fb..21f63ada4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { IconButton } from 'browndash-components'; -import { action, computed, observable, reaction } from 'mobx'; +import { action, computed, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { FaUndo } from 'react-icons/fa'; import { DateField } from '../../fields/DateField'; @@ -182,7 +182,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P dragData.removeDocument = dragDocView.props.removeDocument; dragData.isDocDecorationMove = true; dragData.canEmbed = dragTitle; - this._hidden = this.Interacting = true; + this._hidden = true; DragManager.StartDocumentDrag( SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, @@ -191,7 +191,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P { dragComplete: action(e => { dragData.canEmbed && SelectionManager.DeselectAll(); - this._hidden = this.Interacting = false; + this._hidden = false; }), hideSource: true, } @@ -293,7 +293,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P */ @action onRadiusDown = (e: React.PointerEvent): void => { - this._isRounding = true; + this._isRounding = DocumentDecorations.Instance.Interacting = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); // Call util move event function setupMoveUpEvents( @@ -316,10 +316,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return false; }, // moveEvent action(e => { - this._isRounding = false; + DocumentDecorations.Instance.Interacting = this._isRounding = false; this._resizeUndo?.end(); }), // upEvent - e => {} // clickEvent + e => {}, // clickEvent, + true ); }; @@ -710,11 +711,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P return this._rotCenter; } + @observable _showNothing = true; + render() { const { b, r, x, y } = this.Bounds; const bounds = { b, r, x, y }; - const seldocview = SelectionManager.Views().slice(-1)[0]; + const seldocview = SelectionManager.Views().lastElement(); if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldocview || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + setTimeout(action(() => (this._showNothing = true))); return null; } // hide the decorations if the parent chooses to hide it or if the document itself hides it @@ -812,7 +816,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
); return ( -
+
(this._showNothing = false))}>
{ MainView.Live = window.location.search.includes('live'); - ReactDOM.createRoot(document.getElementById('root')!).render(); + const root = ReactDOM.createRoot(document.getElementById('root')!); + root.render(); window.location.search.includes('safe') && CollectionView.SetSafeMode(true); const info = await CurrentUserUtils.loadCurrentUser(); if (info.email === 'guest') DocServer.Control.makeReadOnly(); @@ -47,6 +48,6 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure new LinkManager(); new TrackMovements(); new ReplayMovements(); - ReactDOM.createRoot(document.getElementById('root')!).render(); + root.render(); }, 0); })(); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 5ab91dd70..3bdf65d01 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -49,7 +49,7 @@ export class MarqueeAnnotator extends React.Component { super(props); AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('', true), true); - AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true)); + AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)); AnchorMenu.Instance.OnAudio = unimplementedFunction; AnchorMenu.Instance.Highlight = this.highlight; AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true); diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 6d06bbbf6..b9af28413 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -88,6 +88,7 @@ export class SidebarAnnos extends React.Component { }; }); + if (!anchor.text) Doc.GetProto(anchor).text = '-selection-'; const textLines: any = [ { type: 'paragraph', @@ -121,16 +122,18 @@ export class SidebarAnnos extends React.Component { content: taggedContent, }; if (taggedContent.length) textLines.push(metadatatext); - Doc.GetProto(target).text = new RichTextField( - JSON.stringify({ - doc: { - type: 'doc', - content: textLines, - }, - selection: { type: 'text', anchor: 4, head: 4 }, // set selection to middle paragraph - }), - '' - ); + if (textLines.length) { + Doc.GetProto(target).text = new RichTextField( + JSON.stringify({ + doc: { + type: 'doc', + content: textLines, + }, + selection: { type: 'text', anchor: 4, head: 4 }, // set selection to middle paragraph + }), + '' + ); + } this.addDocument(target); setTimeout(() => this._stackRef.current?.focusDocument(target, {})); return target; @@ -233,7 +236,7 @@ export class SidebarAnnos extends React.Component { isAnnotationOverlay={false} select={emptyFunction} NativeDimScaling={returnOne} - childShowTitle={this.showTitle} + //childShowTitle={this.showTitle} childDocumentsActive={this.props.isContentActive} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} childHideDecorationTitle={returnTrue} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3cb920ba0..817baeae6 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -19,6 +19,7 @@ import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); import { Shadows } from 'browndash-components'; +import { SelectionManager } from '../util/SelectionManager'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -116,7 +117,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt dv.rootDoc === doc) ? 'black' : highlightColor, highlightIndex, highlightStroke: doc.type === DocumentType.INK }; } } return undefined; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 2f495d55c..6314b4529 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -129,6 +129,7 @@ export class CollectionStackingView extends CollectionSubView (this._renderCount = Math.min(docs.length, this._renderCount + 5)))); return docs.map((d, i) => { const height = () => this.getDocHeight(d); const width = () => this.getDocWidth(d); @@ -139,7 +140,7 @@ export class CollectionStackingView extends CollectionSubView - {this.getDisplayDoc(d, width)} + {this.getDisplayDoc(d, width, i)}
); }); @@ -297,12 +298,13 @@ export class CollectionStackingView extends CollectionSubView (this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); + @observable _renderCount = 5; isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined); // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list - getDisplayDoc(doc: Doc, width: () => number) { + getDisplayDoc(doc: Doc, width: () => number, count: number) { const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); @@ -310,7 +312,7 @@ export class CollectionStackingView extends CollectionSubView this.getDocTransform(doc, dref); this._docXfs.push({ stackedDocTransform, width, height }); //DocumentView is how the node will be rendered - return ( + return count > this._renderCount ? null : ( (dref = r || undefined)} Document={doc} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1a265af4a..456f2a13d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -259,11 +259,13 @@ export class CollectionTreeView extends CollectionSubView ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); }; headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields); + @observable _renderCount = 1; @computed get treeViewElements() { TraceMobx(); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; + if (this.treeChildren.length < this._renderCount) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20)))); return TreeView.GetChildElements( this.treeChildren, this, @@ -296,7 +298,8 @@ export class CollectionTreeView extends CollectionSubView { private _header: React.RefObject = React.createRef(); private _tref = React.createRef(); @observable _docRef: Opt; - private _selDisposer: Opt; + private _disposers: { [name: string]: IReactionDisposer } = {}; private _editTitleScript: (() => ScriptField) | undefined; private _openScript: (() => ScriptField) | undefined; private _treedropDisposer?: DragManager.DragDropDisposer; @@ -212,14 +213,14 @@ export class TreeView extends React.Component { }; @action setEditTitle = (docView?: DocumentView) => { - this._selDisposer?.(); + this._disposers.selection?.(); if (!docView) { this._editTitle = false; } else if (docView.isSelected()) { const doc = docView.Document; SelectionManager.SelectSchemaViewDoc(doc); this._editTitle = true; - this._selDisposer = reaction( + this._disposers.selection = reaction( () => SelectionManager.SelectedSchemaDoc(), seldoc => seldoc !== doc && this.setEditTitle(undefined) ); @@ -259,7 +260,8 @@ export class TreeView extends React.Component { }; componentWillUnmount() { - this._selDisposer?.(); + this._renderTimer && clearTimeout(this._renderTimer); + Object.values(this._disposers).forEach(disposer => disposer?.()); this._treeEle && this.props.unobserveHeight(this._treeEle); document.removeEventListener('pointermove', this.onDragMove, true); document.removeEventListener('pointermove', this.onDragUp, true); @@ -268,6 +270,10 @@ export class TreeView extends React.Component { } componentDidUpdate() { + this._disposers.opening = reaction( + () => this.treeViewOpen, + open => !open && (this._renderCount = 20) + ); this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex); } @@ -512,6 +518,8 @@ export class TreeView extends React.Component { return rows; } + _renderTimer: any; + @observable _renderCount = 1; @computed get renderContent() { TraceMobx(); const expandKey = this.treeViewExpandedView; @@ -543,6 +551,14 @@ export class TreeView extends React.Component { const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs; let downX = 0, downY = 0; + if (docs?.length && this._renderCount < docs?.length) { + this._renderTimer && clearTimeout(this._renderTimer); + this._renderTimer = setTimeout( + action(() => { + this._renderCount = Math.min(docs!.length, this._renderCount + 20); + }) + ); + } return ( <> {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : ( @@ -599,7 +615,8 @@ export class TreeView extends React.Component { // TODO: [AL] add these this.props.AddToMap, this.props.RemFromMap, - this.props.hierarchyIndex + this.props.hierarchyIndex, + this._renderCount )} @@ -651,7 +668,7 @@ export class TreeView extends React.Component { return (
{ // TODO: [AL] add these AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[], RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[], - hierarchyIndex?: number[] + hierarchyIndex?: number[], + renderCount?: number ) { const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField); if (viewSpecScript) { @@ -1144,6 +1162,7 @@ export class TreeView extends React.Component { return docs .filter(child => child instanceof Doc) .map((child, i) => { + if (renderCount && i > renderCount) return null; const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return null; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d6e95f97f..4d6e0dff2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -53,6 +53,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); +import { DocumentDecorations } from '../../DocumentDecorations'; export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -1305,7 +1306,9 @@ export class CollectionFreeFormView extends CollectionSubView { const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); const pointerEvents = - this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.()); + this.props.isContentActive() === false || DocumentDecorations.Instance.Interacting + ? 'none' + : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.()); return pointerEvents; }; getChildDocView(entry: PoolData) { @@ -2287,6 +2290,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY SelectionManager.DeselectAll(); dv.props.focus(dv.props.Document, { willPanZoom: true, + zoomScale: 0.8, afterFocus: async didMove => { if (!didMove) { const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 36c0240f1..3d89566ee 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -664,7 +664,7 @@ export class DocumentViewInternal extends DocComponent (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click')); - if (this.onDoubleClickHandler) { + if (this.onDoubleClickHandler && !this.props.Document.allowClickBeforeDoubleClick) { runInAction(() => (this._pendingDoubleClick = true)); this._timeout = setTimeout(() => { this._timeout = undefined; @@ -780,7 +780,9 @@ export class DocumentViewInternal extends DocComponent -
+
{fadepath === srcpath ? null : (
this._selectionText; + selectionContent = () => this._selectionContent; @action createTextAnnotation = (sel: Selection, selRange: Range | undefined) => { if (this._mainCont.current && selRange) { @@ -276,6 +280,8 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}> {this.urlContent} @@ -1014,7 +1019,7 @@ export class WebBox extends ViewBoxAnnotatableComponent diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 9d67283a0..b352f3790 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -136,7 +136,7 @@ export class FontIconBox extends DocComponent() { const setValue = (value: number) => UndoManager.RunInBatch(() => numScript?.script.run({ value, _readOnly_: false }), 'set num value'); // Script for checking the outcome of the toggle - const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; + const checkResult = Number(numScript?.script.run({ value: 0, _readOnly_: true }).result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)); const label = !FontIconBox.GetShowLabels() ? null :
{this.label}
; @@ -150,7 +150,6 @@ export class FontIconBox extends DocComponent() { max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult} className={'menu-slider'} - id="slider" onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))} onPointerUp={() => this._batch?.end()} onChange={e => { @@ -642,13 +641,7 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo if (checkResult) { return (selected ?? Doc.UserDoc())._fontHighlight; } - if (selected) { - selected._fontColor = color; - if (color) { - editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch); - } - } - Doc.UserDoc()._fontHighlight = color; + color && RichTextMenu.Instance.setHighlight(color); }); // toggle: Set overlay status of selected document @@ -795,7 +788,7 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, GestureOverlay.Instance.KeepPrimitiveMode = keepPrim; } if (Object.values(GestureUtils.Gestures).includes(tool as any)) { - if (GestureOverlay.Instance.InkShape === tool) { + if (GestureOverlay.Instance.InkShape === tool && !keepPrim) { Doc.ActiveTool = InkTool.None; GestureOverlay.Instance.InkShape = undefined; } else { @@ -804,7 +797,7 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, } } else if (tool) { // pen or eraser - if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape) { + if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) { Doc.ActiveTool = InkTool.None; } else { Doc.ActiveTool = tool as any; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 39005a18b..b33529aeb 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -18,12 +18,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { NodeSelection } from 'prosemirror-state'; import { OpenWhere } from '../DocumentView'; +import { FormattedTextBoxComment } from './FormattedTextBoxComment'; export class DashFieldView { dom: HTMLDivElement; // container for label and value root: any; + node: any; + tbox: FormattedTextBox; + unclickable = () => !this.tbox.props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview); constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { + this.node = node; + this.tbox = tbox; this.dom = document.createElement('div'); this.dom.style.width = node.attrs.width; this.dom.style.height = node.attrs.height; @@ -44,7 +50,18 @@ export class DashFieldView { this.root = ReactDOM.createRoot(this.dom); this.root.render( - + ); } destroy() { @@ -68,6 +85,7 @@ interface IDashFieldViewInternal { editable: boolean; node: any; getPos: any; + unclickable: () => boolean; } @observer @@ -125,7 +143,7 @@ export class DashFieldViewInternal extends React.Component(); private _ref: React.RefObject = React.createRef(); @@ -102,7 +101,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch); + this._editorView?.state && RichTextMenu.Instance.setHighlight(color); return undefined; }); AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true); @@ -788,7 +786,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate), icon: 'expand-arrows-alt' }); uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: 'expand-arrows-alt' }); uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' }); !Doc.noviceMode && @@ -1455,7 +1452,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu(); - if (!this._downEvent) return; - this._downEvent = false; - if (this._editorView?.state.selection.empty && this.props.isContentActive(true) && !(e.nativeEvent as any).dash) { - const editor = this._editorView!; + const editor = this._editorView!; + const state = editor?.state; + if (!state || !editor) return; + if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu(); + else if (this.props.isContentActive(true)) { const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); - !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); + !this.props.isSelected(true) && editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0)))); let target = e.target as any; // hrefs are stored on the dataset of the node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true'); - if (pcords && pcords.inside > 0 && this._editorView.state.doc.nodeAt(pcords.inside)?.type === this._editorView.state.schema.nodes.dashDoc) { + if (pcords && pcords.inside > 0 && state.doc.nodeAt(pcords.inside)?.type === state.schema.nodes.dashDoc) { return; } } diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index b70da2e5e..f0caa1f4f 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -54,7 +54,7 @@ export class RichTextMenu extends AntimodeMenu { @observable private _activeFontColor: string = 'black'; @observable private showColorDropdown: boolean = false; - @observable private activeHighlightColor: string = 'transparent'; + @observable private _activeHighlightColor: string = 'transparent'; @observable private showHighlightDropdown: boolean = false; @observable private currentLink: string | undefined = ''; @@ -89,6 +89,9 @@ export class RichTextMenu extends AntimodeMenu { @computed get fontColor() { return this._activeFontColor; } + @computed get fontHighlight() { + return this._activeHighlightColor; + } @computed get fontFamily() { return this._activeFontFamily; } @@ -138,7 +141,7 @@ export class RichTextMenu extends AntimodeMenu { this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various'; this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0]; this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...'; - this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...'; + this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...'; // update link in current selection this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle)); @@ -356,11 +359,13 @@ export class RichTextMenu extends AntimodeMenu { this.updateMenu(this.view, undefined, this.props); }; - setHighlight(color: String, view: EditorView, dispatch: any) { - const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color }); - if (view.state.selection.empty) return false; - view.focus(); - this.setMark(highlightMark, view.state, dispatch, false); + setHighlight(color: string) { + if (this.view) { + const highlightMark = this.view.state.schema.mark(this.view.state.schema.marks.marker, { highlight: color }); + this.setMark(highlightMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(highlightMark)), true); + this.view.focus(); + } else Doc.UserDoc()._fontHighlight = color; + this.updateMenu(this.view, undefined, this.props); } setColor(color: string) { @@ -563,7 +568,7 @@ export class RichTextMenu extends AntimodeMenu { } @action setActiveHighlight(color: string) { - this.activeHighlightColor = color; + this._activeHighlightColor = color; } @action setCurrentLink(link: string) { diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 7e728306c..f2e9be61d 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -54,7 +54,7 @@ export class TopBar extends React.Component { dash
)} - {Doc.ActiveDashboard && !Doc.noviceMode && ( + {Doc.ActiveDashboard && (
) : null} @@ -292,7 +304,7 @@ export class DocumentLinksButton extends React.Component void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { updateIcon?: () => void; // updates the icon representation of the document - getAnchor?: (addAsAnnotation: boolean) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) - scrollFocus?: (doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus + getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) + scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus + scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views @@ -585,7 +586,13 @@ export class DocumentViewInternal extends DocComponent (focusSpeed => (PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined))(options.instant ? 0 : options.zoomTime ?? 500)); + const focusSpeed = targetMatch && scrollFocus?.(this.props.DocumentView(), presItem?.proto === anchor ? presItem : anchor, options); // FOCUS: navigate through the display hierarchy making sure the target is in view const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing; diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 5c0005dae..8fa01c97a 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -13,7 +13,7 @@ import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; -import { DocFocusOptions } from './DocumentView'; +import { DocFocusOptions, DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; const EquationSchema = createSchema({}); @@ -42,14 +42,14 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent ); } getAnchor = (addAsAnnotation: boolean) => { - const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc }); + const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); anchor.xRange = new List(Array.from(this._plot.options.xAxis.domain)); anchor.yRange = new List(Array.from(this._plot.options.yAxis.domain)); if (addAsAnnotation) this.addDocument(anchor); return anchor; }; @action - scrollFocus = (doc: Doc, smooth: DocFocusOptions) => { + scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { this.dataDoc.xRange = new List(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10])))); this.dataDoc.yRange = new List(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9])))); return 0; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0ba576e55..363cd1d94 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -8,7 +8,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -26,11 +26,11 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, OpenWhere } from './DocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; -import { PresBox } from './trails'; +import { PinProps, PresBox } from './trails'; import React = require('react'); import Color = require('color'); import { LinkDocPreview } from './LinkDocPreview'; @@ -71,34 +71,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - return PresBox.restoreTargetDocView( - this.props.DocumentView?.(), // - { pinDocLayout: BoolCast(anchor.presPinLayout) }, - anchor, - focusSpeed, - !anchor.presPinData - ? {} - : { - pannable: true, - dataannos: anchor.presAnnotations !== undefined, - dataview: true, - } - ) - ? focusSpeed - : undefined; - }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document - - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinData: { pannable: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc); return anchor; } return this.rootDoc; @@ -177,9 +157,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.rootDoc; - getTitle() { return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 88d134bba..8d7ab2f0d 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -5,7 +5,7 @@ import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; -import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, PdfField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -22,13 +22,13 @@ import { LightboxView } from '../LightboxView'; import { CreateImage } from '../nodes/WebBoxRenderer'; import { PDFViewer } from '../pdf/PDFViewer'; import { SidebarAnnos } from '../SidebarAnnos'; +import { DocFocusOptions, DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './PDFBox.scss'; +import { PinProps, PresBox } from './trails'; import { VideoBox } from './VideoBox'; import React = require('react'); -import { PresBox } from './trails'; -import { DocFocusOptions } from './DocumentView'; @observer export class PDFBox extends ViewBoxAnnotatableComponent() { @@ -200,18 +200,18 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.brushView(view); - scrollFocus = (doc: Doc, options: DocFocusOptions) => { + scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { let didToggle = false; - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { this.toggleSidebar(options.preview); didToggle = true; } - if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; - this._initialScrollTarget = doc; - PresBox.restoreTargetDocView(this.props.DocumentView?.(), {}, doc, options.zoomTime ?? 500, { pannable: doc.presPinData ? true : false }); - return this._pdfViewer?.scrollFocus(doc, NumCast(doc.presPinViewScroll, NumCast(doc.y)), options) ?? (didToggle ? 1 : undefined); + if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + this._initialScrollTarget = anchor; + PresBox.restoreTargetDocView(docView, anchor, options.zoomTime ?? 500); + return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.presPinViewScroll, NumCast(anchor.y)), options) ?? (didToggle ? 1 : undefined); }; - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { let ele: Opt = undefined; if (this._pdfViewer?.selectionContent()) { ele = document.createElement('div'); @@ -223,11 +223,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent { this._pdfViewer = pdfViewer; - if (this._initialScrollTarget) { - this.scrollFocus(this._initialScrollTarget, { instant: true }); + const docView = this.props.DocumentView?.(); + if (this._initialScrollTarget && docView) { + this.scrollFocus(docView, this._initialScrollTarget, { instant: true }); this._initialScrollTarget = undefined; } }; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index f94996c66..61e4894f0 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -130,7 +130,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc; + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc; }; videoLoad = () => { diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 281967a21..fa2021642 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -20,6 +20,7 @@ import { EditableView } from '../EditableView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import { OverlayView } from '../OverlayView'; import { DocumentIconContainer } from './DocumentIcon'; +import { DocFocusOptions, DocumentView } from './DocumentView'; import './ScriptingBox.scss'; const _global = (window /* browser */ || global) /* node */ as any; @@ -481,10 +482,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { - return undefined; - }; - getSuggestedParams(pos: number) { const firstScript = this.rawText.slice(0, pos); const indexP = firstScript.lastIndexOf('.'); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 1dfa55c64..c26562e7c 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -7,6 +7,7 @@ import * as rp from 'request-promise'; import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; +import { ObjectField } from '../../../fields/ObjectField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; import { emptyFunction, formatTime, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -27,11 +28,11 @@ import { DocumentDecorations } from '../DocumentDecorations'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; +import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { RecordingBox } from './RecordingBox'; +import { PinProps, PresBox } from './trails'; import './VideoBox.scss'; -import { ObjectField } from '../../../fields/ObjectField'; -import { DocFocusOptions, OpenWhere } from './DocumentView'; const path = require('path'); /** @@ -390,13 +391,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true)); }; - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null); const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation); - return ( - CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || - this.rootDoc - ); + if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent'; + const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc; + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc); + return anchor; }; // sets video info on load @@ -953,14 +954,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (doc !== this.rootDoc) { - const showTime = Cast(doc._timecodeToShow, 'number', null); - showTime !== undefined && setTimeout(() => this.Seek(showTime), 100); - return 0.1; - } - }; - // renders CollectionStackedTimeline @computed get renderTimeline() { return ( diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 4c9bead9d..caab18e0d 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as WebRequest from 'web-request'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; @@ -11,8 +11,9 @@ import { listSpec } from '../../../fields/Schema'; import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, getWordAtPoint, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; +import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; +import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -21,7 +22,6 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; -import { DocumentDecorations } from '../DocumentDecorations'; import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; @@ -29,13 +29,13 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { Annotation } from '../pdf/Annotation'; import { SidebarAnnos } from '../SidebarAnnos'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, DocumentView, DocumentViewInternal, DocumentViewProps } from './DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { LinkDocPreview } from './LinkDocPreview'; import { VideoBox } from './VideoBox'; import './WebBox.scss'; import React = require('react'); -import { DragManager } from '../../util/DragManager'; +import { PinProps, PresBox } from './trails'; const { CreateImage } = require('./WebBoxRenderer'); const _global = (window /* browser */ || global) /* node */ as any; const htmlToText = require('html-to-text'); @@ -189,6 +189,7 @@ export class WebBox extends ViewBoxAnnotatableComponent void) => (this._setBrushViewer = func); brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view); - scrollFocus = (doc: Doc, options: DocFocusOptions) => { - if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), options.preview); - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + focus = (doc: Doc, options: DocFocusOptions) => { + !doc.unrendered && this.props.DocumentView?.() && this.scrollFocus(this.props.DocumentView?.(), doc, {}); + this.props.focus(doc, options); + }; + scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { + if (this._url && StrCast(anchor.webUrl) !== this._url) this.submitURL(StrCast(anchor.webUrl), options.preview); + if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { this.toggleSidebar(options.preview); } - if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; - if (doc !== this.rootDoc && this._outerRef.current) { + if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + if (anchor !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this._scrollHeight)); + const scrollTo = !anchor[HeightSym]() + ? NumCast(anchor.y) + : Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); + const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; if (scrollTo !== undefined && this._initialScroll === undefined) { - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; this.goTo(scrollTo, focusSpeed, options.easeFunc); + PresBox.restoreTargetDocView(docView, anchor, focusSpeed); return focusSpeed; } else if (!this._webPageHasBeenRendered || !this._scrollHeight || this._initialScroll !== undefined) { this._initialScroll = scrollTo; + return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; } } return undefined; }; - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { let ele: Opt = undefined; try { const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents(); @@ -330,9 +339,11 @@ export class WebBox extends ViewBoxAnnotatableComponent { + const layoutFrameNumber = Cast(dv.props.ContainingCollectionDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values + const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + if (contentFrameNumber !== undefined) { + CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { backgroundColor: color }); + } else { + dv.rootDoc._backgroundColor = color; + } + }); + } else { + const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : []; + if (checkResult) { + return selected.lastElement()?._backgroundColor ?? 'transparent'; + } + selected.forEach(doc => (doc._backgroundColor = color)); } - if (selected) selected._backgroundColor = color; }); // toggle: Set overlay status of selected document diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 788de7af9..269438a7c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -45,7 +45,7 @@ import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; -import { DocFocusOptions, DocumentViewInternal, OpenWhere } from '../DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { LinkDocPreview } from '../LinkDocPreview'; import { DashDocCommentView } from './DashDocCommentView'; @@ -932,7 +932,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + scrollFocus = (docView: DocumentView, textAnchor: Doc, options: DocFocusOptions) => { let didToggle = false; if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) { this.toggleSidebar(options.preview); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 13cbd87eb..45d386436 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -4,9 +4,9 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AnimationSym, Doc, DocListCast, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; +import { AnimationSym, Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../../fields/Doc'; import { Copy, Id } from '../../../../fields/FieldSymbols'; -import { InkTool } from '../../../../fields/InkField'; +import { InkField, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; @@ -34,8 +34,23 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; +import { SerializationHelper } from '../../../util/SerializationHelper'; const { Howl } = require('howler'); +export interface pinDataTypes { + scrollable?: boolean; + pannable?: boolean; + viewType?: boolean; + inkable?: boolean; + filters?: boolean; + pivot?: boolean; + temporal?: boolean; + clippable?: boolean; + dataview?: boolean; + textview?: boolean; + poslayoutview?: boolean; + dataannos?: boolean; +} export interface PinProps { audioRange?: boolean; activeFrame?: number; @@ -45,18 +60,7 @@ export interface PinProps { pinDocLayout?: boolean; // pin layout info (width/height/x/y) pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text) pinAudioPlay?: boolean; // pin audio annotation - pinData?: { - scrollable?: boolean | undefined; - pannable?: boolean | undefined; - viewType?: boolean | undefined; - filters?: boolean | undefined; - temporal?: boolean | undefined; - clippable?: boolean | undefined; - dataview?: boolean | undefined; - textview?: boolean | undefined; - poslayoutview?: boolean | undefined; - dataannos?: boolean | undefined; - }; + pinData?: pinDataTypes; } @observer @@ -318,19 +322,9 @@ export class PresBox extends ViewBoxBaseComponent() { this.onHideDocument(); //Handles hide after/before } }); - static pinDataTypes(target?: Doc): { - scrollable?: boolean; - pannable?: boolean; - viewType?: boolean; - filters?: boolean; - temporal?: boolean; - clippable?: boolean; - dataview?: boolean; - textview?: boolean; - poslayoutview?: boolean; - dataannos?: boolean; - } { + static pinDataTypes(target?: Doc): pinDataTypes { const targetType = target?.type as any; + const inkable = [DocumentType.INK].includes(targetType); const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking; const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform); const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType); @@ -340,18 +334,19 @@ export class PresBox extends ViewBoxBaseComponent() { const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined; const viewType = targetType === DocumentType.COL; const filters = true; + const pivot = true; const dataannos = false; - return { scrollable, pannable, viewType, filters, temporal, clippable, dataview, textview, poslayoutview, dataannos }; + return { scrollable, pannable, inkable, viewType, pivot, filters, temporal, clippable, dataview, textview, poslayoutview, dataannos }; } @action playAnnotation = (anno: AudioField) => {}; @action - static restoreTargetDocView(bestTargetView: Opt, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTargetView?.rootDoc)) { - if (!bestTargetView) return; - const bestTarget = bestTargetView.rootDoc; + static restoreTargetDocView(bestTargetView: Opt, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.presPinLayout), pinDataTypes?: pinDataTypes, targetDoc?: Doc) { + const bestTarget = bestTargetView?.rootDoc ?? (targetDoc?.unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc); + if (!bestTarget) return; let changed = false; - if (pinProps?.pinDocLayout) { + if (pinDocLayout) { if ( bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) || bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) || @@ -369,33 +364,51 @@ export class PresBox extends ViewBoxBaseComponent() { changed = true; } } - if (pinDataTypes.clippable) { + if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presPinClipWidth !== undefined)) { if (bestTarget._clipWidth !== activeItem.presPinClipWidth) { bestTarget._clipWidth = activeItem.presPinClipWidth; changed = true; } } - if (pinDataTypes.temporal) { + if (pinDataTypes?.temporal || (!pinDataTypes && activeItem.presStartTime !== undefined)) { if (bestTarget._currentTimecode !== activeItem.presStartTime) { bestTarget._currentTimecode = activeItem.presStartTime; changed = true; } } - if (pinDataTypes.viewType && activeItem.presPinViewType !== undefined) { + if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.presFillColor !== undefined || activeItem.color !== undefined))) { + if (bestTarget.fillColor !== activeItem.presFillColor) { + Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor; + changed = true; + } + if (bestTarget.color !== activeItem.color) { + Doc.GetProto(bestTarget).color = activeItem.color; + changed = true; + } + } + if ((pinDataTypes?.viewType && activeItem.presPinViewType !== undefined) || (!pinDataTypes && activeItem.presPinViewType !== undefined)) { if (bestTarget._viewType !== activeItem.presPinViewType) { bestTarget._viewType = activeItem.presPinViewType; changed = true; } } - if (pinDataTypes.filters && activeItem.presPinDocFilters !== undefined) { + if ((pinDataTypes?.filters && activeItem.presPinDocFilters !== undefined) || (!pinDataTypes && activeItem.presPinDocFilters !== undefined)) { if (bestTarget.docFilters !== activeItem.presPinDocFilters) { bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presPinDocFilters as ObjectField) || new List([]); changed = true; } } - if (pinDataTypes.scrollable) { + if ((pinDataTypes?.pivot && activeItem.presPinPvitoField !== undefined) || (!pinDataTypes && activeItem.presPinPivotField !== undefined)) { + if (bestTarget.pivotField !== activeItem.presPinPivotField) { + bestTarget.pivotField = activeItem.presPinPivotField; + bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView + changed = true; + } + } + + if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presPinViewScroll !== undefined)) { if (bestTarget._scrollTop !== activeItem.presPinViewScroll) { bestTarget._scrollTop = activeItem.presPinViewScroll; changed = true; @@ -406,39 +419,59 @@ export class PresBox extends ViewBoxBaseComponent() { } } } - if (pinDataTypes.dataannos) { + if (pinDataTypes?.dataannos || (!pinDataTypes && activeItem.presAnnotations !== undefined)) { const fkey = Doc.LayoutFieldKey(bestTarget); - Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]); + const oldItems = DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered); + const newItems = DocListCast(activeItem.presAnnotations).map(doc => { + doc.hidden = false; + return doc; + }); + const hiddenItems = DocListCast(bestTarget[fkey + '-annotations']) + .filter(doc => !doc.unrendered && !newItems.includes(doc)) + .map(doc => { + doc.hidden = true; + return doc; + }); + const newList = new List([...oldItems, ...hiddenItems, ...newItems]); + Doc.GetProto(bestTarget)[fkey + '-annotations'] = newList; } - if (pinDataTypes.dataview && activeItem.presData !== undefined) { + if ((pinDataTypes?.dataview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) { bestTarget._dataTransition = `all ${transTime}ms`; const fkey = Doc.LayoutFieldKey(bestTarget); Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt; setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); } - if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; - if (pinDataTypes.poslayoutview) { + if ((pinDataTypes?.textview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) { + Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; + } + if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.presPinLayoutData !== undefined)) { changed = true; + const layoutField = Doc.LayoutFieldKey(bestTarget); + const transitioned = new Set(); StrListCast(activeItem.presPinLayoutData) - .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number }) - .forEach(data => { - const doc = DocServer.GetCachedRefField(data.id) as Doc; - doc._dataTransition = `all ${transTime}ms`; - doc.x = data.x; - doc.y = data.y; - doc._width = data.w; - doc._height = data.h; + .map(str => JSON.parse(str) as { id: string; x: number; y: number; back: string; fill: string; w: number; h: number; data: string; text: string }) + .forEach(async data => { + const doc = DocCast(DocServer.GetCachedRefField(data.id)); + if (doc) { + transitioned.add(doc); + const field = !data.data ? undefined : await SerializationHelper.Deserialize(data.data); + const tfield = !data.text ? undefined : await SerializationHelper.Deserialize(data.text); + doc._dataTransition = `all ${transTime}ms`; + doc.x = data.x; + doc.y = data.y; + data.back && (doc._backgroundColor = data.back); + data.fill && (doc._fillColor = data.fill); + doc._width = data.w; + doc._height = data.h; + data.data && (Doc.GetProto(doc).data = field); + data.text && (Doc.GetProto(doc).text = tfield); + Doc.AddDocToList(Doc.GetProto(bestTarget), layoutField, doc); + } }); - setTimeout( - () => - StrListCast(activeItem.presPinLayoutData) - .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number }) - .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))), - transTime + 10 - ); + setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10); } - if (pinDataTypes.pannable) { + if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPinViewX !== undefined || activeItem.presPinViewScale !== undefined))) { const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; @@ -460,7 +493,7 @@ export class PresBox extends ViewBoxBaseComponent() { } } if (changed) { - return bestTargetView.setViewTransition('all', transTime); + return bestTargetView?.setViewTransition('all', transTime); } } @@ -499,12 +532,31 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presAnnotations = new List(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered)); } if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text; + if (pinProps.pinData.inkable) { + pinDoc.presFillColor = targetDoc.fillColor; + pinDoc.presColor = targetDoc.color; + } if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop; if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth; if (pinProps.pinData.poslayoutview) - pinDoc.presPinLayoutData = new List(DocListCast(targetDoc[fkey] as ObjectField).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) }))); + pinDoc.presPinLayoutData = new List( + DocListCast(targetDoc[fkey] as ObjectField).map(d => + JSON.stringify({ + id: d[Id], + x: NumCast(d.x), + y: NumCast(d.y), + w: NumCast(d._width), + h: NumCast(d._height), + fill: StrCast(d._fillColor), + back: StrCast(d._backgroundColor), + data: SerializationHelper.Serialize(d.data instanceof ObjectField ? d.data[Copy]() : ''), + text: SerializationHelper.Serialize(d.text instanceof ObjectField ? d.text[Copy]() : ''), + }) + ) + ); if (pinProps.pinData.viewType) pinDoc.presPinViewType = targetDoc._viewType; if (pinProps.pinData.filters) pinDoc.presPinDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField); + if (pinProps.pinData.pivot) pinDoc.presPinPivotField = targetDoc._pivotField; if (pinProps.pinData.pannable) { pinDoc.presPinViewX = NumCast(targetDoc._panX); pinDoc.presPinViewY = NumCast(targetDoc._panY); @@ -586,7 +638,7 @@ export class PresBox extends ViewBoxBaseComponent() { const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined; if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) { // targetDoc may or may not be displayed. so get the first available document (or alias) view that matches targetDoc and use it - PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500)); + // PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500), undefined, targetDoc); } }; const finishAndRestoreLayout = () => { @@ -660,16 +712,43 @@ export class PresBox extends ViewBoxBaseComponent() { _exitTrail: Opt<() => void>; PlayTrail = (docs: Doc[]) => { - const savedStates = docs.map(doc => (doc._viewType !== CollectionViewType.Freeform ? undefined : { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) })); + const savedStates = docs.map(doc => { + switch (doc.type) { + case DocumentType.COL: + if (doc._viewType === CollectionViewType.Freeform) return { type: CollectionViewType.Freeform, doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }; + break; + case DocumentType.INK: + if (doc.data instanceof InkField) { + return { type: doc.type, doc, data: doc.data?.[Copy](), fillColor: doc.fillColor, color: doc.color, x: NumCast(doc.x), y: NumCast(doc.y) }; + } + } + return undefined; + }); this.startPresentation(0); this._exitTrail = () => { savedStates .filter(savedState => savedState) .map(savedState => { - const { x, y, s, c } = savedState!; - c._panX = x; - c._panY = y; - c._viewScale = s; + switch (savedState?.type) { + case CollectionViewType.Freeform: + { + const { x, y, s, doc } = savedState!; + doc._panX = x; + doc._panY = y; + doc._viewScale = s; + } + break; + case DocumentType.INK: + { + const { data, fillColor, color, x, y, doc } = savedState!; + doc.x = x; + doc.y = y; + doc.data = data; + doc.fillColor = fillColor; + doc.color = color; + } + break; + } }); LightboxView.SetLightboxDoc(undefined); Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); @@ -1869,7 +1948,7 @@ export class PresBox extends ViewBoxBaseComponent() { ); } - scrollFocus = () => { + scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { // this.gotoDocument(0); // this.startOrPause(false); return undefined; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 4a7f5d038..fc8f1da49 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -41,7 +41,6 @@ export class AnchorMenu extends AntimodeMenu { @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)'; @observable private _showLinkPopup: boolean = false; - @observable public Highlighting: boolean = false; @observable public Status: 'marquee' | 'annotation' | '' = ''; public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search @@ -121,9 +120,7 @@ export class AnchorMenu extends AntimodeMenu { @action highlightClicked = (e: React.MouseEvent) => { - if (!this.Highlight(this.highlightColor, false, undefined, true) && this.Pinned) { - this.Highlighting = !this.Highlighting; - } + this.Highlight(this.highlightColor, false, undefined, true); AnchorMenu.Instance.fadeOut(true); }; @@ -138,7 +135,7 @@ export class AnchorMenu extends AntimodeMenu { const button = ( diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 2db44788f..9877690f8 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -30,7 +30,7 @@ const _global = (window /* browser */ || global) /* node */ as any; //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js'; +pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.js'; interface IViewerProps extends FieldViewProps { Document: Doc; @@ -179,7 +179,7 @@ export class PDFViewer extends React.Component { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, this._scrollHeight); + const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; else if (!options.instant && !options.preview) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); @@ -518,6 +518,10 @@ export class PDFViewer extends React.Component { return this.props.styleProvider?.(doc, props, property); }; + focus = (doc: Doc, options: DocFocusOptions) => { + !doc.unrendered && this.props.DocumentView?.() && this.scrollToAnnotation(doc); + this.props.focus(doc, options); + }; renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => (
{ PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} dropAction={'alias'} + focus={this.focus} docFilters={docFilters} select={emptyFunction} bringToFront={emptyFunction} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index df49c32f0..ed5eaa756 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -722,13 +722,22 @@ export namespace Doc { return bestAlias ?? Doc.MakeAlias(doc); } - export async function makeClone(doc: Doc, cloneMap: Map, linkMap: Map, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise { + export async function makeClone( + doc: Doc, + cloneMap: Map, + linkMap: Map, + rtfs: { copy: Doc; key: string; field: RichTextField }[], + exclusions: string[], + topLevelExclusions: string[], + dontCreate: boolean, + asBranch: boolean + ): Promise { if (Doc.IsBaseProto(doc)) return doc; if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!; const copy = dontCreate ? (asBranch ? Cast(doc.branchMaster, Doc, null) || doc : doc) : new Doc(undefined, true); cloneMap.set(doc[Id], copy); const fieldExclusions = doc.type === DocumentType.MARKER ? exclusions.filter(ex => ex !== 'annotationOn') : exclusions; - const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])]; + const filter = [...fieldExclusions, ...topLevelExclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])]; await Promise.all( Object.keys(doc).map(async key => { if (filter.includes(key)) return; @@ -739,10 +748,10 @@ export namespace Doc { const list = await Cast(doc[key], listSpec(Doc)); const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc); if (docs !== undefined && docs.length) { - const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch))); + const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch))); !dontCreate && assignKey(new List(clones)); } else if (doc[key] instanceof Doc) { - assignKey(key.includes('layout[') ? undefined : key.startsWith('layout') ? (doc[key] as Doc) : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields + assignKey(key.includes('layout[') ? undefined : key.startsWith('layout') ? (doc[key] as Doc) : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields } else { !dontCreate && assignKey(ObjectField.MakeCopy(field)); if (field instanceof RichTextField) { @@ -754,17 +763,18 @@ export namespace Doc { }; if (key === 'proto') { if (doc[key] instanceof Doc) { - assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)); + assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch)); } } else if (key === 'anchor1' || key === 'anchor2') { if (doc[key] instanceof Doc) { - assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch)); + assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, [], true, asBranch)); } } else { if (field instanceof RefField) { assignKey(field); } else if (cfield instanceof ComputedField) { - !dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript)); + !dontCreate && assignKey(cfield[Copy]()); + // ComputedField.MakeFunction(cfield.script.originalScript)); } else if (field instanceof ObjectField) { await copyObjectField(field); } else if (field instanceof Promise) { @@ -776,7 +786,7 @@ export namespace Doc { }) ); for (const link of Array.from(doc[DirectLinksSym])) { - const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch); + const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, [], dontCreate, asBranch); linkMap.set(link, linkClone); } if (!dontCreate) { @@ -793,7 +803,7 @@ export namespace Doc { export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map = new Map()) { const linkMap = new Map(); const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = []; - const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf', 'context'], dontCreate, asBranch); + const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf'], ['context'], dontCreate, asBranch); Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true)); rtfMap.map(({ copy, key, field }) => { const replacer = (match: any, attr: string, id: string, offset: any, string: any) => { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index b23732b45..5f02bd73d 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -185,9 +185,11 @@ export class ComputedField extends ScriptField { const compiled = ScriptField.CompileScript(script, params, false); return compiled.compiled ? new ComputedField(compiled) : undefined; } - public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) { + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) { const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); - return compiled.compiled ? new ComputedField(compiled) : undefined; + const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined; + const compiledsetscript = compiledsetter?.compiled ? compiledsetter : undefined; + return compiled.compiled ? new ComputedField(compiled, compiledsetscript) : undefined; } public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt) { if (!doc[`${fieldKey}-indexed`]) { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 33809824f..f461cf3fa 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -501,7 +501,7 @@ export namespace DashUploadUtils { const parseExifData = async (source: string) => { const image = await request.get(source, { encoding: null }); - const { data, error } = await new Promise(resolve => { + const { data, error } = await new Promise<{ data: any; error: any }>(resolve => { new ExifImage({ image }, (error, data) => { let reason: Opt = undefined; if (error) { diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 9b91a35a6..68b003496 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -199,7 +199,7 @@ export namespace WebSocket { return Database.Instance.getDocument(id, callback); } function GetRefField([id, callback]: [string, (result?: Transferable) => void]) { - process.stdout.write(`.`); + process.stdout.write(`+`); GetRefFieldLocal([id, callback]); } -- cgit v1.2.3-70-g09d2 From 4a60851bd4d3495b2605863e3070c73129c9bc57 Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 2 Mar 2023 11:41:35 -0500 Subject: Image uploads use local path rather than web url --- src/client/views/nodes/ImageBox.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 28 +++++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e2ecca0b6..1eaf86c56 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -326,7 +326,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - newDoc.data = image_url; - newDoc._height = 200; - newDoc._width = 200; - DocUtils.MakeLink({doc: this.rootDoc}, {doc: newDoc}, "Dall-E"); - }, 500) + + DocUtils.MakeLink({doc: this.rootDoc}, {doc: newDoc}, "Dall-E"); } } catch (err) { console.log(err); -- cgit v1.2.3-70-g09d2 From 0c38e4dc096d6abf82ef11286616856b7119c6e1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 5 Mar 2023 21:24:09 -0500 Subject: replace jumpToDocument with showDocument. restructure code to get rid of scrollFocus by adding getView() and fixing focus() and restoreTargetView --- src/Utils.ts | 27 +++ src/client/documents/Documents.ts | 2 +- src/client/util/DocumentManager.ts | 235 +++++++-------------- .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/LinkFollower.ts | 56 +---- src/client/util/SharingManager.tsx | 2 +- src/client/views/InkingStroke.tsx | 8 +- src/client/views/LightboxView.tsx | 4 +- src/client/views/MainView.tsx | 2 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/SidebarAnnos.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 15 +- .../collections/CollectionStackedTimeline.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 13 +- .../views/collections/CollectionTimeView.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 10 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 151 ++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +- .../collectionLinear/CollectionLinearView.tsx | 2 +- .../CollectionMulticolumnView.tsx | 3 +- .../CollectionMultirowView.tsx | 1 - .../collectionSchema/CollectionSchemaCells.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 7 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 7 +- src/client/views/nodes/ComparisonBox.tsx | 19 +- src/client/views/nodes/DocumentView.tsx | 66 ++---- src/client/views/nodes/FilterBox.tsx | 2 +- src/client/views/nodes/FunctionPlotBox.tsx | 20 +- src/client/views/nodes/ImageBox.tsx | 7 +- src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 24 +-- src/client/views/nodes/VideoBox.tsx | 35 +-- src/client/views/nodes/WebBox.tsx | 82 ++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 28 +-- src/client/views/nodes/trails/PresBox.scss | 2 +- src/client/views/nodes/trails/PresBox.tsx | 160 +++++++------- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 9 +- src/client/views/search/SearchBox.tsx | 2 +- src/fields/ScriptField.ts | 7 +- src/fields/util.ts | 6 +- 43 files changed, 410 insertions(+), 635 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 22c8cb902..ae1478943 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,6 +1,7 @@ import v4 = require('uuid/v4'); import v5 = require('uuid/v5'); import { ColorState } from 'react-color'; +import * as rp from 'request-promise'; import { Socket } from 'socket.io'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; @@ -34,6 +35,31 @@ export namespace Utils { return v5(seed, v5.URL); } + /** + * Uploads an image buffer to the server and stores with specified filename. by default the image + * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large) + * @param imageUri the bytes of the image + * @param returnedFilename the base filename to store the image on the server + * @param nosuffix optionally suppress creating multiple resolution images + */ + export async function convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) { + try { + const posting = Utils.prepend('/uploadURI'); + const returnedUri = await rp.post(posting, { + body: { + uri: imageUri, + name: returnedFilename, + nosuffix, + replaceRootFilename, + }, + json: true, + }); + return returnedUri; + } catch (e) { + console.log('VideoBox :' + e); + } + } + export function GetScreenTransform(ele?: HTMLElement): { scale: number; translateX: number; translateY: number } { if (!ele) { return { scale: 1, translateX: 1, translateY: 1 }; @@ -218,6 +244,7 @@ export namespace Utils { } export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) { + if (!targetHgt) return targetY; // if there's no height, then assume that if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) { return Math.ceil(targetY + minSpacing + targetHgt - contextHgt); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ebd06dbe2..98469a2f9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1319,7 +1319,7 @@ export namespace DocUtils { } export function DefaultFocus(doc: Doc, options: DocFocusOptions) { - options?.afterFocus?.(false); + return undefined; } export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 0d10bed43..1a38e9aff 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,17 +1,22 @@ +import { loadAsync } from 'jszip'; import { action, observable, ObservableSet, runInAction } from 'mobx'; import { AnimationSym, Doc, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../fields/Types'; import { AudioField } from '../../fields/URLField'; -import { returnFalse } from '../../Utils'; -import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { emptyFunction } from '../../Utils'; +import { CollectionViewType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; +import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; +import { MainView } from '../views/MainView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; +import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; +import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; const { Howl } = require('howler'); @@ -165,12 +170,12 @@ export class DocumentManager { public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { const views: DocumentView[] = []; Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); - return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); + return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined); }; public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind, originatingDoc); const views = this.getDocumentViews(toFind); //.filter(view => view.rootDoc !== originatingDoc); - return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); + return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined); }; public getDocumentViews(toFindIn: Doc): DocumentView[] { const toFind = @@ -198,7 +203,7 @@ export class DocumentManager { static GetContextPath(doc: Opt, includeExistingViews?: boolean) { if (!doc) return []; - const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null); + const srcContext = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); var containerDocContext = srcContext ? [srcContext, doc] : [doc]; while ( containerDocContext.length && @@ -235,171 +240,91 @@ export class DocumentManager { CollectionDockingView.AddSplit(doc, OpenWhereMod.right); finished?.(); }; - public jumpToDocument = ( + + // shows a documentView by: + // traverses down through the viewPath of contexts to the view: + // focusing on each context + public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => { + const docViewPath = targetDocView.docViewPath.slice(); + let rootContextView = docViewPath.shift(); + return rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined })); + }; + + // shows a document by first: + // traversing down through the contexts that contain target until an existing view is found + // if no container view is found, create one by: opening an existing tab that has the top-level view, or showing the top-level context in the lightbox. + // once a containing view is found, it then traverses back down through the contexts to the target document by: + // focusing on each context + // and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration + public showDocument = async ( targetDoc: Doc, // document to display options: DocFocusOptions, // options for how to navigate to target - createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist - docContextPath: Doc[], // context to load that should contain the target finished?: () => void - ): void => { - const originalTarget = options.originalTarget ?? targetDoc; - const docView = this.getFirstDocumentView(targetDoc, options.originatingDoc); - const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); - const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? docView?.rootDoc ?? targetDoc : docView?.rootDoc ?? targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field - var wasHidden = resolvedTarget.hidden; - if (wasHidden) { - runInAction(() => { - resolvedTarget.hidden = false; // if the target is hidden, un-hide it here. - docView?.props.bringToFront(resolvedTarget); - }); - } - const focusAndFinish = action((didFocus: boolean) => { - const finalTargetDoc = resolvedTarget; - if (options.toggleTarget) { - if (!didFocus && !wasHidden) { - // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal - finalTargetDoc.hidden = !finalTargetDoc.hidden; - } - } else { - finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined); - !options.noSelect && docView?.select(false); - } - if (targetDoc.textHtml && options.zoomTextSelections) { - const containerView = DocumentManager.Instance.getFirstDocumentView(finalTargetDoc); - if (containerView) { - containerView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect)); - containerView.textHtmlOverlay = StrCast(targetDoc.textHtml); - DocumentManager._overlayViews.add(containerView); - if (Doc.UnhighlightTimer) { - Doc.AddUnHighlightWatcher(() => { - DocumentManager.removeOverlayViews(); - containerView.htmlOverlayEffect = ''; - }); - } else setTimeout(() => (containerView.htmlOverlayEffect = '')); - } - } - finished?.(); + ) => { + const docContextPath = DocumentManager.GetContextPath(targetDoc, true); + let rootContextView = await new Promise(res => { + const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc)); + if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!); + docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation as OpenWhere); + this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); }); - const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && this.getFirstDocumentView(annotatedDoc); - if (annoContainerView) { - if (annoContainerView.props.Document.layoutKey === 'layout_icon') { - return annoContainerView.iconify(() => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, originalTarget, toggleTarget: false }, createViewFunc, docContextPath, finished)), 30); - } - if (!docView && targetDoc.type !== DocumentType.MARKER) { - annoContainerView.focus(targetDoc, options); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below - } - } - const contextDoc = docContextPath.length ? docContextPath[0] : undefined; - const remainingDocContext = docContextPath.length ? docContextPath.slice(1) : []; - const targetDocContext = contextDoc || annotatedDoc; - const targetDocContextView = (targetDocContext && this.getFirstDocumentView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above - const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; - if (focusView) { - // if (focusView.rootDoc === originalTarget) { - if (!options.noSelect) Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox - else { - Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox - focusView.rootDoc[AnimationSym] = options.effect; - if (Doc.UnhighlightTimer) { - Doc.AddUnHighlightWatcher(action(() => (focusView.rootDoc[AnimationSym] = undefined))); - } - } - //} - if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc); - const doFocus = (forceDidFocus: boolean) => { - if (options.openInLightbox || (!options.originatingDoc?.followLinkLocation && DocCast(focusView.rootDoc.context)?._isLightbox)) { - focusView.props.addDocTab(targetDoc, OpenWhere.lightbox); - focusAndFinish(true); - } else { - focusView.focus(originalTarget, { - ...options, - originalTarget, - afterFocus: (didFocus: boolean) => - new Promise(res => { - focusAndFinish(forceDidFocus || didFocus); - res(ViewAdjustment.doNothing); - }), - }); - } - }; - if (focusView.props.Document.layoutKey === 'layout_icon' && focusView.rootDoc.type !== DocumentType.SCRIPTING) { - focusView.iconify(() => doFocus(true)); - } else { - doFocus(false); - } - } else { - if (!targetDocContext) { - // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default - createViewFunc(Doc.BrushDoc(targetDoc), () => focusAndFinish(true)); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); - } else { - // otherwise try to get a view of the context of the target - if (targetDocContextView) { - // we found a context view and aren't forced to create a new one ... focus on the context first.. - wasHidden = wasHidden || targetDocContextView.rootDoc.hidden; - targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden + docContextPath.shift(); + const childViewIterator = async () => { + const innerDoc = docContextPath.shift(); + return { viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.unrendered ? (await rootContextView.ComponentView?.getView?.(innerDoc)) ?? this.getDocumentView(innerDoc) : undefined }; + }; + const target = await this.focusViewsInPath(rootContextView, options, childViewIterator); + this.restoreDocView(target.viewSpec, target.docView, options, target.contextView ?? target.docView, targetDoc); - if (targetDocContext.layoutKey === 'layout_icon') { - return targetDocContextView.iconify( - () => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(resolvedTarget ?? targetDoc, { ...options /* originalTarget - needed? */ }, createViewFunc, docContextPath, finished)), - 30 - ); - } + finished?.(); + }; - const contextFocusTime = options.zoomTime ? options.zoomTime / 2 : 500; - const remainingFocustime = options.zoomTime ? options.zoomTime - contextFocusTime : undefined; - targetDocContextView.setViewTransition('transform', contextFocusTime); - // this makes focusing on contexts run in parallel -- jutmp to document below makes them run sequentially - this.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, zoomTime: remainingFocustime }, createViewFunc, remainingDocContext, finished)); - targetDocContextView.props.focus(targetDocContextView.rootDoc, { - ...options, - zoomTime: contextFocusTime, - // originalTarget, // needed? - afterFocus: async () => { - // now find the target document within the context - if (targetDoc._timecodeToShow) { - // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode; - targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow; - finished?.(); - } else { - // otherwise, just look for the target document in this context view now that we've focused the context view - if (this.getFirstDocumentView(resolvedTarget)) { - // test again for the target view snce we presumably created the context above by focusing on it - this.jumpToDocument(targetDoc, { ...options, zoomTime: remainingFocustime }, createViewFunc, remainingDocContext, finished); - } else if (targetDoc.layout) { - // there will no layout for a TEXTANCHOR type document - createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target - } - } - return ViewAdjustment.doNothing; - }, - }); - } else { - if (docContextPath.length && docContextPath[0]?.layoutKey === 'layout_icon') { - Doc.deiconifyView(docContextPath[0]); - this.jumpToDocument(targetDoc, options, createViewFunc, docContextPath, finished); - } else { - // there's no context view so we need to create one first and try again when that finishes - createViewFunc( - targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target - () => this.jumpToDocument(targetDoc, { ...options }, (doc: Doc, finished?: () => void) => doc !== targetDocContext && createViewFunc(doc, finished), remainingDocContext, finished) - ); - } - } - } + focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: () => Promise<{ viewSpec: Opt; childDocView: Opt }>) => { + let contextView: DocumentView | undefined; // view containing context that contains target + while (true) { + docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; + docView.props.focus(docView.rootDoc, options); // focus the view within its container + const { childDocView, viewSpec } = await iterator(); + if (!childDocView) return { viewSpec: viewSpec ?? docView.rootDoc, docView, contextView }; + contextView = docView; + docView = childDocView; } }; + + @action + restoreDocView(viewSpec: Opt, docView: DocumentView, options: DocFocusOptions, contextView: Opt, targetDoc: Doc) { + if (viewSpec && docView) { + if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options); + PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); + Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect); + if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); + if (options.toggleTarget) docView.rootDoc.hidden = !docView.rootDoc.hidden; + if (options.effect) docView.rootDoc[AnimationSym] = options.effect; + + if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) { + // if the docView is a text anchor, the contextView is the PDF/Web/Text doc + contextView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect)); + contextView.textHtmlOverlay = StrCast(targetDoc.textHtml); + DocumentManager._overlayViews.add(contextView); + } + Doc.AddUnHighlightWatcher(() => { + docView.rootDoc[AnimationSym] = undefined; + DocumentManager.removeOverlayViews(); + contextView && (contextView.htmlOverlayEffect = ''); + }); + } + } } export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); - if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) { - dv.props.focus(dv.props.Document, { willPanZoom: true }); - Doc.linkFollowHighlight(dv?.props.Document, false); + if (dv) { + DocumentManager.Instance.showDocumentView(dv, { willZoomCentered: true }); } else { const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null); const showDoc = context || doc; - CollectionDockingView.AddSplit(Doc.BestAlias(showDoc), OpenWhereMod.right) && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc, {})); + DocumentManager.Instance.showDocument(Doc.BestAlias(showDoc), { openLocation: OpenWhere.addRight }, () => DocumentManager.Instance.showDocument(doc, { willZoomCentered: true })); } } ScriptingGlobals.add(DocFocusOrOpen); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 7f0c8a3e8..559958c2b 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -168,7 +168,7 @@ export class DirectoryImportBox extends React.Component { await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer }); Doc.AddDocToList(Doc.GetProto(parent.props.Document), 'data', importContainer); !this.persistent && this.props.removeDocument && this.props.removeDocument(doc); - DocumentManager.Instance.jumpToDocument(importContainer, { willPanZoom: true }, undefined, []); + DocumentManager.Instance.showDocument(importContainer, { willZoomCentered: true }); } runInAction(() => { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index b40c7bdf9..c9a178db7 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -1,15 +1,11 @@ import { action, runInAction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { List } from '../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; -import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { DocumentDecorations } from '../views/DocumentDecorations'; -import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; -import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; @@ -32,46 +28,10 @@ export class LinkFollower { // depending on the followLinkLocation property of the source (or the link itself as a fallback); public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => { const batch = UndoManager.StartBatch('follow link click'); - // open up target if it's not already in view ... - const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => { - const createTabForTarget = (didFocus: boolean) => - new Promise(res => { - const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); - const lightbox = where === OpenWhere.lightbox && DocumentManager.GetContextPath(doc).find(container => container.isLightbox && DocumentManager.Instance.getDocumentView(container)); - const addDocTab = lightbox ? DocumentManager.Instance.getDocumentView(lightbox)?.ComponentView?.addDocTab : undefined; - (addDocTab ?? docViewProps.addDocTab)(doc, where); - setTimeout(() => { - const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise. - if (targDocView) { - targDocView.props.focus(doc, { - willPan: true, - willPanZoom: BoolCast(sourceDoc.followLinkZoom, false), - afterFocus: (didFocus: boolean) => { - finished?.(); - res(ViewAdjustment.resetView); - return new Promise(res2 => res2(ViewAdjustment.doNothing)); - }, - }); - } else { - finished?.(); - res(BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); - } - }, 100); - }); - - if (!sourceDoc.followLinkZoom) { - createTabForTarget(false); - } else { - // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target - docViewProps.focus(sourceDoc, { willPan: true, willPanZoom: BoolCast(sourceDoc.followLinkZoom, true), zoomTime: 1000, zoomScale: 1, afterFocus: createTabForTarget }); - } - }; runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value LinkFollower.traverseLink( linkDoc, sourceDoc, - createViewFunc, - docViewProps.ContainingCollectionDoc, action(() => { batch.end(); Doc.AddUnHighlightWatcher(action(() => (DocumentDecorations.Instance.overrideBounds = false))); @@ -80,7 +40,7 @@ export class LinkFollower { ); }; - public static traverseLink(link: Opt, sourceDoc: Doc, createViewFunc: CreateViewFunc, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { + public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : DocListCast(sourceDoc.links); const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1 const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2 @@ -104,15 +64,17 @@ export class LinkFollower { ) as Doc; if (target) { const doFollow = (canToggle?: boolean) => { + const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle); const options: DocFocusOptions = { playAudio: BoolCast(sourceDoc.followLinkAudio), - toggleTarget: canToggle && BoolCast(sourceDoc.followLinkToggle), + toggleTarget, + noSelect: true, willPan: true, - willPanZoom: BoolCast(sourceDoc.followLinkZoom, false), + willZoomCentered: BoolCast(sourceDoc.followLinkZoom, false), zoomTime: NumCast(sourceDoc.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, - openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox, + openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox), effect: sourceDoc, originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), @@ -125,9 +87,7 @@ export class LinkFollower { } finished?.(); } else { - const containerDocContext = DocumentManager.GetContextPath(target); - const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext; - DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.lightbox), finished), targetContexts, allFinished); + DocumentManager.Instance.showDocument(target, options, allFinished); } }; let movedTarget = false; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 36095700c..3c05af4bb 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -375,7 +375,7 @@ export class SharingManager extends React.Component<{}> { onClick={() => { let context: Opt; if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) { - DocumentManager.Instance.jumpToDocument(this.targetDoc, { willPanZoom: true }, undefined, [context.props.Document]); + DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 73e46a553..4f08a8e22 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -91,18 +91,12 @@ export class InkingStroke extends ViewBoxBaseComponent() { if (anchor) { anchor.backgroundColor = 'transparent'; // /* addAsAnnotation &&*/ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true, inkable: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.rootDoc); return anchor; } return this.rootDoc; }; - scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { - if (this._subContentView) return this._subContentView?.scrollFocus?.(docView, anchor, options); - - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; - }; /** * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space); * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 8d60da0dd..2567d44bb 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -160,7 +160,7 @@ export class LightboxView extends React.Component { if (targetDocView && target) { const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); - targetDocView.focus(target, { originalTarget: target, willPanZoom: true, zoomScale: 0.9 }); + DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target }); } else { if (!target && LightboxView.path.length) { @@ -195,7 +195,7 @@ export class LightboxView extends React.Component { const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); if (docView) { LightboxView._docTarget = target; - target && docView.focus(target, { willPanZoom: true, zoomScale: 0.9 }); + target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { LightboxView.SetLightboxDoc(doc, target); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 95c0f3755..e1ba5943d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -205,7 +205,7 @@ export class MainView extends React.Component { document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.jumpToDocument(doc, { willPan: false }, undefined, []) : null)); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); this.initEventListeners(); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index a5c01490f..ac1b66013 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -16,11 +16,11 @@ import { undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; -import { VideoBox } from './nodes/VideoBox'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); import { LinkManager } from '../util/LinkManager'; +import { Utils } from '../../Utils'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -222,7 +222,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { setTimeout(() => pasteImageBitmap((data_url: any, error: any) => { error && console.log(error); - data_url && VideoBox.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename))); + data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename))); }) ); } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5105cc615..e750dc43a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -335,7 +335,7 @@ export class PropertiesView extends React.Component { NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} PanelWidth={panelWidth} PanelHeight={panelHeight} - focus={returnFalse} + focus={emptyFunction} ScreenToLocalTransform={this.getTransform} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index b9af28413..74ea624a6 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -139,7 +139,7 @@ export class SidebarAnnos extends React.Component { return target; }; makeDocUnfiltered = (doc: Doc) => { - if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) { + if (DocListCast(this.props.rootDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) { if (this.props.layoutDoc[this.filtersKey]) { this.props.layoutDoc[this.filtersKey] = new List(); } diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 0e1601f35..6035871cd 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -3,7 +3,7 @@ import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; -import { Copy, Id } from '../../../fields/FieldSymbols'; +import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; @@ -11,7 +11,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; -import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -19,7 +18,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { LightboxView } from '../LightboxView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; @@ -187,20 +186,16 @@ export class CollectionNoteTakingView extends CollectionSubView() { // let's dive in and get the actual document we want to drag/move around focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); - let focusSpeed = 0; const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + let focusSpeed = options.zoomTime ?? 500; + smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + return focusSpeed; } } - const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing); - this.props.focus(this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), - }); }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 6fd4c8c0a..572fbfec2 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -765,7 +765,7 @@ class StackedTimelineAnchor extends React.Component // starting the drag event for anchor resizing onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { - this.props._timeline?.setPointerCapture(e.pointerId); + //this.props._timeline?.setPointerCapture(e.pointerId); const newTime = (e: PointerEvent) => { const rect = (e.target as any).getBoundingClientRect(); return this.props.toTimeline(e.clientX - rect.x, rect.width); @@ -793,7 +793,7 @@ class StackedTimelineAnchor extends React.Component }, e => { this.props.setTime(newTime(e)); - this.props._timeline?.releasePointerCapture(e.pointerId); + // this.props._timeline?.releasePointerCapture(e.pointerId); undo?.end(); }, emptyFunction diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 6314b4529..4805a748b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -22,7 +22,7 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; @@ -250,20 +250,17 @@ export class CollectionStackingView extends CollectionSubView { Doc.BrushDoc(doc); - let focusSpeed = 0; const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + let focusSpeed = options.zoomTime ?? 500; + smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + return focusSpeed; } } - const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing; - this.props.focus(this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), - }); + return undefined; }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 437b22040..89b2fbfe3 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -66,11 +66,11 @@ export class CollectionTimeView extends CollectionSubView() { }; @action - scrollPreview = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { + scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => { // if in preview, then override document's fields with view spec - this._focusFilters = StrListCast(anchor.presPinDocFilters); + this._focusFilters = StrListCast(anchor.presDocFilters); this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters); - this._focusPivotField = StrCast(anchor.presPinPivotField); + this._focusPivotField = StrCast(anchor.presPivotField); return undefined; }; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 042c2b71b..99283996e 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -305,7 +305,7 @@ export class TabDocView extends React.Component { const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); - setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), { willPan: true }, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things + setTimeout(() => DocumentManager.Instance.showDocument(docList.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things } setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } @@ -388,13 +388,17 @@ export class TabDocView extends React.Component { getCurrentFrame = () => { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); }; + static Activate = (tabDoc: Doc) => { + const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc); + tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) + return tab !== undefined; + }; @action focusFunc = (doc: Doc, options: DocFocusOptions) => { - options?.afterFocus?.(false); - if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) } + return undefined; }; active = () => this._isActive; @observable _forceInvalidateScreenToLocal = 0; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 79832eb39..63b566f28 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { DateField } from '../../../../fields/DateField'; -import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../../fields/Doc'; +import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -15,13 +15,12 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnTransparent, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { HistoryUtil } from '../../../util/History'; import { InteractionUtils } from '../../../util/InteractionUtils'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; @@ -33,15 +32,15 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; +import { DocumentDecorations } from '../../DocumentDecorations'; import { GestureOverlay } from '../../GestureOverlay'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { PinProps, PresBox } from '../../nodes/trails/PresBox'; -import { VideoBox } from '../../nodes/VideoBox'; import { CreateImage } from '../../nodes/WebBoxRenderer'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; @@ -52,8 +51,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); -import { DocumentDecorations } from '../../DocumentDecorations'; -import { PresEffect } from '../../nodes/trails'; export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -300,6 +297,30 @@ export class CollectionFreeFormView extends CollectionSubView= -1e-4 && curTime <= endTime); } + focus = (anchor: Doc, options: DocFocusOptions) => { + const xfToCollection = options?.docTransform ?? Transform.Identity(); + const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; + const cantTransform = this.fitContentsToBox || ((this.rootDoc._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 + const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale); + // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... + if (didMove) { + const focusTime = options?.instant ? 0 : options.zoomTime ?? 500; + options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale); + this.setPan(panX, panY, focusTime, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow + return focusTime; + } + }; + + getView = async (doc: Doc): Promise> => { + return new Promise>(res => { + const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + findDoc(dv => res(dv)); + }); + }; + @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false; @@ -1199,79 +1220,6 @@ export class CollectionFreeFormView extends CollectionSubView(res => setTimeout(() => res(runInAction(() => (this._panZoomTransition = 0))), this._panZoomTransition)); // set transition to be smooth, then reset } - _focusCount = 0; - focusDocument = (doc: Doc, options: DocFocusOptions) => { - const state = HistoryUtil.getState(); - - // TODO This technically isn't correct if type !== "doc", as - // currently nothing is done, but we should probably push a new state - if (state.type === 'doc' && this.Document._panX !== undefined && this.Document._panY !== undefined) { - const init = state.initializers![this.Document[Id]]; - if (!init) { - state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) }; - HistoryUtil.pushState(state); - } else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) { - init.panX = NumCast(this.Document._panX); - init.panY = NumCast(this.Document._panY); - HistoryUtil.pushState(state); - } - } - // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) { - // SelectionManager.DeselectAll(); - // } - if (this.props.getScrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) { - this.props.focus(doc, options); - } else { - const xfToCollection = options?.docTransform ?? Transform.Identity(); - const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willPanZoom ? this.Document[this.scaleFieldKey] : undefined }; - const newState = HistoryUtil.getState(); - const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); - const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willPanZoom) ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willPanZoom ? options?.zoomScale || 0.75 : undefined); - if (!cantTransform) { - // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection - newState.initializers![this.Document[Id]] = { panX, panY }; - HistoryUtil.pushState(newState); - } - // focus on the document in the collection - const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale); - const focusSpeed = options?.instant ? 0 : didMove ? options.zoomTime ?? 500 : 0; - // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... - if (didMove) { - options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale); - this.setPan(panX, panY, focusSpeed, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow - } - - const focusCount = ++this._focusCount; - const startTime = Date.now(); - // focus on this collection within its parent view. the parent view after focusing determines whether to reset the view change within the collection - const endFocus = async (moved: boolean) => { - doc.hidden && Doc.UnHighlightDoc(doc); - const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing; - if (resetView) { - const restoreState = savedState; - if (typeof restoreState !== 'boolean') { - this.Document._panX = restoreState.panX; - this.Document._panY = restoreState.panY; - this.Document[this.scaleFieldKey] = restoreState.scale; - } - } - this._focusCount === focusCount && didMove && runInAction(() => (this._panZoomTransition = 0)); - return resetView; - }; - const xf = !cantTransform - ? Transform.Identity() - : this.props.isAnnotationOverlay - ? new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc)) - : new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1); - - this.props.focus(!cantTransform ? this.rootDoc : doc, { - ...options, - docTransform: xf, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))), - }); - } - }; - calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { const layoutdoc = Doc.Layout(doc); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); @@ -1371,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView { - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - if (options.preview) { - this._focusFilters = StrListCast(anchor.presPinDocFilters); - this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters); - return undefined; - } - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; - }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document - 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.CollectionAnchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), unrendered: true, presTransition: 500, annotationOn: this.rootDoc }); @@ -1744,7 +1681,7 @@ export class CollectionFreeFormView extends CollectionSubView { - const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); + const returnedFilename = await Utils.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); cb(returnedFilename as string, nativeWidth, nativeHeight); }) .catch(function (error: any) { @@ -2303,21 +2240,21 @@ class CollectionFreeFormBackgroundGrid extends React.Component { - if (!didMove) { - const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; - const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 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 - await ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5); - } - return ViewAdjustment.doNothing; - }, - }); - Doc.linkFollowHighlight(dv?.props.Document, false); + if ( + dv.props.focus(dv.props.Document, { + willZoomCentered: true, + zoomScale: 0.8, + zoomTime: browseTransitionTime, + }) === undefined + ) { + const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; + const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 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.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime); + Doc.linkFollowHighlight(dv?.props.Document, false); + } } ScriptingGlobals.add(CollectionBrowseClick); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bd33c4d80..a73627165 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,7 +19,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { OpenWhere } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; -import { VideoBox } from '../../nodes/VideoBox'; import { pasteImageBitmap } from '../../nodes/WebBoxRenderer'; import { PreviewCursor } from '../../PreviewCursor'; import { SubCollectionViewProps } from '../CollectionSubView'; @@ -167,7 +166,7 @@ export class MarqueeView extends React.Component { error && console.log(error); data && - VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => { + Utils.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => { this.props.Document['thumb-frozen'] = new ImageField(returnedfilename); }); }) diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index b9cda7130..d54e8ce98 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -152,7 +152,7 @@ export class CollectionLinearView extends CollectionSubView() { Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => ( <> - DocumentManager.Instance.jumpToDocument(clip.rootDoc, { willPanZoom: true }, undefined, [])}> + DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}> {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} clip.ComponentView?.TogglePause?.()} />{' '} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index b88da5191..88d045fa7 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -234,7 +234,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { onChildClickHandler = () => ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); - focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { @@ -260,7 +259,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { hideResizeHandles={this.props.childHideResizeHandles?.()} hideDecorationTitle={this.props.childHideDecorationTitle?.()} fitContentsToBox={this.props.fitContentsToBox} - focus={this.focusDocument} + focus={this.props.focus} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 407deaabd..f18917bef 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -234,7 +234,6 @@ export class CollectionMultirowView extends CollectionSubView() { onChildClickHandler = () => ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); - focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index 97a6c5c18..18ddd881b 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -214,7 +214,7 @@ export class CollectionSchemaCell extends React.Component { const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); // Jump to the this document - DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc)); + DocumentManager.Instance.showDocument(this._rowDoc, { willPan: true }, () => this.props.setPreviewDoc(this._rowDoc)); } }; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index a2a2255e6..cdac91a62 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -139,7 +139,7 @@ export class LinkMenuItem extends React.Component { ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined; - if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, { instant: true }); + if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true }); LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); } } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 3966aecf6..4c26468b9 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -71,12 +71,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val))); CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); - CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode))); - const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so - doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) + CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode))); + const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so + //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); targetDoc.dataTransition = 'inherit'; }); @@ -190,7 +190,6 @@ export class CollectionFreeFormDocumentView extends DocComponent this.sizeProvider?.width || this.props.PanelWidth?.(); panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.(); screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y); - focusDoc = (doc: Doc) => this.props.focus(doc, {}); returnThis = () => this; render() { TraceMobx(); diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index dd03b9b99..ecffe6c4f 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, OmitKeys, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; @@ -12,6 +13,7 @@ import { StyleProp } from '../StyleProvider'; import './ComparisonBox.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { PinProps, PresBox } from './trails'; import React = require('react'); @observer @@ -24,6 +26,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { this._disposers[disposerId]?.(); if (ele) { @@ -72,6 +78,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { + const anchor = Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc }); + if (anchor) { + if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; + /* addAsAnnotation &&*/ this.addDocument(anchor); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc); + return anchor; + } + return this.rootDoc; + }; + @undoBatch clearDoc = (e: React.MouseEvent, fieldKey: string) => { e.stopPropagation; // prevent click event action (slider movement) in registerSliding @@ -103,7 +120,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { - whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true }); + //whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true }); }} {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} isContentActive={returnFalse} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f2aaa5cbc..dcccd1143 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -66,11 +66,6 @@ declare class MediaRecorder { constructor(e: any); } -export enum ViewAdjustment { - resetView = 1, - doNothing = 0, -} - export enum OpenWhere { lightbox = 'lightbox', add = 'add', @@ -96,33 +91,30 @@ export enum OpenWhereMod { } export interface DocFocusOptions { - originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab willPan?: boolean; // determines whether to pan to target document - willPanZoom?: boolean; // determines whether to zoom in on target document + willZoomCentered?: boolean; // determines whether to zoom in on target document zoomScale?: number; // percent of containing frame to zoom into document zoomTime?: number; - afterFocus?: DocAfterFocusFunc; // function to call after focusing on a document docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) preview?: boolean; // whether changes should be previewed by the componentView or written to the document effect?: Doc; // animation effect for focus noSelect?: boolean; // whether target should be selected after focusing playAudio?: boolean; // whether to play audio annotation on focus - openInLightbox?: boolean; // whether to open target in lightbox or just focus on it + openLocation?: string; // where to open a missing document zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections toggleTarget?: boolean; // whether to toggle target on and off originatingDoc?: Doc; // document that triggered the focus easeFunc?: 'linear' | 'ease'; // transition method for scrolling } -export type DocAfterFocusFunc = (notFocused: boolean) => Promise; -export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void; +export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { updateIcon?: () => void; // updates the icon representation of the document getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) - scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus - scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus + scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; + getView?: (doc: Doc) => Promise>; // returns a nested DocumentView for the specified doc or undefined addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views @@ -131,10 +123,10 @@ export interface DocComponentView { getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; - Pause?: () => void; - IsPlaying?: () => boolean; - TogglePause?: (keep?: boolean) => void; - setFocus?: () => void; + Pause?: () => void; // pause a media document (eg, audio/video) + IsPlaying?: () => boolean; // is a media document playing + TogglePause?: (keep?: boolean) => void; // toggle media document playing state + setFocus?: () => void; // sets input focus to the componentView componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; incrementalRendering?: () => void; fieldKey?: string; @@ -583,32 +575,15 @@ export class DocumentViewInternal extends DocComponent { - LightboxView.SetCookie(StrCast(anchor['cookies-set'])); - - // Restore viewing specification of target by reading them out of the anchor and applying to the target doc. - const presItem = DocCast(options.originatingDoc); // if originating doc was a presItem, then anchor will be its proto. use presItem instead - const targetMatch = Doc.AreProtosEqual(anchor, this.rootDoc) || (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) ? true : false; - const scrollFocus = - (LinkDocPreview.LinkInfo ? this._componentView?.scrollPreview : undefined) ?? - this._componentView?.scrollFocus ?? - ((docView: DocumentView, anchor: Doc, options: DocFocusOptions) => (focusSpeed => (PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined))(options.instant ? 0 : options.zoomTime ?? 500)); - const focusSpeed = targetMatch && scrollFocus?.(this.props.DocumentView(), presItem?.proto === anchor ? presItem : anchor, options); - - // FOCUS: navigate through the display hierarchy making sure the target is in view - const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing; - const startTime = Date.now(); - this.props.focus(options?.docTransform ? anchor : this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => - new Promise(async res => - setTimeout( - async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), // - didFocus ? Math.max(0, (options.zoomTime ?? 500) - (Date.now() - startTime)) : 0 - ) - ), - }); + defaultRestoreTargetView = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => { + const targetMatch = + Doc.AreProtosEqual(anchor, this.rootDoc) || // anchor is this document, so anchor's properties apply to this document + (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an unrendered annotation on this document, so anchor properties applie to this document + ? true + : false; + return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; }; + onClick = action((e: React.MouseEvent | React.PointerEvent) => { if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { let stopPropagate = true; @@ -1201,7 +1176,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints} @@ -1242,8 +1217,8 @@ export class DocumentViewInternal extends DocComponent Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) || Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) || - ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) || - ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc)) + ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) || + ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc)) ); } @computed get allLinks() { @@ -1721,7 +1696,6 @@ export class DocumentView extends React.Component { } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); - focus = (doc: Doc, options: DocFocusOptions) => this.docView?.focus(doc, options); getBounds = () => { if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 78ba499ec..d41a4bb82 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -465,7 +465,7 @@ export class FilterBox extends ViewBoxBaseComponent() { isContentActive={returnTrue} whenChildContentsActiveChanged={returnFalse} treeViewHideTitle={true} - focus={returnFalse} + focus={emptyFunction} onCheckedClick={this.scriptField} treeViewHideHeaderFields={false} dontRegisterView={true} diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 8fa01c97a..75b4907cd 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -15,6 +15,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { DocFocusOptions, DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { PinProps, PresBox } from './trails'; const EquationSchema = createSchema({}); @@ -37,23 +38,18 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent componentDidMount() { this.props.setContentView?.(this); reaction( - () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange], + () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.rootDoc.xRange, this.rootDoc.yRange], () => this.createGraph() ); } - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); - anchor.xRange = new List(Array.from(this._plot.options.xAxis.domain)); - anchor.yRange = new List(Array.from(this._plot.options.yAxis.domain)); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc); + anchor.presXRange = new List(Array.from(this._plot.options.xAxis.domain)); + anchor.presYRange = new List(Array.from(this._plot.options.yAxis.domain)); if (addAsAnnotation) this.addDocument(anchor); return anchor; }; - @action - scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { - this.dataDoc.xRange = new List(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10])))); - this.dataDoc.yRange = new List(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9])))); - return 0; - }; createGraph = (ele?: HTMLDivElement) => { this._plotEle = ele || this._plotEle; const width = this.props.PanelWidth(); @@ -65,8 +61,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent target: '#' + this._plotEle.id, width, height, - xAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]) }, - yAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]) }, + xAxis: { domain: Cast(this.rootDoc.xRange, listSpec('number'), [-10, 10]) }, + yAxis: { domain: Cast(this.rootDoc.yRange, listSpec('number'), [-1, 9]) }, grid: true, data: fns.map(fn => ({ fn, diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 363cd1d94..c6d4cb694 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -8,7 +8,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -17,6 +17,7 @@ import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_ser import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; +import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../../views/ContextMenu'; @@ -26,15 +27,13 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView'; +import { OpenWhere } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; import { PinProps, PresBox } from './trails'; import React = require('react'); import Color = require('color'); -import { LinkDocPreview } from './LinkDocPreview'; -import { DocumentManager } from '../../util/DocumentManager'; export const pageSchema = createSchema({ googlePhotosUrl: 'string', diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 1eab06381..bbe9b4323 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -253,7 +253,7 @@ export class LinkDocPreview extends React.Component { { const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor, {}); + targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {}); }} Document={this._targetDoc!} moveDocument={returnFalse} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6b2f2246a..5f207cc0d 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -11,6 +11,7 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; @@ -27,7 +28,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './PDFBox.scss'; import { PinProps, PresBox } from './trails'; -import { VideoBox } from './VideoBox'; import React = require('react'); @observer @@ -141,7 +141,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - VideoBox.convertDataUri(data_url, region[Id]).then(returnedfilename => + Utils.convertDataUri(data_url, region[Id]).then(returnedfilename => setTimeout( action(() => { croppingProto.data = new ImageField(returnedfilename); @@ -207,17 +207,16 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - let didToggle = false; - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - didToggle = true; - } - if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + focus = (anchor: Doc, options: DocFocusOptions) => { this._initialScrollTarget = anchor; - PresBox.restoreTargetDocView(docView, anchor, options.zoomTime ?? 500); - return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.presPinViewScroll, NumCast(anchor.y)), options) ?? (didToggle ? 1 : undefined); + return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.presViewScroll)), options); + }; + + getView = async (doc: Doc) => { + if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); }; + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { let ele: Opt = undefined; if (this._pdfViewer?.selectionContent()) { @@ -296,7 +295,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._playing; - _keepCurrentlyPlaying = false; TogglePause = () => { if (!this._playing) this.Play(); else { @@ -369,7 +344,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); + Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); } }; @@ -439,6 +414,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + // return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + // }; + // extracts video thumbnails and saves them as field of doc getVideoThumbnails = () => { if (this.dataDoc.thumbnails !== undefined) return; @@ -455,7 +434,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent a.textInlineAnnotations); } @computed get webField() { - return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; + return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url; } @computed get webThumb() { return ( @@ -163,7 +163,7 @@ export class WebBox extends ViewBoxAnnotatableComponent + Utils.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename => setTimeout( action(() => { this.rootDoc.thumbLockout = false; @@ -188,10 +188,10 @@ export class WebBox extends ViewBoxAnnotatableComponent this._urlHash + '-annotations'; const reqdFuncs: { [key: string]: string } = {}; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) - reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`; + reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`; reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`; - reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`; - DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs); + reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`; + DocUtils.AssignScripts(this.rootDoc, {}, reqdFuncs); }); reaction( () => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), @@ -294,32 +294,25 @@ export class WebBox extends ViewBoxAnnotatableComponent void) => (this._setBrushViewer = func); brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view); - focus = (doc: Doc, options: DocFocusOptions) => { - !doc.unrendered && this.props.DocumentView?.() && this.scrollFocus(this.props.DocumentView?.(), doc, {}); - this.props.focus(doc, options); - }; - scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { - if (this._url && StrCast(anchor.webUrl) !== this._url) this.submitURL(StrCast(anchor.webUrl), options.preview); - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - } - if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + focus = (anchor: Doc, options: DocFocusOptions) => { if (anchor !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = !anchor[HeightSym]() - ? NumCast(anchor.y) - : Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - if (scrollTo !== undefined && this._initialScroll === undefined) { - this.goTo(scrollTo, focusSpeed, options.easeFunc); - PresBox.restoreTargetDocView(docView, anchor, focusSpeed); - return focusSpeed; - } else if (!this._webPageHasBeenRendered || !this._scrollHeight || this._initialScroll !== undefined) { - this._initialScroll = scrollTo; - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; + const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); + if (scrollTo !== undefined) { + if (this._initialScroll === undefined) { + this.goTo(scrollTo, options.zoomTime ?? 500, options.easeFunc); + } else { + this._initialScroll = scrollTo; + } } } - return undefined; + }; + + getView = async (doc: Doc) => { + if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify(); + if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl)); + if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); }; sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { @@ -545,14 +538,14 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'), []); - const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []); + const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'), []); + const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []); if (checkAvailable) return future.length; runInAction(() => { if (future.length) { const curUrl = this._url; - this.dataDoc[this.fieldKey + '-history'] = new List([...history, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + this.rootDoc[this.fieldKey + '-history'] = new List([...history, this._url]); + this.rootDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -566,15 +559,15 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string')); - const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []); + const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string')); + const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []); if (checkAvailable) return history.length; runInAction(() => { if (history.length) { const curUrl = this._url; - if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List([this._url]); - else this.dataDoc[this.fieldKey + '-future'] = new List([...future, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); + if (future === undefined) this.rootDoc[this.fieldKey + '-future'] = new List([this._url]); + else this.rootDoc[this.fieldKey + '-future'] = new List([...future, this._url]); + this.layoutDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -601,11 +594,11 @@ export class WebBox extends ViewBoxAnnotatableComponent([...(history || []), url]); + this.rootDoc[this.fieldKey + '-history'] = new List([...(history || []), url]); this.layoutDoc._scrollTop = 0; if (this._webPageHasBeenRendered) { this.layoutDoc.thumb = undefined; @@ -616,7 +609,7 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />; @@ -849,7 +842,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); if (!nativeWidth) { - const defaultNativeWidth = this.dataDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym](); + const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym](); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth); nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); @@ -1006,6 +999,7 @@ export class WebBox extends ViewBoxAnnotatableComponent (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none'); render() { + setTimeout(() => DocListCast(this.rootDoc[this.annotationKey]).forEach(doc => (doc.webUrl = this._url))); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const scale = previewScale * (this.props.NativeDimScaling?.() || 1); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index dc7a11e6e..d857b150c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -230,16 +230,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { - this.toggleSidebar(false); - return true; - } - return this.props.addDocTab(doc, where); - }; - getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { - if (!pinProps) return this.rootDoc; + if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc; const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); this.addDocument(anchor); this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation); @@ -948,12 +940,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - let didToggle = false; - if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - didToggle = true; + getView = async (doc: Doc) => { + if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + !this.SidebarShown && this.toggleSidebar(false); + setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); } + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + }; + focus = (textAnchor: Doc, options: DocFocusOptions) => { + const focusSpeed = options.zoomTime ?? 500; const textAnchorId = textAnchor[Id]; const findAnchorFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; @@ -992,7 +987,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) { - !options.instant && (this._focusSpeed = 500); + !options.instant && (this._focusSpeed = focusSpeed); let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected @@ -1004,9 +999,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); } } - const finalFocusSpeed = this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; - PresBox.restoreTargetDocView(docView, textAnchor, finalFocusSpeed ?? 0); - return finalFocusSpeed; // if we actually scrolled, then return some focusSpeed }; // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc. diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index fd202590e..eb91c82f3 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -12,7 +12,7 @@ height: 100%; min-height: 35px; letter-spacing: 2px; - overflow: hidden; + //overflow: hidden; transition: 0.7s opacity ease; .presBox-listCont { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ab4822117..94962650a 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -46,6 +46,7 @@ export interface pinDataTypes { pivot?: boolean; temporal?: boolean; clippable?: boolean; + datarange?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; @@ -329,6 +330,7 @@ export class PresBox extends ViewBoxBaseComponent() { const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform); const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType); const clippable = [DocumentType.COMPARISON].includes(targetType); + const datarange = [DocumentType.FUNCPLOT].includes(targetType); const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(targetType) && target?.activeFrame === undefined; const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined; const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined; @@ -350,23 +352,33 @@ export class PresBox extends ViewBoxBaseComponent() { if ( bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) || bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) || - bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) || + bestTarget.rotation !== NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)) || bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) || bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height)) ) { bestTarget._dataTransition = `all ${transTime}ms`; bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x)); bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y)); - bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation)); + bestTarget.rotation = NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)); bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width)); bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height)); setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); changed = true; } } - if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presPinClipWidth !== undefined)) { - if (bestTarget._clipWidth !== activeItem.presPinClipWidth) { - bestTarget._clipWidth = activeItem.presPinClipWidth; + if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) { + if (bestTarget.xRange !== activeItem.presXRange) { + bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy](); + changed = true; + } + if (bestTarget.yRange !== activeItem.presYRange) { + bestTarget.yRange = (activeItem.presYRange as ObjectField)?.[Copy](); + changed = true; + } + } + if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) { + if (bestTarget._clipWidth !== activeItem.presClipWidth) { + bestTarget._clipWidth = activeItem.presClipWidth; changed = true; } } @@ -381,8 +393,8 @@ export class PresBox extends ViewBoxBaseComponent() { Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor; changed = true; } - if (bestTarget.color !== activeItem.color) { - Doc.GetProto(bestTarget).color = activeItem.color; + if (bestTarget.color !== activeItem.presColor) { + Doc.GetProto(bestTarget).color = activeItem.presColor; changed = true; } if (bestTarget.width !== activeItem.width) { @@ -394,31 +406,31 @@ export class PresBox extends ViewBoxBaseComponent() { changed = true; } } - if ((pinDataTypes?.viewType && activeItem.presPinViewType !== undefined) || (!pinDataTypes && activeItem.presPinViewType !== undefined)) { - if (bestTarget._viewType !== activeItem.presPinViewType) { - bestTarget._viewType = activeItem.presPinViewType; + if ((pinDataTypes?.viewType && activeItem.presViewType !== undefined) || (!pinDataTypes && activeItem.presViewType !== undefined)) { + if (bestTarget._viewType !== activeItem.presViewType) { + bestTarget._viewType = activeItem.presViewType; changed = true; } } - if ((pinDataTypes?.filters && activeItem.presPinDocFilters !== undefined) || (!pinDataTypes && activeItem.presPinDocFilters !== undefined)) { - if (bestTarget.docFilters !== activeItem.presPinDocFilters) { - bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presPinDocFilters as ObjectField) || new List([]); + if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) { + if (bestTarget.docFilters !== activeItem.presDocFilters) { + bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List([]); changed = true; } } - if ((pinDataTypes?.pivot && activeItem.presPinPvitoField !== undefined) || (!pinDataTypes && activeItem.presPinPivotField !== undefined)) { - if (bestTarget.pivotField !== activeItem.presPinPivotField) { - bestTarget.pivotField = activeItem.presPinPivotField; + if ((pinDataTypes?.pivot && activeItem.presPivotField !== undefined) || (!pinDataTypes && activeItem.presPivotField !== undefined)) { + if (bestTarget.pivotField !== activeItem.presPivotField) { + bestTarget.pivotField = activeItem.presPivotField; bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView changed = true; } } - if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presPinViewScroll !== undefined)) { - if (bestTarget._scrollTop !== activeItem.presPinViewScroll) { - bestTarget._scrollTop = activeItem.presPinViewScroll; + if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) { + if (bestTarget._scrollTop !== activeItem.presViewScroll) { + bestTarget._scrollTop = activeItem.presViewScroll; changed = true; const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { @@ -479,7 +491,7 @@ export class PresBox extends ViewBoxBaseComponent() { }); setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10); } - if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPinViewX !== undefined || activeItem.presPinViewScale !== undefined))) { + if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) { const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; @@ -492,10 +504,10 @@ export class PresBox extends ViewBoxBaseComponent() { dv.ComponentView?.brushView?.(viewport); } } else { - if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) { - bestTarget._panX = activeItem.presPinViewX; - bestTarget._panY = activeItem.presPinViewY; - bestTarget._viewScale = activeItem.presPinViewScale; + if (bestTarget._panX !== activeItem.presPanX || bestTarget._panY !== activeItem.presPanY || bestTarget._viewScale !== activeItem.presViewScale) { + bestTarget._panX = activeItem.presPanX; + bestTarget._panY = activeItem.presPanY; + bestTarget._viewScale = activeItem.presViewScale; changed = true; } } @@ -514,7 +526,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presPinLayout = true; pinDoc.presX = NumCast(targetDoc.x); pinDoc.presY = NumCast(targetDoc.y); - pinDoc.presRot = NumCast(targetDoc.rotation); + pinDoc.presRotation = NumCast(targetDoc.rotation); pinDoc.presWidth = NumCast(targetDoc.width); pinDoc.presHeight = NumCast(targetDoc.height); } @@ -526,6 +538,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinProps.pinData.pannable || pinProps.pinData.viewType || pinProps.pinData.clippable || + pinProps.pinData.datarange || pinProps.pinData.dataview || pinProps.pinData.textview || pinProps.pinData.poslayoutview || @@ -546,8 +559,12 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presWidth = targetDoc._width; pinDoc.presHeight = targetDoc._height; } - if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop; - if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth; + if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._scrollTop; + if (pinProps.pinData.clippable) pinDoc.presClipWidth = targetDoc._clipWidth; + if (pinProps.pinData.datarange) { + pinDoc.presXRange = undefined; //targetDoc?.xrange; + pinDoc.presYRange = undefined; //targetDoc?.yrange; + } if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List( DocListCast(targetDoc[fkey] as ObjectField).map(d => @@ -564,13 +581,13 @@ export class PresBox extends ViewBoxBaseComponent() { }) ) ); - if (pinProps.pinData.viewType) pinDoc.presPinViewType = targetDoc._viewType; - if (pinProps.pinData.filters) pinDoc.presPinDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField); - if (pinProps.pinData.pivot) pinDoc.presPinPivotField = targetDoc._pivotField; + if (pinProps.pinData.viewType) pinDoc.presViewType = targetDoc._viewType; + if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField); + if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField; if (pinProps.pinData.pannable) { - pinDoc.presPinViewX = NumCast(targetDoc._panX); - pinDoc.presPinViewY = NumCast(targetDoc._panY); - pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); + pinDoc.presPanX = NumCast(targetDoc._panX); + pinDoc.presPanY = NumCast(targetDoc._panY); + pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1); } if (pinProps.pinData.temporal) { pinDoc.presStartTime = targetDoc._currentTimecode; @@ -582,9 +599,9 @@ export class PresBox extends ViewBoxBaseComponent() { // If pinWithView option set then update scale and x / y props of slide const bounds = pinProps.pinViewport; pinDoc.presPinView = true; - pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); - pinDoc.presPinViewX = bounds.left + bounds.width / 2; - pinDoc.presPinViewY = bounds.top + bounds.height / 2; + pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1); + pinDoc.presPanX = bounds.left + bounds.width / 2; + pinDoc.presPanY = bounds.top + bounds.height / 2; pinDoc.presPinViewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } } @@ -618,14 +635,10 @@ export class PresBox extends ViewBoxBaseComponent() { } finished(); }); - const createDocView = (doc: Doc, finished?: () => void) => { - DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.()); - LightboxView.AddDocTab(doc, OpenWhere.lightbox); - }; - PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection); + PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection); }; - static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) { + static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) { if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; @@ -634,7 +647,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500); const options: DocFocusOptions = { willPan: activeItem.presMovement !== PresMovement.None, - willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, + willZoomCentered: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1), zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime), effect: activeItem, @@ -644,38 +657,23 @@ export class PresBox extends ViewBoxBaseComponent() { zoomTextSelections: BoolCast(activeItem.presZoomText), playAudio: BoolCast(activeItem.presPlayAudio), }; - const restoreLayout = () => { - // After navigating to the document, if it is added as a presPinView then it will - // adjust the pan and scale to that of the pinView when it was added. - const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined; - if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) { - // targetDoc may or may not be displayed. so get the first available document (or alias) view that matches targetDoc and use it - // PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500), undefined, targetDoc); - } - }; - const finishAndRestoreLayout = () => { - finished?.(); - restoreLayout(); - }; - const containerDocContext = DocumentManager.GetContextPath(targetDoc); - - let context = containerDocContext.length ? containerDocContext[0] : targetDoc; if (activeItem.presOpenInLightbox) { - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) { - context = DocCast(targetDoc.annotationOn) ?? targetDoc; - LightboxView.SetLightboxDoc(context); // openInTab(targetDoc); + const context = DocCast(targetDoc.annotationOn) ?? targetDoc; + if (!DocumentManager.Instance.getLightboxDocumentView(context)) { + LightboxView.SetLightboxDoc(context); } } if (targetDoc) { if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined; DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => { - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(context.annotationOn) ?? context)) { + // if target or the doc it annotates is not in the lightbox, then close the lightbox + if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { LightboxView.SetLightboxDoc(undefined); } - DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout); + DocumentManager.Instance.showDocument(targetDoc, options, finished); }); - } else finishAndRestoreLayout(); + } else finished?.(); } /** @@ -992,11 +990,14 @@ export class PresBox extends ViewBoxBaseComponent() { presDocView && SelectionManager.SelectView(presDocView, false); }; - focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc); + focusElement = (doc: Doc, options: DocFocusOptions) => { + this.selectElement(doc); + return undefined; + }; //Regular click @action - selectElement = async (doc: Doc, noNav = false) => { + selectElement = (doc: Doc, noNav = false) => { CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => clip?.ComponentView?.Pause?.()); if (noNav) { const index = this.childDocs.indexOf(doc); @@ -1192,15 +1193,15 @@ export class PresBox extends ViewBoxBaseComponent() { } } else if (doc.presPinView && presCollection === tagDoc && dv) { // Case B: Document is presPinView and is presCollection - const scale: number = 1 / NumCast(doc.presPinViewScale); + const scale: number = 1 / NumCast(doc.presViewScale); const height: number = dv.props.PanelHeight() * scale; const width: number = dv.props.PanelWidth() * scale; const indWidth = width / 10; const indHeight = Math.max(height / 10, 15); const indEdge = Math.max(indWidth, indHeight); const indFontSize = indEdge * 0.8; - const xLoc: number = NumCast(doc.presPinViewX) - width / 2; - const yLoc: number = NumCast(doc.presPinViewY) - height / 2; + const xLoc: number = NumCast(doc.presPanX) - width / 2; + const yLoc: number = NumCast(doc.presPanY) - height / 2; docs.push(tagDoc); order.push( <> @@ -1233,8 +1234,8 @@ export class PresBox extends ViewBoxBaseComponent() { if ((index = 0)) pathPoints = n1x + ',' + n1y; else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; } else if (doc.presPinView) { - const n1x = NumCast(doc.presPinViewX); - const n1y = NumCast(doc.presPinViewY); + const n1x = NumCast(doc.presPanX); + const n1y = NumCast(doc.presPanY); if ((index = 0)) pathPoints = n1x + ',' + n1y; else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; } @@ -1960,12 +1961,6 @@ export class PresBox extends ViewBoxBaseComponent() { ); } - scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { - // this.gotoDocument(0); - // this.startOrPause(false); - return undefined; - }; - _keyTimer: NodeJS.Timeout | undefined; /** @@ -2041,9 +2036,10 @@ export class PresBox extends ViewBoxBaseComponent() { const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); const activeColor = Colors.LIGHT_BLUE; const inactiveColor = Colors.WHITE; - return mode === CollectionViewType.Carousel3D ? null : ( + return mode === CollectionViewType.Carousel3D || inOverlay ? null : (
{/*
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> @@ -2404,11 +2400,7 @@ export class PresBox extends ViewBoxBaseComponent() { ); } static NavigateToDoc(bestTarget: Doc, activeItem: Doc) { - const openInTab = (doc: Doc, finished?: () => void) => { - CollectionDockingView.AddSplit(doc, OpenWhereMod.right); - finished?.(); - }; - PresBox.NavigateToTarget(bestTarget, activeItem, openInTab); + PresBox.NavigateToTarget(bestTarget, activeItem); } } diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index d19b78dbc..698a2817e 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -297,7 +297,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; activeItem.presX = NumCast(targetDoc.x); activeItem.presY = NumCast(targetDoc.y); - activeItem.presRot = NumCast(targetDoc.rotation); + activeItem.presRotation = NumCast(targetDoc.rotation); activeItem.presWidth = NumCast(targetDoc.width); activeItem.presHeight = NumCast(targetDoc.height); activeItem.presPinLayout = true; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ffc49e4f3..86d14c22e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -179,10 +179,10 @@ export class PDFViewer extends React.Component { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); + const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; - else if (!options.instant && !options.preview) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); + else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { @@ -518,10 +518,6 @@ export class PDFViewer extends React.Component { return this.props.styleProvider?.(doc, props, property); }; - focus = (doc: Doc, options: DocFocusOptions) => { - !doc.unrendered && this.props.DocumentView?.() && this.scrollToAnnotation(doc); - this.props.focus(doc, options); - }; renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => (
{ PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} dropAction={'alias'} - focus={this.focus} docFilters={docFilters} select={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 4c4275ce7..3ba60edd7 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -414,7 +414,7 @@ export class SearchBox extends ViewBoxBaseComponent() { * or opening it in a new tab. */ selectElement = async (doc: Doc, finishFunc: () => void) => { - await DocumentManager.Instance.jumpToDocument(doc, { willPanZoom: true }, undefined, [], finishFunc); + await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, finishFunc); }; /** diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 5f02bd73d..16da0f9e2 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -218,7 +218,12 @@ export class ComputedField extends ScriptField { doc[`${fieldKey}-indexed`] = flist; } const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {}); - const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); + const setField = ScriptField.CompileScript( + `{setIndexVal (self['${fieldKey}-indexed'], self.${interpolatorKey}, value); console.log(self["data-indexed"][self.${interpolatorKey}],self.data,self["data-indexed"]))}`, + { value: 'any' }, + false, + {} + ); return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 6024705ec..e517e7604 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -335,8 +335,10 @@ export function setter(target: any, in_prop: string | symbol | number, value: an return true; } } - if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { - return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + if (target.__fields[prop] instanceof ComputedField) { + if (target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { + return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + } } return _setter(target, prop, value, receiver); } -- cgit v1.2.3-70-g09d2 From 0fb643d13dc7dbdcd4ae0f4785f8f2bd3fbbc764 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 10 Mar 2023 21:48:11 -0500 Subject: fixed image anchors to save zoom/pan info and to focus() on content when following anchors. fixed progresivized items in trails to not allow dragging items out or dropping new items in. --- src/client/documents/Documents.ts | 3 ++ src/client/views/collections/TreeView.tsx | 81 +++++++++++++++++-------------- src/client/views/nodes/ImageBox.tsx | 17 ++++++- src/client/views/nodes/MapBox/MapBox.tsx | 7 +-- src/client/views/nodes/trails/PresBox.tsx | 2 +- 5 files changed, 68 insertions(+), 42 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6296b0df4..4dc1c3b71 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -261,6 +261,9 @@ export class DocumentOptions { activeFrame?: number; // the active frame of a document in a frame base collection appearFrame?: number; // the frame in which the document appears viewTransitionTime?: number; // transition duration for view parameters + presPanX?: number; // panX saved as a view spec + presPanY?: number; // panY saved as a view spec + presViewScale?: number; // viewScale saved as a view Spec presTransition?: number; //the time taken for the transition TO a document presDuration?: number; //the duration of the slide in presentation view presZoomText?: boolean; // whether text anchors should shown in a larger box when following links to make them stand out diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index c8dc68eae..054d61d0c 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -8,7 +8,7 @@ import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -169,23 +169,6 @@ export class TreeView extends React.Component { @computed get selected() { return SelectionManager.IsSelected(this._docRef); } - // SelectionManager.Views().lastElement()?.props.Document === this.props.document; } - - @observable _presTimer!: NodeJS.Timeout; - @observable _presKeyEventsActive: boolean = false; - - @observable _selectedArray: ObservableMap = new ObservableMap(); - // the selected item's index - @computed get itemIndex() { - return NumCast(this.doc._itemIndex); - } - // the item that's active - @computed get activeItem() { - return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined; - } - @computed get targetDoc() { - return Cast(this.activeItem?.presentationTargetDoc, Doc, null); - } childDocList(field: string) { const layout = Cast(Doc.LayoutField(this.doc), Doc, null); @@ -195,11 +178,17 @@ export class TreeView extends React.Component { DocListCastOrNull(this.doc[field]) ); // otherwise use the document's data field } + moving: boolean = false; @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { if (this.doc !== target && addDoc !== returnFalse) { + const canAdd1 = (this.props.parentTreeView as any).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this.props.parentTreeView?.doc.data)) instanceof ComputedField); + // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse - if (this.props.removeDoc?.(doc) === true) { - return addDoc(doc); + if (canAdd1 && this.props.removeDoc?.(doc) === true) { + this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.moving = true); + const res = addDoc(doc); + this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.moving = false); + return res; } } return false; @@ -207,6 +196,7 @@ export class TreeView extends React.Component { @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { this.props.treeView.props.select(false); const ind = this.dataDoc[key].indexOf(doc); + const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false); return res; @@ -378,20 +368,32 @@ export class TreeView extends React.Component { const docDragData = de.complete.docDragData; if (docDragData && pt[0] < rect.left + rect.width) { if (docDragData.draggedDocuments[0] === this.doc) return true; - if (this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document)) { + if (this.dropDocuments(docDragData.droppedDocuments, before, inside, docDragData.dropAction, docDragData.removeDocument, docDragData.moveDocument, docDragData.treeViewDoc === this.props.treeView.props.Document)) { e.stopPropagation(); } } }; - dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) { + dropping: boolean = false; + dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, removeDocument: DragManager.RemoveFunction | undefined, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) { const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); - const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd; - const localAdd = (doc: Doc) => (Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false); - const addDoc = !inside ? parentAddDoc : (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean); + const localAdd = (doc: Doc | Doc[]) => { + const innerAdd = (doc: Doc) => { + const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField; + const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); + dataIsComputed && (doc.context = this.doc.context); + return added; + }; + return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); + }; + const addDoc = inside ? localAdd : parentAddDoc; const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument; + const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd; if (canAdd) { - return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false)); + this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true); + const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false)); + this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false); + return res; } return false; } @@ -437,12 +439,16 @@ export class TreeView extends React.Component { const expandedWidth = () => this.props.panelWidth() - leftOffset.width; if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc)) { const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); - const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { - const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - added && (doc.context = this.doc.context); - return added; + const moveDoc = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(doc, target, addDoc); + const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => { + const innerAdd = (doc: Doc) => { + const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; + const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); + dataIsComputed && (doc.context = this.doc.context); + return added; + }; + return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); }; - const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); contentElement = TreeView.GetChildElements( contents instanceof Doc ? [contents] : DocListCast(contents), this.props.treeView, @@ -453,7 +459,7 @@ export class TreeView extends React.Component { this.props.prevSibling, addDoc, remDoc, - this.move, + moveDoc, this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, @@ -533,6 +539,7 @@ export class TreeView extends React.Component { ); const key = (expandKey === 'annotations' ? `${this.fieldKey}-` : '') + expandKey; const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key); + const moveDoc = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(doc, target, addDoc); const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => { // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't), // then the modification would be done here @@ -543,8 +550,10 @@ export class TreeView extends React.Component { docs.push(doc); docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i)); } - const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - added && (doc.context = this.doc.context); + const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; + const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); + !dataIsComputed && added && (doc.context = this.doc.context); + return added; }; const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); @@ -593,7 +602,7 @@ export class TreeView extends React.Component { this.props.prevSibling, addDoc, remDoc, - this.move, + moveDoc, StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, @@ -1064,7 +1073,7 @@ export class TreeView extends React.Component { const before = pt[1] < rect.top + rect.height / 2; const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false); - const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, false)); + const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false)); }; render() { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c6d4cb694..e60a03190 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -27,7 +27,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { OpenWhere } from './DocumentView'; +import { DocFocusOptions, OpenWhere } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; @@ -73,7 +73,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent { const anchor = this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor - Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc }); + Docs.Create.ImageanchorDocument({ + title: 'ImgAnchor:' + this.rootDoc.title, + presPanX: NumCast(this.layoutDoc._panX), + presPanY: NumCast(this.layoutDoc._panY), + presViewScale: Cast(this.layoutDoc._viewScale, 'number', null), + presTransition: 1000, + unrendered: true, + annotationOn: this.rootDoc, + }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation &&*/ this.addDocument(anchor); @@ -433,6 +441,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent this._ffref.current?.focus(anchor, options); + + _ffref = React.createRef(); savedAnnotations = () => this._savedAnnotations; render() { TraceMobx(); @@ -460,6 +471,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent (); private _ref: React.RefObject = React.createRef(); - constructor(props: any) { - super(props); + componentDidMount() { + this.props.setContentView?.(this); } @action @@ -525,7 +526,7 @@ export class MapBox extends ViewBoxAnnotatableComponent AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; /** * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 2d9aaeca4..dda96d8e7 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -557,7 +557,7 @@ export class PresBox extends ViewBoxBaseComponent() { }); setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10); } - if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) && !PresBox.targetRenderedDoc(activeItem)._isGroup) { + if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) && !bestTarget._isGroup) { const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; -- cgit v1.2.3-70-g09d2 From 95c076b64a816c6bd503089f90234f8ce078eae8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 29 Mar 2023 11:51:51 -0400 Subject: fixed sizing of link count button. moved border paths and audio to style provider --- src/client/documents/Documents.ts | 2 + src/client/views/StyleProvider.scss | 24 +-- src/client/views/StyleProvider.tsx | 37 +++- src/client/views/nodes/DocumentLinksButton.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 282 ++++++++----------------- src/client/views/nodes/ImageBox.tsx | 5 +- src/client/views/nodes/VideoBox.tsx | 18 +- 7 files changed, 140 insertions(+), 234 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a110782b2..9b8b9c877 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -221,6 +221,8 @@ export class DocumentOptions { recording?: boolean; // whether WebCam is recording or not autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline. dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. + linkSource?: Doc; // the source document for a collection of backlinks + updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything) toolTip?: string; // tooltip to display on hover toolType?: string; // type of pen tool expertMode?: boolean; // something available only in expert (not novice) mode diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss index b865c8ddd..80c878386 100644 --- a/src/client/views/StyleProvider.scss +++ b/src/client/views/StyleProvider.scss @@ -1,3 +1,5 @@ +.styleProvider-filter, +.styleProvider-audio, .styleProvider-lock { font-size: 10; width: 15; @@ -9,31 +11,21 @@ pointer-events: all; opacity: 0.3; display: flex; + flex-direction: column; color: gold; border-radius: 3px; justify-content: center; cursor: default; } .styleProvider-filter { - font-size: 10; - width: 15; - height: 15; - position: absolute; right: 0; - top: 0; - background: black; - pointer-events: all; - opacity: 0.3; - display: flex; - color: gold; - border-radius: 3px; - justify-content: center; - cursor: default; } -.styleProvider-filter:hover { - opacity: 1; +.styleProvider-audio { + right: 15; } -.styleProvider-lock:hover { +.styleProvider-lock:hover, +.styleProvider-audio:hover, +.styleProvider-filter:hover { opacity: 1; } diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index def0eeef7..50cf8eba7 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -7,7 +7,7 @@ import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocFocusOrOpen } from '../util/DocumentManager'; +import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme, SettingsManager } from '../util/SettingsManager'; @@ -22,6 +22,9 @@ import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); import { KeyValueBox } from './nodes/KeyValueBox'; +import { listSpec } from '../../fields/Schema'; +import { AudioField } from '../../fields/URLField'; +import { Tooltip } from '@material-ui/core'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -177,9 +180,21 @@ export function DefaultStyleProvider(doc: Opt, props: Opt + + + +
+ ), + }; case StyleProp.JitterRotation: return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0; case StyleProp.ShowCaption: @@ -323,10 +338,24 @@ export function DefaultStyleProvider(doc: Opt, props: Opt ); }; + const audio = () => { + const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, 'stopped'); + const audioAnnosCount = (doc: Doc) => StrListCast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations']).length; + if (!doc || props?.renderDepth === -1 || (!audioAnnosCount(doc) && audioAnnoState(doc) === 'stopped')) return null; + const audioIconColors: { [key: string]: string } = { recording: 'red', playing: 'green', stopped: 'blue' }; + return ( + {StrListCast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations-text']).lastElement()}
}> +
DocumentManager.Instance.getFirstDocumentView(doc)?.docView?.playAnnotation()}> + +
+
+ ); + }; return ( <> {lock()} {filter()} + {audio()} ); } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index a40599d85..d6679b46d 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -71,11 +71,7 @@ export class DocumentLinksButton extends React.Component { - if (doubleTap) { - DocumentView.showBackLinks(this.props.View.rootDoc); - } - }), + action((e, doubleTap) => doubleTap && DocumentView.showBackLinks(this.props.View.rootDoc)), undefined, undefined, action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View)) diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f2add9856..091a6b050 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,10 +1,9 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; +import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; -import { AclAdmin, AclEdit, AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; +import { AclPrivate, AnimationSym, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -12,8 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; -import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; -import { MobileInterface } from '../../../mobile/MobileInterface'; +import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../../DocServer'; @@ -28,7 +26,6 @@ import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; -import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -48,7 +45,6 @@ import './DocumentView.scss'; import { FieldViewProps } from './FieldView'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { LinkAnchorBox } from './LinkAnchorBox'; -import { RadialMenu } from './RadialMenu'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); @@ -155,7 +151,6 @@ export interface DocumentViewSharedProps { ContainingCollectionDoc: Opt; suppressSetHeight?: boolean; thumbShown?: () => boolean; - isHovering?: () => boolean; setContentView?: (view: DocComponentView) => any; CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; PanelWidth: () => number; @@ -240,7 +235,6 @@ export interface DocumentViewInternalProps extends DocumentViewProps { NativeWidth: () => number; NativeHeight: () => number; isSelected: (outsideReaction?: boolean) => boolean; - isHovering: () => boolean; select: (ctrlPressed: boolean, shiftPress?: boolean) => void; DocumentView: () => DocumentView; viewPath: () => DocumentView[]; @@ -289,9 +283,6 @@ export class DocumentViewInternal extends DocComponent disposer?.()); } - @action - onRadialMenu = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { - const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1]; - RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15); - - // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), OpenWhere.addRight), icon: "map-pin", selected: -1 }); - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && - RadialMenu.Instance.addItem({ - description: 'Delete', - event: () => { - this.props.ContainingCollectionView?.removeDocument(this.props.Document); - RadialMenu.Instance.closeMenu(); - }, - icon: 'external-link-square-alt', - selected: -1, - }); - // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, OpenWhere.addRight), icon: "trash", selected: -1 }); - RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document, {}), icon: 'map-pin', selected: -1 }); - RadialMenu.Instance.addItem({ description: 'Open', event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: 'trash', selected: -1 }); - - SelectionManager.DeselectAll(); - }; - startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) { if (this._mainCont.current) { const views = SelectionManager.Views().filter(dv => dv.docView?._mainCont.current); @@ -435,7 +402,7 @@ export class DocumentViewInternal extends DocComponent { const targetMatch = Doc.AreProtosEqual(anchor, this.rootDoc) || // anchor is this document, so anchor's properties apply to this document - (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an unrendered annotation on this document, so anchor properties applie to this document + (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an unrendered annotation on this document, so anchor properties apply to this document ? true : false; return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; @@ -573,11 +540,9 @@ export class DocumentViewInternal extends DocComponent document.removeEventListener('pointermove', this.onPointerMove); - cleanupPointerEvents = () => { this.cleanUpInteractions(); - this.cancelMoveEvents(); + document.removeEventListener('pointermove', this.onPointerMove); document.removeEventListener('pointerup', this.onPointerUp); }; @@ -586,7 +551,7 @@ export class DocumentViewInternal extends DocComponent { - const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); + const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from'); if (!portalLink) { DocUtils.MakeLink( { doc: this.props.Document }, @@ -928,25 +893,6 @@ export class DocumentViewInternal extends DocComponent([ - ['recording', 'red'], - ['playing', 'green'], - ['stopped', audioAnnosCount ? 'blue' : 'gray'], - ]); - return this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!this.props.isSelected() && !this.props.isHovering() && this.audioAnnoState !== 'recording') || (!audioAnnosCount && this.audioAnnoState === 'stopped') ? null : ( - {audioTextAnnos?.lastElement()}
}> -
- -
- - ); - } contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents); @computed get contents() { TraceMobx(); @@ -965,10 +911,8 @@ export class DocumentViewInternal extends DocComponent { setTimeout(action(() => (this._retryThumb = 0))); - setTimeout( - action(() => (this._retryThumb = 1)), - 150 - ); + // prettier-ignore + setTimeout(action(() => (this._retryThumb = 1)), 150 ); }} /> )} @@ -978,7 +922,6 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints} - {this.audioAnnoView}
); } - get indicatorIcon() { - if (this.props.Document['acl-Public'] !== SharingPermissions.None) return 'globe-americas'; - else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return 'users'; - else return 'user'; - } - - @undoBatch - hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true); anchorPanelWidth = () => this.props.PanelWidth() || 1; anchorPanelHeight = () => this.props.PanelHeight() || 1; anchorStyleProvider = (doc: Opt, props: Opt, property: string): any => { @@ -1036,12 +970,13 @@ export class DocumentViewInternal extends DocComponent () => (link.linkDisplay = false)); @computed get allLinkEndpoints() { // the small blue dots that mark the endpoints of links TraceMobx(); if (this.props.hideLinkAnchors || this.layoutDoc.hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null; const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.linkDisplay); - return filtered.map((link, i) => ( + return filtered.map(link => (
@@ -1064,26 +999,6 @@ export class DocumentViewInternal extends DocComponent { - const self = this; - const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null); - const anno = audioAnnos?.lastElement(); - if (anno instanceof AudioField && this.audioAnnoState === 'stopped') { - new Howl({ - src: [anno.url.href], - format: ['mp3'], - autoplay: true, - loop: false, - volume: 0.5, - onend: function () { - runInAction(() => (self.dataDoc.audioAnnoState = 'stopped')); - }, - }); - this.dataDoc.audioAnnoState = 'playing'; - } - }; - static recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) { let gumStream: any; let recorder: any; @@ -1131,6 +1046,23 @@ export class DocumentViewInternal extends DocComponent { + const self = this; + const audioAnnoState = this.dataDoc.audioAnnoState ?? 'stopped'; + const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null); + const anno = audioAnnos?.lastElement(); + if (anno instanceof AudioField && audioAnnoState === 'stopped') { + new Howl({ + src: [anno.url.href], + format: ['mp3'], + autoplay: true, + loop: false, + volume: 0.5, + onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')), + }); + this.dataDoc.audioAnnoState = 'playing'; + } + }; captionStyleProvider = (doc: Opt, props: Opt, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); @computed get innards() { @@ -1225,7 +1157,7 @@ export class DocumentViewInternal extends DocComponent { TraceMobx(); - return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DataSym]) === AclPrivate || this.hidden + return !DocCast(this.Document) || GetEffectiveAcl(this.Document[DataSym]) === AclPrivate ? null : this.docContents ?? (
(!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.props.Document)} onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.props.Document)} style={{ - display: this.hidden ? 'inline' : undefined, borderRadius: this.borderRounding, pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, }}> - {!borderPath.path ? ( - animRenderDoc - ) : ( - <> - {animRenderDoc} -
- - - -
- - )} + <> + {DocumentViewInternal.AnimationEffect(renderDoc, this.rootDoc[AnimationSym], this.rootDoc)} + {borderPath?.jsx} +
); } @@ -1332,6 +1254,11 @@ export class DocumentViewInternal extends DocComponent { public static ROOT_DIV = 'documentView-effectsWrapper'; @observable public static LongPress = false; + @observable public docView: DocumentViewInternal | undefined | null; + @observable public textHtmlOverlay: Opt; + @observable private _isHovering = false; + + public htmlOverlayEffect = ''; public get displayName() { return 'DocumentView(' + this.props.Document?.title + ')'; } // this makes mobx trace() statements more descriptive @@ -1343,6 +1270,10 @@ export class DocumentView extends React.Component { this.ViewTimer && clearTimeout(this.ViewTimer); this.rootDoc._viewTransition = undefined; }; + public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource); + + public showContextMenu = (pageX: number, pageY: number) => this.docView?.onContextMenu(undefined, pageX, pageY); + public setAnimEffect = (presEffect: Doc, timeInMs: number, afterTrans?: () => void) => { this.AnimEffectTimer && clearTimeout(this.AnimEffectTimer); this.rootDoc[AnimationSym] = presEffect; @@ -1374,34 +1305,18 @@ export class DocumentView extends React.Component { ); } + // shows a stacking view collection (by default, but the user can change) of all documents linked to the source public static showBackLinks(linkSource: Doc) { const docId = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish'; - DocServer.GetRefField(docId).then(docx => { - const rootAlias = () => { - const rootAlias = Doc.MakeAlias(linkSource); - rootAlias.x = rootAlias.y = 0; - return rootAlias; - }; - const linkCollection = - (docx instanceof Doc && docx) || - Docs.Create.StackingDocument( - [ - /*rootAlias()*/ - ], - { title: linkSource.title + '-pivot', _width: 500, _height: 500 }, - docId - ); - linkCollection.linkSource = linkSource; - if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript('updateLinkCollection(self)'); - LightboxView.SetLightboxDoc(linkCollection); - }); + // prettier-ignore + DocServer.GetRefField(docId).then(docx => docx instanceof Doc && + LightboxView.SetLightboxDoc( + docx || // reuse existing pivot view of documents, or else create a new collection + Docs.Create.StackingDocument([], { title: linkSource.title + '-pivot', _width: 500, _height: 500, linkSource, updateContentsScript: ScriptField.MakeScript('updateLinkCollection(self)') }, docId) + ) + ); } - @observable public docView: DocumentViewInternal | undefined | null; - - showContextMenu(pageX: number, pageY: number) { - return this.docView?.onContextMenu(undefined, pageX, pageY); - } get Document() { return this.props.Document; } @@ -1409,13 +1324,10 @@ export class DocumentView extends React.Component { return this.props.renderDepth === 0; } get rootDoc() { - return this.docView?.rootDoc || this.Document; + return this.docView?.rootDoc ?? this.Document; } get dataDoc() { - return this.docView?.dataDoc || this.Document; - } - get finalLayoutKey() { - return this.docView?.finalLayoutKey || 'layout'; + return this.docView?.dataDoc ?? this.Document; } get ContentDiv() { return this.docView?.ContentDiv; @@ -1429,23 +1341,22 @@ export class DocumentView extends React.Component { get LayoutFieldKey() { return this.docView?.LayoutFieldKey || 'layout'; } - get fitWidth() { + @computed get fitWidth() { return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth; } - + @computed get anchorViewDoc() { + return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined; + } @computed get hideLinkButton() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.isSelected() ? ':selected' : '')); } - linkButtonInverseScaling = () => (this.props.NativeDimScaling?.() || 1) * this.screenToLocalTransform().Scale; - @computed get linkCountView() { const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; - return hideCount ? null : ; + return hideCount ? null : ; } @computed get hidden() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); } - @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; } @@ -1475,7 +1386,6 @@ export class DocumentView extends React.Component { } return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled } - @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } @@ -1500,12 +1410,12 @@ export class DocumentView extends React.Component { return this.props.dontCenter?.includes('y') ? 0 : this.Yshift; } - toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); - getBounds = () => { - if (!this.docView || !this.docView.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { + public toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); + public getBounds = () => { + if (!this.docView?.ContentDiv || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; } - const xf = this.docView?.props + const xf = this.docView.props .ScreenToLocalTransform() .scale(this.trueNativeWidth() ? this.nativeScaling : 1) .inverse(); @@ -1542,7 +1452,8 @@ export class DocumentView extends React.Component { Doc.setNativeView(this.props.Document); custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); }; - switchViews = action((custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => { + @action + switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => { this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc setTimeout( action(() => { @@ -1562,14 +1473,9 @@ export class DocumentView extends React.Component { }), this.docView ? Math.max(0, this.docView?.animateScaleTime - 10) : 0 ); - }); - - startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource); + }; - @observable textHtmlOverlay: Opt; - @computed get anchorViewDoc() { - return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined; - } + scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale; docViewPathFunc = () => this.docViewPath; isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction); select = (extendSelection: boolean) => SelectionManager.SelectView(this, !SelectionManager.Views().some(v => v.props.Document === this.props.ContainingCollectionDoc) && extendSelection); @@ -1586,11 +1492,12 @@ export class DocumentView extends React.Component { .translate(-this.centeringX, -this.centeringY) .scale(this.trueNativeWidth() ? 1 / this.nativeScaling : 1); componentDidMount() { - this._disposers.reactionScript = reaction( - () => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result, + this._disposers.updateContentsScript = reaction( + () => ScriptCast(this.rootDoc.updateContentsScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result, output => output ); this._disposers.height = reaction( + // increase max auto height if document has been resized to be greater than current max () => NumCast(this.layoutDoc._height), action(height => { const docMax = NumCast(this.layoutDoc.docMaxAutoHeight); @@ -1603,24 +1510,20 @@ export class DocumentView extends React.Component { Object.values(this._disposers).forEach(disposer => disposer?.()); !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this); } - _hoverTimeout: any = undefined; - isHovering = () => this._isHovering; - @observable _isHovering = false; - - htmlOverlayEffect = ''; @computed get htmlOverlay() { - const effectProps = { - delay: 0, - duration: 500, - }; - const highlight = ( -
- console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} /> -
- ); return !this.textHtmlOverlay ? null : (
-
{{DocumentViewInternal.AnimationEffect(highlight, { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc, this.rootDoc)} }
+
+ + {DocumentViewInternal.AnimationEffect( +
+ console.log('PARSE error', e)} renderInWrapper={false} jsx={StrCast(this.textHtmlOverlay)} /> +
, + { presEffect: this.htmlOverlayEffect ?? 'Zoom' } as any as Doc, + this.rootDoc + )}{' '} +
+
); } @@ -1631,19 +1534,7 @@ export class DocumentView extends React.Component { const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined; return this.hidden ? null : ( -
{ - clearTimeout(this._hoverTimeout); - this._isHovering = true; - })} - onPointerLeave={action(() => { - clearTimeout(this._hoverTimeout); - this._hoverTimeout = setTimeout( - action(() => (this._isHovering = false)), - 500 - ); - })}> +
(this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))}> {!this.props.Document || !this.props.PanelWidth() ? null : (
{ NativeDimScaling={this.NativeDimScaling} isSelected={this.isSelected} select={this.select} - isHovering={this.isHovering} ScreenToLocalTransform={this.screenToLocalTransform} focus={this.props.focus || emptyFunction} ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} @@ -1682,10 +1572,6 @@ export class DocumentView extends React.Component { } } -export function deiconifyViewFunc(documentView: DocumentView) { - documentView.iconify(); - //StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc); -} ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { documentView.iconify(); documentView.select(false); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e60a03190..48e54b722 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -368,6 +368,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent +
(this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
{fadepath === srcpath ? null : (
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index dc6bf6f9e..0b88f5fe3 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -335,7 +335,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); + Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY)); } }; @@ -359,11 +359,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + createSnapshotLink = (imagePath: string, downX?: number, downY?: number) => { const url = !imagePath.startsWith('/') ? Utils.CorsProxy(imagePath) : imagePath; const width = NumCast(this.layoutDoc._width) || 1; const height = NumCast(this.layoutDoc._height); - const imageSummary = Docs.Create.ImageDocument(url, { + const imageSnapshot = Docs.Create.ImageDocument(url, { _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc), x: NumCast(this.layoutDoc.x) + width, @@ -373,12 +373,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true)); + setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true)); }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { -- cgit v1.2.3-70-g09d2 From c5c2c309cd88bbeb2f1b668cb040cffaac9c8470 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 4 Apr 2023 20:12:16 -0400 Subject: fixed using freeformview in sidebar of pdfs. fixed issues with dragging items out of schema and with selecting the schema view by clicking on headers. Fixed a lot of errors caused by using OmitKeys which masks type checking. fixed some pointerevent problems with treeview and freeformview --- src/client/documents/Documents.ts | 22 ++-- src/client/views/DocumentDecorations.tsx | 40 +++--- src/client/views/EditableView.tsx | 4 +- src/client/views/InkStrokeProperties.ts | 1 - src/client/views/InkingStroke.tsx | 13 +- src/client/views/MainView.tsx | 1 + src/client/views/PropertiesView.tsx | 73 ++--------- src/client/views/SidebarAnnos.tsx | 15 +-- src/client/views/StyleProvider.tsx | 2 +- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionCarousel3DView.tsx | 7 +- .../views/collections/CollectionCarouselView.tsx | 8 +- .../collections/CollectionStackedTimeline.tsx | 54 +++----- .../views/collections/CollectionTreeView.tsx | 24 ++-- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 139 +++++++++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 + .../collectionGrid/CollectionGridView.tsx | 9 +- .../collectionSchema/CollectionSchemaView.tsx | 99 +++------------ .../collectionSchema/SchemaColumnHeader.tsx | 10 +- src/client/views/nodes/AudioBox.tsx | 6 +- src/client/views/nodes/ComparisonBox.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 26 +--- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 12 +- src/client/views/nodes/MapBox/MapBox.tsx | 6 +- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 141 ++++++++++++++++----- src/client/views/nodes/ScreenshotBox.tsx | 18 ++- src/client/views/nodes/VideoBox.tsx | 12 +- src/client/views/nodes/WebBox.tsx | 14 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 27 ++-- src/client/views/pdf/PDFViewer.tsx | 15 ++- src/fields/Doc.ts | 9 +- 34 files changed, 389 insertions(+), 437 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9b8b9c877..9cb480c4a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1420,41 +1420,39 @@ export namespace DocUtils { export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { let created: Doc | undefined; - let layout: ((fieldKey: string) => string) | undefined; const field = target[fieldKey]; - const resolved = options || {}; + const resolved = options ?? {}; if (field instanceof ImageField) { created = Docs.Create.ImageDocument(field.url.href, resolved); - layout = ImageBox.LayoutString; + created.layout = ImageBox.LayoutString(fieldKey); } else if (field instanceof Doc) { created = field; } else if (field instanceof VideoField) { created = Docs.Create.VideoDocument(field.url.href, resolved); - layout = VideoBox.LayoutString; + created.layout = VideoBox.LayoutString(fieldKey); } else if (field instanceof PdfField) { created = Docs.Create.PdfDocument(field.url.href, resolved); - layout = PDFBox.LayoutString; + created.layout = PDFBox.LayoutString(fieldKey); } else if (field instanceof AudioField) { created = Docs.Create.AudioDocument(field.url.href, resolved); - layout = AudioBox.LayoutString; + created.layout = AudioBox.LayoutString(fieldKey); } else if (field instanceof RecordingField) { created = Docs.Create.RecordingDocument(field.url.href, resolved); - layout = RecordingBox.LayoutString; + created.layout = RecordingBox.LayoutString(fieldKey); } else if (field instanceof InkField) { created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved); - layout = InkingStroke.LayoutString; + created.layout = InkingStroke.LayoutString(fieldKey); } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); - layout = CollectionView.LayoutString; + created.layout = CollectionView.LayoutString(fieldKey); } else if (field instanceof MapField) { created = Docs.Create.MapDocument(DocListCast(field), resolved); - layout = MapBox.LayoutString; + created.layout = MapBox.LayoutString(fieldKey); } else { created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved }); - layout = FormattedTextBox.LayoutString; + created.layout = FormattedTextBox.LayoutString(fieldKey); } if (created) { - created.layout = layout?.(fieldKey); created.title = fieldKey; proto && created.proto && (created.proto = Doc.GetProto(proto)); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 985e6f88f..9bc583ce5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -31,6 +31,7 @@ import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); +import { RichTextField } from '../../fields/RichTextField'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -127,19 +128,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } //@ts-ignore const titleField = +this._accumulatedTitle == this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle; - Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); - if (d.rootDoc.syncLayoutFieldWithTitle) { - const title = titleField.toString(); + if (titleField.toString().startsWith('')) { + const title = titleField.toString().replace(/\.?/, ''); const curKey = Doc.LayoutFieldKey(d.rootDoc); - if (curKey !== title && d.dataDoc[title] === undefined) { - d.rootDoc.layout = FormattedTextBox.LayoutString(title); - setTimeout(() => { - const val = d.dataDoc[curKey]; - d.dataDoc[curKey] = undefined; - d.dataDoc[title] = val; - }); + if (curKey !== title) { + if (title) { + if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') { + d.rootDoc.layoutKey = `layout_${title}`; + d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title); + d.rootDoc[`${title}-nativeWidth`] = d.rootDoc[`${title}-nativeHeight`] = 0; + } + } else { + d.rootDoc.layoutKey = undefined; + } } + } else { + Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); } }), 'title blur' @@ -453,17 +458,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P @action onPointerDown = (e: React.PointerEvent): void => { - const views = SelectionManager.Views().map(dv => dv.rootDoc); - this._inkDragDocs = views - .filter(doc => doc.type === DocumentType.INK) - .map(doc => { - if (InkStrokeProperties.Instance._lock) { - Doc.SetNativeHeight(doc, NumCast(doc._height)); - Doc.SetNativeWidth(doc, NumCast(doc._width)); - } - return { doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }; - }); - setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them this._resizeHdlId = e.currentTarget.className; @@ -487,10 +481,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (!first) return false; let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY }; var fixedAspect = Doc.NativeAspect(first.layoutDoc); - InkStrokeProperties.Instance._lock && - SelectionManager.Views() - .filter(dv => dv.rootDoc.type === DocumentType.INK) - .forEach(dv => (fixedAspect = Doc.NativeAspect(dv.rootDoc))); const resizeHdl = this._resizeHdlId.split(' ')[0]; if (fixedAspect && (resizeHdl === 'documentDecorations-bottomRightResizer' || resizeHdl === 'documentDecorations-topLeftResizer')) { diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index bb190e93b..164b6c57a 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -129,7 +129,7 @@ export class EditableView extends React.Component { this._editing = true; this.props.isEditingCallback?.(true); } - e.stopPropagation(); + // e.stopPropagation(); } }; @@ -215,7 +215,7 @@ export class EditableView extends React.Component { className={`editableView-container-editing${this.props.oneLine ? '-oneLine' : ''}`} ref={this._ref} style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: 'nowrap', height: this.props.height || 'auto', maxHeight: this.props.maxHeight }} - onPointerDown={e => e.stopPropagation()} + //onPointerDown={this.stopPropagation} onClick={this.onClick} placeholder={this.props.placeholder}> {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()} diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 1d8d52425..e6df0801c 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -19,7 +19,6 @@ export class InkStrokeProperties { return this._Instance || new InkStrokeProperties(); } - @observable _lock = false; @observable _controlButton = false; @observable _currentPoint = -1; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index e3642fdaf..2fd6cc4d6 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -25,10 +25,11 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, HeightSym, WidthSym } from '../../fields/Doc'; import { InkData, InkField } from '../../fields/InkField'; -import { BoolCast, Cast, DocCast, NumCast, RTFCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { DashColor, OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils'; +import { DashColor, returnFalse, setupMoveUpEvents } from '../../Utils'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; +import { Docs } from '../documents/Documents'; import { InteractionUtils } from '../util/InteractionUtils'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; @@ -41,13 +42,12 @@ import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles'; import './InkStroke.scss'; import { InkStrokeProperties } from './InkStrokeProperties'; import { InkTangentHandles } from './InkTangentHandles'; -import { DocComponentView, DocFocusOptions, DocumentView } from './nodes/DocumentView'; +import { DocComponentView } from './nodes/DocumentView'; import { FieldView, FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { PinProps, PresBox } from './nodes/trails'; import { StyleProp } from './StyleProvider'; import Color = require('color'); -import { Docs } from '../documents/Documents'; -import { PinProps, PresBox } from './nodes/trails'; @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -464,7 +464,8 @@ export class InkingStroke extends ViewBoxBaseComponent() { //top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2, }}> { @observable inOptions: boolean = false; @observable _controlButton: boolean = false; - @observable _lock: boolean = false; componentDidMount() { this.selectedDocListenerDisposer?.(); @@ -586,16 +585,6 @@ export class PropertiesView extends React.Component {
- {InkStrokeProperties.Instance._lock ? 'Unlock ratio' : 'Lock ratio'}
}> -
(InkStrokeProperties.Instance._lock = !InkStrokeProperties.Instance._lock))}> - -
- - {'Rotate 90Ëš'}
}> -
this.rotate(Math.PI / 2))}> - -
-
); } @@ -644,9 +633,6 @@ export class PropertiesView extends React.Component { @action upDownButtons = (dirs: string, field: string) => { switch (field) { - case 'rot': - this.rotate(dirs === 'up' ? 0.1 : -0.1); - break; case 'Xps': this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === 'up' ? 10 : -10)); break; @@ -662,7 +648,6 @@ export class PropertiesView extends React.Component { const oldX = NumCast(this.selectedDoc?.x); const oldY = NumCast(this.selectedDoc?.y); this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === 'up' ? 10 : -10)); - InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth) * NumCast(this.selectedDoc?._height)); const doc = this.selectedDoc; if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { const ink = Cast(doc.data, InkField)?.inkData; @@ -684,7 +669,6 @@ export class PropertiesView extends React.Component { const oX = NumCast(this.selectedDoc?.x); const oY = NumCast(this.selectedDoc?.y); this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === 'up' ? 10 : -10)); - InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight) * NumCast(this.selectedDoc?._width)); const docu = this.selectedDoc; if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { const ink = Cast(docu.data, InkField)?.inkData; @@ -704,11 +688,7 @@ export class PropertiesView extends React.Component { }; getField(key: string) { - //if (this.selectedDoc) { return Field.toString(this.selectedDoc?.[key] as Field); - // } else { - // return undefined as Opt; - // } } @computed get shapeXps() { @@ -717,9 +697,6 @@ export class PropertiesView extends React.Component { @computed get shapeYps() { return this.getField('y'); } - @computed get shapeRot() { - return this.getField('rotation'); - } @computed get shapeHgt() { return this.getField('_height'); } @@ -732,18 +709,11 @@ export class PropertiesView extends React.Component { set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); } - set shapeRot(value) { - this.selectedDoc && (this.selectedDoc.rotation = Number(value)); - } set shapeWid(value) { - const oldWidth = NumCast(this.selectedDoc?._width); this.selectedDoc && (this.selectedDoc._width = Number(value)); - InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); } set shapeHgt(value) { - const oldHeight = NumCast(this.selectedDoc?._height); this.selectedDoc && (this.selectedDoc._height = Number(value)); - InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); } @computed get hgtInput() { @@ -790,30 +760,6 @@ export class PropertiesView extends React.Component { 'Y:' ); } - @computed get rotInput() { - return this.inputBoxDuo( - 'rot', - this.shapeRot, - (val: string) => { - if (!isNaN(Number(val))) { - this.rotate(Number(val) - Number(this.shapeRot)); - this.shapeRot = val; - } - return true; - }, - '∠:', - 'rot', - this.shapeRot, - (val: string) => { - if (!isNaN(Number(val))) { - this.rotate(Number(val) - Number(this.shapeRot)); - this.shapeRot = val; - } - return true; - }, - '' - ); - } @observable private _fillBtn = false; @observable private _lineBtn = false; @@ -1080,10 +1026,9 @@ export class PropertiesView extends React.Component { @computed get transformEditor() { return (
- {this.controlPointsButton} + {this.isInk ? this.controlPointsButton : null} {this.hgtInput} {this.XpsInput} - {this.rotInput}
); } @@ -1194,17 +1139,15 @@ export class PropertiesView extends React.Component {
)} - {this.isInk ? ( -
-
(this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}> - Transform -
- -
+
+
(this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}> + Transform +
+
- {this.openTransform ?
{this.transformEditor}
: null}
- ) : null} + {this.openTransform ?
{this.transformEditor}
: null} +
); } diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 02dd15960..48c0150e6 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -5,7 +5,7 @@ import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { DocCast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils'; +import { emptyFunction, returnAll, returnFalse, returnOne, returnTrue, returnZero } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; @@ -26,6 +26,7 @@ interface ExtraProps { // usePanelWidth: boolean; showSidebar: boolean; nativeWidth: number; + usePanelWidth?: boolean; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: Doc | Doc[], suffix: string) => boolean; @@ -158,13 +159,10 @@ export class SidebarAnnos extends React.Component { .ScreenToLocalTransform() .translate(Doc.NativeWidth(this.props.dataDoc), 0) .scale(this.props.NativeDimScaling?.() || 1); - // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 : - // this.props.usePanelWidth ? this.props.PanelWidth() : - // (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); panelWidth = () => !this.props.showSidebar ? 0 - : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP + : this.props.usePanelWidth // [DocumentType.RTF, DocumentType.MAP].includes(this.props.layoutDoc.type as any) ? this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); @@ -224,20 +222,21 @@ export class SidebarAnnos extends React.Component {
, props: Opt = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; const docView = props?.DocumentView?.(); - const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, 'backgroundColor'); + const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, StyleProp.BackgroundColor); if (!backColor) return undefined; return lightOrDark(backColor); case StyleProp.Hidden: diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 681ff66e0..45db240a9 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -79,7 +79,7 @@ export class TemplateMenu extends React.Component { }; componentDidMount() { !this._addedKeys && (this._addedKeys = new ObservableSet()); - Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))) + [...Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))), ...Array.from(Object.keys(this.props.docViews[0].props.Document))] .filter(key => key.startsWith('layout_')) .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', '')))); } diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 57ff1b292..a266c9207 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { OmitKeys, returnFalse, Utils } from '../../../Utils'; +import { returnFalse, returnZero, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView } from '../nodes/DocumentView'; import { StyleProp } from '../StyleProvider'; @@ -43,7 +43,10 @@ export class CollectionCarousel3DView extends CollectionSubView() { const displayDoc = (childPair: { layout: Doc; data: Doc }) => { return (
this.currentTime; - @computed get renderDictation() { - const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); - return !dictation ? null : ( -
- -
- ); - } - // renders selection region on timeline @computed get selectionContainer() { const markerEnd = CollectionStackedTimeline.SelectingRegion === this ? this.currentTime : this._markerEnd; @@ -638,7 +606,6 @@ export class CollectionStackedTimeline extends CollectionSubView )} - {/* {this.renderDictation} */}
// renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, options: DocFocusOptions) => this.props.playLink(mark); + const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => { + this.props.playLink(mark); + return undefined; + }; return { anchor, view: ( (anchor.view = r))} Document={mark} DataDoc={undefined} + docViewPath={returnEmptyDoclist} pointerEvents={this.noEvents ? returnNone : undefined} styleProvider={this.props.styleProvider} renderDepth={this.props.renderDepth + 1} @@ -837,7 +810,16 @@ class StackedTimelineAnchor extends React.Component PanelHeight={height} fitWidth={returnTrue} ScreenToLocalTransform={screenXf} + addDocTab={returnFalse} + pinToPres={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} + isContentActive={returnFalse} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + searchFilterDocs={returnEmptyDoclist} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 553967b95..4a11e8f0b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -7,7 +7,7 @@ import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils'; +import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -384,12 +384,12 @@ export class CollectionTreeView extends CollectionSubView { + @computed get content() { const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; - return [ -
+ return ( +
{!this.buttonMenu && !this.noviceExplainer ? null : (
r && (this._headerHeight = Number(getComputedStyle(r).height.replace(/px/, ''))))}> {this.buttonMenu} @@ -428,9 +428,9 @@ export class CollectionTreeView extends CollectionSubView
-
, - ]; - }; +
+ ); + } render() { TraceMobx(); @@ -439,7 +439,11 @@ export class CollectionTreeView extends CollectionSubView {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? ( - {this.contentFunc} + {this.content} ) : ( - this.contentFunc() + this.content )}
); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 51624689e..bc25ad43a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -45,7 +45,7 @@ interface CollectionViewProps_ extends FieldViewProps { // property overrides for child documents childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) - childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) + childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childFitWidth?: (child: Doc) => boolean; childShowTitle?: () => string; childOpacity?: () => number; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aed3683d4..d39668a5d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { Bezier } from 'bezier-js'; import { Colors } from 'browndash-components'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { DateField } from '../../../../fields/DateField'; @@ -53,10 +53,14 @@ import { MarqueeView } from './MarqueeView'; import React = require('react'); export type collectionFreeformViewProps = { + noPointerWheel?: () => boolean; // turn off pointerwheel interactions (see PDFViewer) + NativeWidth?: () => number; + NativeHeight?: () => number; + originTopLeft?: boolean; annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: string; - scaleField?: string; + viewField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; getScrollHeight?: () => number | undefined; @@ -97,8 +101,14 @@ export class CollectionFreeFormView extends CollectionSubView (this.fitContentsToBox ? true : false); // panx, pany, zoomscale 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._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); - panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); + panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); + panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1)); contentTransform = () => - !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && 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)`; getTransform = () => this.cachedGetTransform.copy(); getLocalTransform = () => this.cachedGetLocalTransform.copy(); getContainerTransform = () => this.cachedGetContainerTransform.copy(); @@ -293,7 +301,7 @@ export class CollectionFreeFormView extends CollectionSubView { - options.docTransform = new Transform(-NumCast(this.rootDoc.panX) + NumCast(anchor.x), -NumCast(this.rootDoc.panY) + NumCast(anchor.y), 1); + 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 = undefined; return res; @@ -301,7 +309,7 @@ export class CollectionFreeFormView extends CollectionSubView { const xfToCollection = options?.docTransform ?? Transform.Identity(); - const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; + 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 { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale || 0.75 : undefined); @@ -772,7 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView= 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, NumCast(this.props.Document.scrollTop) * safeScale || -localTransform.TranslateY / safeScale); + this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.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.props.noPointerWheel?.() || this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom PresBox.Instance?.pauseAutoPres(); if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; e.stopPropagation(); @@ -995,7 +1003,7 @@ export class CollectionFreeFormView extends CollectionSubView= panX + panelDim[0] / 2) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds - else if (ranges.xrange.max <= panX - panelDim[0] / 2) panX = ranges.xrange.min - panelDim[0] / 2; - if (ranges.yrange.min >= panY + panelDim[1] / 2) panY = ranges.yrange.max + panelDim[1] / 2; - else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2; + const panelWidMax = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelWidMin = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1); + const panelHgtMax = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelHgtMin = (this.props.PanelHeight() / this.zoomScaling()) * (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 || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) { @@ -1078,8 +1089,8 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) { this.setPan( - NumCast(this.layoutDoc._panX) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale - NumCast(this.layoutDoc._panY) + ((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 ); @@ -1106,12 +1117,14 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).slice(); docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1; - if (zlast - docs.length > 100) { - for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; - zlast = docs.length + 1; + let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1; + if (docs.lastElement() !== doc) { + if (zlast - docs.length > 100) { + for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; + zlast = docs.length + 1; + } + doc.zIndex = zlast + 1; } - doc.zIndex = zlast + 1; } }; @@ -1134,8 +1147,8 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -1158,8 +1171,8 @@ export class CollectionFreeFormView extends CollectionSubView screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0; @@ -1171,8 +1184,8 @@ export class CollectionFreeFormView extends CollectionSubView { if (cbounds) { const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; - const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; + const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])]; const pbounds = { x: cbounds.x - p[0] + c[0], y: cbounds.y - p[1] + c[1], @@ -1511,8 +1524,8 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(doc._height))) + 20; const dim = Math.ceil(Math.sqrt(docs.length)); docs.forEach((doc, i) => { - doc.x = NumCast(this.Document._panX) + (i % dim) * width - (width * dim) / 2; - doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - (height * dim) / 2; + doc.x = NumCast(this.Document[this.panXFieldKey]) + (i % dim) * width - (width * dim) / 2; + doc.y = NumCast(this.Document[this.panYFieldKey]) + Math.floor(i / dim) * height - (height * dim) / 2; }); }; @@ -1675,7 +1688,7 @@ export class CollectionFreeFormView extends CollectionSubView { - this.props.Document._panX = this.props.Document._panY = 0; + this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: 'compress-arrows-alt', @@ -1795,11 +1808,11 @@ export class CollectionFreeFormView extends CollectionSubView !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1); }); - children = () => { + get children() { this.incrementalRender(); - const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : []; + const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : []; return [...children, ...this.views, ]; - }; + } @computed get placeholder() { return ( @@ -1843,6 +1856,7 @@ export class CollectionFreeFormView extends CollectionSubView this.nativeDimScaling; private groupDropDisposer?: DragManager.DragDropDisposer; protected createGroupEventsTarget = (ele: HTMLDivElement) => { @@ -1912,7 +1927,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.createDashEventsTarget(r); // prevent wheel events from passivly propagating up through containers - r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); + !this.props.isAnnotationOverlay && r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); }} onWheel={this.onPointerWheel} onClick={this.onClick} @@ -1985,7 +2000,8 @@ interface CollectionFreeFormViewPannableContentsProps { transform: () => string; zoomScaling: () => number; viewDefDivClick?: ScriptField; - children: () => JSX.Element[]; + children?: React.ReactNode | undefined; + //children: () => JSX.Element[]; transition?: string; presPaths: () => JSX.Element | null; presPinView?: boolean; @@ -2079,7 +2095,7 @@ class CollectionFreeFormViewPannableContents extends React.Component - {this.props.children()} + {this.props.children} {!this.props.brushView.width ? null : (
number; PanelHeight: () => number; isAnnotationOverlay?: boolean; + nativeDimScaling: () => number; zoomScaling: () => number; layoutDoc: Doc; cachedCenteringShiftX: number; @@ -2124,10 +2141,10 @@ class CollectionFreeFormBackgroundGrid extends React.Component { + // if (this.props.pointerEvents?.() === 'none') return; this._downX = this._lastX = e.clientX; this._downY = this._lastY = e.clientY; if (!(e.nativeEvent as any).marqueeHit) { @@ -345,6 +346,7 @@ export class MarqueeView extends React.Component { + if (this.props.pointerEvents?.() === 'none') return; if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { if (Doc.ActiveTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 9468c5f06..e8ae88ae5 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Doc, Opt } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -186,7 +186,10 @@ export class CollectionGridView extends CollectionSubView() { getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ( ); } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index d47c9762c..fd9bcf681 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,23 +1,20 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx'; +import { action, computed, observable, ObservableMap, untracked } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc'; +import { computedFn } from 'mobx-utils'; +import { Doc, Field, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; -import { RichTextField } from '../../../../fields/RichTextField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; -import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; -import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -215,9 +212,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addNewKey = (key: string, defaultVal: any) => { - this.childDocs.forEach(doc => (doc[key] = defaultVal)); - }; + addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal)); @undoBatch @action @@ -303,9 +298,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addRowRef = (doc: Doc, ref: HTMLDivElement) => { - this._rowEles.set(doc, ref); - }; + addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref); @action setColRef = (index: number, ref: HTMLDivElement) => { @@ -405,68 +398,12 @@ export class CollectionSchemaView extends CollectionSubView() { menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); - const layoutItems: ContextMenuProps[] = []; - const docItems: ContextMenuProps[] = []; - const dataDoc = this.props.DataDoc || this.props.Document; - - DocUtils.addDocumentCreatorMenuItems( - doc => { - FormattedTextBox.SelectOnLoad = StrCast(doc[Id]); - return this.addRow(doc); - }, - this.addRow, - x, - y, - true - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string') - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document)); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - return this.addRow(created); - } - }, - icon: 'compress-arrows-alt', - }) - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length) - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey }); - if (created) { - const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document; - if (container.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, container); - return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); - } - return this.addRow(created) || false; - } - }, - icon: 'compress-arrows-alt', - }) - ); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' }); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' }); + DocUtils.addDocumentCreatorMenuItems(doc => this.addRow(doc), this.addRow, x, y, true); + ContextMenu.Instance.setDefaultItem('::', (name: string): void => { Doc.GetProto(this.props.Document)[name] = ''; - const created = Docs.Create.TextDocument('', { title: name, _autoHeight: true }); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - this.addRow(created); - } + this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true })); }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @@ -543,9 +480,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeColumnMenu = () => { - this._columnMenuIndex = undefined; - }; + closeColumnMenu = () => (this._columnMenuIndex = undefined); @action openFilterMenu = (index: number) => { @@ -596,19 +531,14 @@ export class CollectionSchemaView extends CollectionSubView() { getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); removeFieldFilters = (field: string) => { - this.getFieldFilters(field).forEach(filter => { - Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'); - }); + this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove')); }; onFilterKeyDown = (e: React.KeyboardEvent) => { + //prettier-ignore switch (e.key) { - case 'Enter': - this.closeFilterMenu(true); - break; - case 'Escape': - this.closeFilterMenu(false); - break; + case 'Enter' : this.closeFilterMenu(true); break; + case 'Escape': this.closeFilterMenu(false);break; } }; @@ -903,6 +833,7 @@ interface CollectionSchemaViewDocsProps { class CollectionSchemaViewDocs extends React.Component { tableWidthFunc = () => this.props.schema.tableWidth; rowHeightFunc = () => CollectionSchemaView._rowHeight; + childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -CollectionSchemaView._rowHeight - index * this.rowHeightFunc())); render() { return (
@@ -932,7 +863,7 @@ class CollectionSchemaViewDocs extends React.Component @action onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false); }; render() { return ( -
{ - col && this.props.setColRef(this.props.columnIndex, col); - }}> +
col && this.props.setColRef(this.props.columnIndex, col)}>
this.props.resizeColumn(e, this.props.columnIndex)}>
{this.fieldKey}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 384975b45..a6acf882c 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, DateCast, NumCast } from '../../../fields/Types'; import { AudioField, nullAudio } from '../../../fields/URLField'; -import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, formatTime, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; @@ -19,7 +19,6 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import './AudioBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { SelectionManager } from '../../util/SelectionManager'; import { PinProps, PresBox } from './trails'; /** @@ -655,7 +654,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent (this._stackedTimeline = r))} - {...OmitKeys(this.props, ['CollectionFreeFormDocumentView']).omit} + {...this.props} + CollectionFreeFormDocumentView={undefined} fieldKey={this.annotationKey} dictationKey={this.fieldKey + '-dictation'} mediaPath={this.path} diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index ecffe6c4f..ace388c57 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -3,7 +3,7 @@ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, OmitKeys, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -122,7 +122,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { //whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true }); }} - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} + {...this.props} + NativeWidth={returnZero} + NativeHeight={returnZero} isContentActive={returnFalse} isDocumentActive={returnFalse} styleProvider={this.docStyleProvider} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 091a6b050..c7bf37a45 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -12,7 +12,7 @@ import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; -import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -512,7 +512,6 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); - !Doc.noviceMode && - appearanceItems.push({ - description: 'Add a Field', - event: () => { - const alias = Doc.MakeAlias(this.rootDoc); - alias.layout = FormattedTextBox.LayoutString('newfield'); - alias.title = 'newfield'; - alias._height = 35; - alias._width = 100; - alias.syncLayoutFieldWithTitle = true; - alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width); - alias.y = NumCast(this.rootDoc.y); - this.props.addDocument?.(alias); - }, - icon: 'eye', - }); LinkManager.Links(this.Document).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? 'Show' : 'Hide'} Link Button`, event: action(() => (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' }); !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); @@ -863,7 +846,7 @@ export class DocumentViewInternal extends DocComponent (this.disableClickScriptFunc ? undefined : this.onClickHandler); setHeight = (height: number) => (this.layoutDoc._height = height); setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); - isContentActive = (outsideReaction?: boolean) => { + isContentActive = (outsideReaction?: boolean): boolean | undefined => { return this.props.isContentActive() === false ? false : Doc.ActiveTool !== InkTool.None || @@ -1064,7 +1047,7 @@ export class DocumentViewInternal extends DocComponent, props: Opt, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); + captionStyleProvider = (doc: Opt, props: Opt, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); @computed get innards() { TraceMobx(); const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1; @@ -1079,7 +1062,7 @@ export class DocumentViewInternal extends DocComponent
); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index e53422ab7..8d3534a5c 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -20,7 +20,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { select: (isCtrlPressed: boolean) => void; isContentActive: (outsideReaction?: boolean) => boolean | undefined; - isDocumentActive?: () => boolean; + isDocumentActive?: () => boolean | undefined; isSelected: (outsideReaction?: boolean) => boolean; setHeight?: (height: number) => void; NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 48e54b722..b5193cd20 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -11,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; +import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -409,8 +409,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent [this.content]; - private _mainCont: React.RefObject = React.createRef(); private _annotationLayer: React.RefObject = React.createRef(); @observable _marqueeing: number[] | undefined; @@ -473,7 +471,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent - {this.contentFunc} + {this.content} {this.annotationLayer} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index ea8f47b39..36be7d257 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -8,7 +8,7 @@ import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -635,7 +635,8 @@ export class MapBox extends ViewBoxAnnotatableComponent ( (this._stack = r)} - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} + {...this.props} + setContentView={emptyFunction} Document={this.props.place} DataDoc={undefined} fieldKey="data" diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5f207cc0d..a254edd87 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -5,16 +5,20 @@ import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; -import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { InkTool } from '../../../fields/InkField'; +import { ComputedField } from '../../../fields/ScriptField'; +import { Cast, FieldValue, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, PdfField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; +import { emptyFunction, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; -import { DocumentType } from '../../documents/DocumentTypes'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; +import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; +import { CollectionStackingView } from '../collections/CollectionStackingView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; @@ -41,7 +45,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent; private _pdfViewer: PDFViewer | undefined; private _searchRef = React.createRef(); - private _selectReactionDisposer: IReactionDisposer | undefined; + private _disposers: { [name: string]: IReactionDisposer } = {}; private _sidebarRef = React.createRef(); @observable private _searching: boolean = false; @@ -184,11 +188,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent disposer?.()); } componentDidMount() { this.props.setContentView?.(this); - this._selectReactionDisposer = reaction( + this._disposers.select = reaction( () => this.props.isSelected(), () => { document.removeEventListener('keydown', this.onKeyDown); @@ -196,6 +200,16 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.rootDoc.scrollTop, + () => { + if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '-panY'])) instanceof ComputedField)) { + this.props.Document[this.SidebarKey + '-panY'] = ComputedField.MakeFunction('this.scrollTop'); + } + this.props.Document[this.SidebarKey + '-viewScale'] = 1; + this.props.Document[this.SidebarKey + '-panX'] = 0; + } + ); } brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._pdfViewer?.brushView(view); @@ -303,7 +317,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); }; @@ -428,6 +442,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent { const funcs: ContextMenuProps[] = []; + funcs.push({ + description: 'Toggle Sidebar Type', + event: () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform), + icon: 'expand-arrows-alt', + }); funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); @@ -467,12 +486,87 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.sidebarNativeWidth; + sidebarNativeHeightFunc = () => this.sidebarNativeHeight; + sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey); + sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey); + sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate((this.sidebarWidth() - this.props.PanelWidth()) / this.pdfScale, 0); + @computed get sidebarCollection() { + const renderComponent = (tag: string) => { + const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : CollectionStackingView; + return ComponentTag === CollectionStackingView ? ( + + ) : ( +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> + +
+ ); + }; + return ( +
+ {renderComponent(StrCast(this.layoutDoc.sidebarViewType))} +
+ ); + } isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected() || (this.props.renderDepth === 0 && LightboxView.IsLightboxDocView(this.props.docViewPath())); @computed get renderPdfView() { TraceMobx(); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const scale = previewScale * (this.props.NativeDimScaling?.() || 1); - return ( + return !this._pdf ? null : (
-
- -
+
{this.sidebarCollection}
{this.settingsPanel()}
); @@ -535,21 +614,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent>(); render() { TraceMobx(); - if (this._pdf) { - if (!this.props.thumbShown?.()) { - return this.renderPdfView; - } - return null; - } + if (this.props.thumbShown?.()) return null; + const pdfView = this.renderPdfView; const href = this.pdfUrl?.url.href; - if (href) { + if (!pdfView && href) { if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href)))); else { if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise); PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, (this._pdf = pdf)))); } } - return this.renderTitleBox; + return pdfView ?? this.renderTitleBox; } } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 61e4894f0..db11a7776 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -11,7 +11,7 @@ import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { AudioField, VideoField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnFalse, returnOne } from '../../../Utils'; +import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; @@ -277,7 +277,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent [this.threed, this.content]; videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth(); formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight()); render() { @@ -287,7 +286,10 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent
- {this.contentFunc} + <> + {this.threed} + {this.content} +
{!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : ( )} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 0b88f5fe3..0570c7fcb 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -9,7 +9,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; +import { emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; @@ -894,8 +894,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent this._playing; - contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content]; - scaling = () => this.props.NativeDimScaling?.() || 1; panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100; @@ -1068,7 +1066,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent - {this.contentFunc} + {this.youtubeVideoId ? this.youtubeContent : this.content}
{this.annotationLayer} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index d57518a8d..66d0fd385 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; +import { emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -305,7 +305,9 @@ export class WebBox extends ViewBoxAnnotatableComponent string[]) => ( + const renderAnnotations = (docFilters: () => string[]) => ( this.props.Document._fitContentsToBox; + fitContentsToBox = () => BoolCast(this.props.Document._fitContentsToBox); sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); - // console.log("printting allSideBarDocs"); - // console.log(this.allSidebarDocs); return this.addDocument(doc, sidebarKey); }; sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey); @@ -1785,8 +1783,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent @@ -1800,33 +1798,36 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> { return this.props.styleProvider?.(doc, props, property); }; - renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => ( + renderAnnotations = (docFilters: () => string[], mixBlendMode?: any, display?: string) => (
{ pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> { PanelHeight={this.panelHeight} PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} + isAnyChildContentActive={returnFalse} + isAnnotationOverlayScrollable={true} dropAction={'alias'} docFilters={docFilters} select={emptyFunction} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 553c4525c..cc024d83a 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1213,7 +1213,7 @@ export namespace Doc { return doc[StrCast(doc.layoutKey, 'layout')]; } export function LayoutFieldKey(doc: Doc): string { - return StrCast(Doc.Layout(doc).layout).split("'")[1]; + return StrCast(Doc.Layout(doc)[StrCast(doc.layoutKey, 'layout')]).split("'")[1]; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layoutKey } export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) { return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1); @@ -1222,9 +1222,10 @@ export namespace Doc { return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeWidth'], useWidth ? doc[WidthSym]() : 0)); } export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) { - const dheight = doc ? NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeHeight'], useHeight ? doc[HeightSym]() : 0) : 0; - const nheight = doc ? (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym]() : 0; - return !doc ? 0 : NumCast(doc._nativeHeight, nheight || dheight); + if (!doc) return 0; + const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym](); + const dheight = NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeHeight'], useHeight ? doc[HeightSym]() : 0); + return NumCast(doc._nativeHeight, nheight || dheight); } export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeWidth'] = width; -- cgit v1.2.3-70-g09d2 From e4b76f6c1dee63c49408c48d7b2bc98e62dc813a Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Apr 2023 12:19:01 -0400 Subject: adjusted general context menu items to always appear last. cleaned up makeLink api, --- src/client/documents/Documents.ts | 32 +++++----- src/client/util/DragManager.ts | 1 - src/client/util/LinkFollower.ts | 2 - src/client/views/ContextMenu.tsx | 12 ++-- src/client/views/DocumentButtonBar.tsx | 4 +- src/client/views/MainView.tsx | 1 + src/client/views/SidebarAnnos.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 28 ++++----- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 26 ++------ .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 70 +++++++++++++++------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 31 +++++----- src/client/views/nodes/VideoBox.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/search/SearchBox.tsx | 2 +- 21 files changed, 117 insertions(+), 118 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9cb480c4a..5623ca218 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -470,7 +470,7 @@ export namespace Docs { DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, forceReflow: true, nativeDimModifiable: true }, + options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true }, }, ], [ @@ -934,13 +934,13 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } - export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) { + export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) { const linkDoc = InstanceFromProto( Prototypes.get(DocumentType.LINK), undefined, { - anchor1: source.doc, - anchor2: target.doc, + anchor1: source, + anchor2: target, ...options, }, id @@ -1294,16 +1294,14 @@ export namespace DocUtils { broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); - const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor(true) || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline'); - link && (link.followLinkLocation = OpenWhere.addRight); - return link; + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' }); }); } - export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { - if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; - const sv = DocumentManager.Instance.getDocumentView(source.doc); - if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return; + export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { + if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; + const sv = DocumentManager.Instance.getDocumentView(source); + if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return; if (target.doc === Doc.UserDoc()) return undefined; const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { @@ -1343,16 +1341,16 @@ export namespace DocUtils { target, { title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, - 'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined, - 'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined, + 'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined, + 'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined, 'acl-Public': SharingPermissions.Augment, '_acl-Public': SharingPermissions.Augment, - linkDisplay: true, + linkDisplay: linkSettings.linkDisplay, _linkAutoMove: true, - linkRelationship, + linkRelationship: linkSettings.linkRelationship, _showCaption: 'description', _showTitle: 'linkRelationship', - description, + description: linkSettings.description, }, id ), @@ -1700,7 +1698,7 @@ export namespace DocUtils { _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); - const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', ''); + const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, ''); doc._timecodeToShow = undefined; return pushpin; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index cc116fb46..7d2aa813f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -168,7 +168,6 @@ export namespace DragManager { this.dropDocCreator = dropDocCreator; this.offset = [0, 0]; } - linkSourceDoc?: Doc; dropDocCreator: (annotationOn: Doc | undefined) => Doc; dropDocument?: Doc; offset: number[]; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 785018990..df61ecece 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -9,8 +9,6 @@ import { DocumentManager } from './DocumentManager'; import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; - -type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; /* * link doc: * - anchor1: doc diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 6a530e3ae..e4c3e864b 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -102,13 +102,11 @@ export class ContextMenu extends React.Component { } } @action - moveAfter(item: ContextMenuProps, after: ContextMenuProps) { - if (after && this.findByDescription(after.description)) { - const curInd = this._items.findIndex(i => i.description === item.description); - this._items.splice(curInd, 1); - const afterInd = this._items.findIndex(i => i.description === after.description); - this._items.splice(afterInd + 1, 0, item); - } + moveAfter(item: ContextMenuProps, after?: ContextMenuProps) { + const curInd = this._items.findIndex(i => i.description === item.description); + this._items.splice(curInd, 1); + const afterInd = after && this.findByDescription(after.description) ? this._items.findIndex(i => i.description === after.description) : this._items.length; + this._items.splice(afterInd, 0, item); } @action setDefaultItem(prefix: string, item: (name: string) => void) { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 9389fed01..3e1a0f265 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -416,7 +416,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const targetDoc = this.view0?.props.Document; return !targetDoc ? null : ( {`Open Context Menu`}
}> -
this.openContextMenu(e)}> +
@@ -569,7 +569,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc; const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true); if (trail !== anchor.presTrail) { - DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail'); + DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' }); anchor.presTrail = trail; } Doc.ActivePresentation = trail; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index befb0200d..b0888df68 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -270,6 +270,7 @@ export class MainView extends React.Component { fa.faLaptopCode, fa.faMale, fa.faCopy, + fa.faHome, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 48c0150e6..2d2b0f83e 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -74,7 +74,7 @@ export class SidebarAnnos extends React.Component { }); FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; - const link = DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on'); + const link = DocUtils.MakeLink(anchor, target, { linkRelationship: 'inline comment:comment on' }); link && (link.linkDisplay = false); const taggedContent = this.docFilters() diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 121260680..cb5be990d 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -441,7 +441,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed e.stopPropagation(); } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); return false; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 64ec419ec..1e02fc9d4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,14 +3,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -460,7 +460,7 @@ export class CollectionStackingView extends CollectionSubView { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped()) { - const subItems: ContextMenuProps[] = []; - subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); - subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); - subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' }); - ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' }); + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); + optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' }); + !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); } }; @@ -735,14 +737,6 @@ export class CollectionStackingView extends CollectionSubView
)} - {/* {this.chromeHidden || !this.props.isSelected() ? (null) : - } */}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5100d8d67..132ed6fb6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -335,7 +335,7 @@ export function CollectionSubView(moreProps?: X) { const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any; if (focusNode) { const anchor = srcWeb?.ComponentView?.getAnchor?.(true); - anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor }); + anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); } } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 99b7549c0..75e76019e 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -359,7 +359,7 @@ export class TreeView extends React.Component { if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor(); const destDoc = this.doc; - DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', ''); + DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' }); e.stopPropagation(); } const docDragData = de.complete.docDragData; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d39668a5d..fb0bd2a19 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -397,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' }); appearanceItems.push({ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`, @@ -1700,7 +1704,7 @@ export class CollectionFreeFormView extends CollectionSubView TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' }); - //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); + !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' }); 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(false), icon: 'font' }); @@ -1730,27 +1734,9 @@ export class CollectionFreeFormView extends CollectionSubView this.importDocument(e.clientX, e.clientY) }); !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' }); }; - importDocument = (x: number, y: number) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.zip'; - input.onchange = _e => { - input.files && - Doc.importDocument(input.files[0]).then(doc => { - if (doc instanceof Doc) { - const [xx, yy] = this.getTransform().transformPoint(x, y); - (doc.x = xx), (doc.y = yy); - this.props.addDocument?.(doc); - } - }); - }; - input.click(); - }; - @undoBatch @action transcribeStrokes = (math: boolean) => { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 043fe0bcf..0b4f76fa1 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -527,7 +527,7 @@ export class MarqueeView extends React.Component { + drop = (e: Event, de: DragManager.DropEvent) => { if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return; if (this.props.Document === Doc.ActiveDashboard) { alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.'); return; } const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData; - if (linkdrag) linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor(); - if (linkdrag?.linkSourceDoc) { - e.stopPropagation(); - if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) { - de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined); - } - if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) { - const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document; - de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]); + if (linkdrag) { + linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor(); + if (linkdrag.linkSourceDoc) { + e.stopPropagation(); + if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) { + de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined); + } + if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) { + const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc; + de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, undefined, [de.x, de.y - 50]); + } } } }; @@ -643,9 +645,9 @@ export class DocumentViewInternal extends DocComponent d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from'); if (!portalLink) { DocUtils.MakeLink( - { doc: this.props.Document }, - { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) }, - 'portal to:portal from' + this.props.Document, + Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }), + { linkRelationship: 'portal to:portal from' } ); } this.Document.followLinkLocation = OpenWhere.lightbox; @@ -653,6 +655,24 @@ export class DocumentViewInternal extends DocComponent { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.zip'; + input.onchange = _e => { + if (input.files) { + const batch = UndoManager.StartBatch('importing'); + Doc.importDocument(input.files[0]).then(doc => { + if (doc instanceof Doc) { + this.props.addDocTab(doc, OpenWhere.addRight); + batch.end(); + } + }); + } + }; + input.click(); + }; + @action onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => { if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) { @@ -704,9 +724,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); - LinkManager.Links(this.Document).length && - appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? 'Show' : 'Hide'} Link Button`, event: action(() => (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' }); - !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); + !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { const existingOnClick = cm.findByDescription('OnClick...'); @@ -774,16 +792,21 @@ export class DocumentViewInternal extends DocComponent Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' }); } - moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); - - (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); } - if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) { + !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' }); + } + const constantItems: ContextMenuProps[] = []; + + if (!Doc.IsSystem(this.rootDoc)) { + constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); + constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() }); + (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); + if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions) - moreItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); + constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); } - !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' }); + cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' }); } const help = cm.findByDescription('Help...'); const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; @@ -834,7 +857,8 @@ export class DocumentViewInternal extends DocComponent !isClick && batch.end(), () => { - this.toggleSidebar(); + onButton && this.toggleSidebar(); batch.end(); } ); @@ -440,17 +440,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform); specificContextMenu = (e: React.MouseEvent): void => { - const funcs: ContextMenuProps[] = []; - funcs.push({ - description: 'Toggle Sidebar Type', - event: () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform), - icon: 'expand-arrows-alt', - }); - funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); - funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); - //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); - ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' }); + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' }); + !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); + //optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); + !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' }); + const help = cm.findByDescription('Help...'); + const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; + helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); + !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' }); }; @computed get renderTitleBox() { @@ -523,8 +526,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> @@ -568,7 +569,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true)); }; @@ -1031,7 +1031,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || - DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); + DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!); newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); @@ -1273,7 +1273,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { makeLink = action((linkTo: Doc) => { const linkFrom = this.props.linkCreateAnchor?.(); if (linkFrom) { - const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }); + const link = DocUtils.MakeLink(linkFrom, linkTo, {}); link && this.props.linkCreated?.(link); } }); -- cgit v1.2.3-70-g09d2 From 8a1267faf796b2e2a30a6ba9f86879854e9ee983 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 11 Apr 2023 13:51:44 -0400 Subject: removed arrangeItems context button for collections to make it a funciton on drop instead of a reaction. Converted isLinkButton to be an onClick script. got rid of unused PARAMS field on templates. fixed PresElementBox rendering of embedded docs. --- src/client/documents/Documents.ts | 7 +-- src/client/util/CurrentUserUtils.ts | 4 +- src/client/util/DropConverter.ts | 10 +--- src/client/util/LinkFollower.ts | 6 ++ src/client/views/DocumentButtonBar.tsx | 9 +-- src/client/views/MarqueeAnnotator.tsx | 8 ++- src/client/views/PropertiesButtons.tsx | 7 ++- src/client/views/StyleProvider.tsx | 5 +- src/client/views/collections/CollectionMenu.tsx | 19 +----- .../views/collections/CollectionNoteTakingView.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 6 +- .../views/collections/CollectionStackingView.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 56 ++++++++++-------- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 67 +++++----------------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 7 ++- .../views/nodes/formattedText/DashFieldView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- src/fields/Doc.ts | 10 ++-- src/fields/documentSchemas.ts | 1 - 24 files changed, 104 insertions(+), 147 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 979b294e0..7600fcac9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -263,7 +263,6 @@ export class DocumentOptions { caption?: RichTextField; opacity?: number; defaultBackgroundColor?: string; - _isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed isFolder?: boolean; @@ -508,13 +507,13 @@ export namespace Docs { layout: { view: LinkBox, dataField: defaultDataKey }, options: { childDontRegisterViews: true, - _isLinkButton: true, + onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }), hideLinkAnchors: true, _height: 150, description: '', showCaption: 'description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area - _removeDropProperties: new List(['isLinkButton']), + _removeDropProperties: new List(['onClick']), }, }, ], @@ -1695,7 +1694,7 @@ export namespace DocUtils { _width: 15, _height: 15, _xPadding: 0, - _isLinkButton: true, + onClick: ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }), _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 623aaf357..3d0db04b4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -271,7 +271,7 @@ export class CurrentUserUtils { {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true, }}, {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, - {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, _isLinkButton: true }}, + {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: ScriptField.MakeScript("followLink(this,altKey)", {altKey:"boolean"}) }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}}, @@ -622,7 +622,7 @@ export class CurrentUserUtils { { title: "Snap\xA0Lines",icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "View\xA0All", icon: "object-group",toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Arrange", icon: "window", toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Arrange", icon: "window", toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Reset", icon: "check", toolTip: "Reset View", btnType: ButtonType.ClickButton, expertMode: false, backgroundColor:"transparent", scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform ] } diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 7c209d1e0..2e1d6ba23 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -33,15 +33,7 @@ function makeTemplate(doc: Doc, first: boolean = true, rename: Opt = und let any = false; docs.forEach(d => { if (!StrCast(d.title).startsWith('-')) { - const params = StrCast(d.title) - .match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1] - .replace('()', ''); - if (params) { - any = makeTemplate(d, false) || any; - d.PARAMS = params; - } else { - any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any; - } + any = Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)) || any; } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) { any = makeTemplate(d, false) || any; } diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index df61ecece..f5aae6632 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -7,6 +7,7 @@ import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/no import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; import { LinkManager } from './LinkManager'; +import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; /* @@ -118,3 +119,8 @@ export class LinkFollower { }); } } + +ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) { + SelectionManager.DeselectAll(); + LinkFollower.FollowLink(undefined, doc, altKey); +}); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index e83ea00cf..7e1b32398 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -5,7 +5,7 @@ import { action, computed, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; -import { Cast, DocCast, NumCast } from '../../fields/Types'; +import { Cast, DocCast, NumCast, ScriptCast } from '../../fields/Types'; import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; @@ -238,11 +238,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV ); }; + const followLink = ScriptCast(targetDoc?.onClick)?.script.originalScript.includes('followLink('); return !targetDoc ? null : ( Set onClick to follow primary link
}>
this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
{followBtn( @@ -384,7 +385,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV .views() .filter(v => v) .map(dv => dv!.rootDoc); - TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: {dataview: e.altKey}, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); e.stopPropagation(); }}>
@@ -593,7 +594,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV (link.linkDisplay = !this.props.views().lastElement()?.rootDoc.isLinkButton)} + linkCreated={link => (link.linkDisplay = !ScriptCast(this.props.views().lastElement()?.rootDoc.onClick)?.script.originalScript.includes('followLink('))} linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this.props.views().lastElement()?.rootDoc} /> diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 30867a774..6b4f8ad85 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -14,6 +14,7 @@ import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { AnchorMenu } from './pdf/AnchorMenu'; import React = require('react'); +import { ScriptField } from '../../fields/ScriptField'; const _global = (window /* browser */ || global) /* node */ as any; export interface MarqueeAnnotatorProps { @@ -146,7 +147,12 @@ export class MarqueeAnnotator extends React.Component { const scale = this.props.scaling?.() || 1; const anno = savedAnnos[0]; const containerOffset = this.props.containerOffset?.() || [0, 0]; - const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: 'Annotation on ' + this.props.rootDoc.title }); + const marqueeAnno = Docs.Create.FreeformDocument([], { + onClick: isLinkButton ? ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }) : undefined, + backgroundColor: color, + annotationOn: this.props.rootDoc, + title: 'Annotation on ' + this.props.rootDoc.title, + }); marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1); marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop); marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 11e9dd9c9..0723966e1 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -289,6 +289,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { .filter(dv => dv.docView) .map(dv => dv.docView!) .forEach(docView => { + const linkButton = ScriptCast(docView.props.Document.onClick)?.script.originalScript.includes('followLink('); docView.noOnClick(); switch (onClick) { case 'enterPortal': @@ -299,11 +300,11 @@ export class PropertiesButtons extends React.Component<{}, {}> { break; case 'linkInPlace': docView.toggleFollowLink(false, false); - docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.lightbox : undefined; + docView.props.Document.followLinkLocation = linkButton ? OpenWhere.lightbox : undefined; break; case 'linkOnRight': docView.toggleFollowLink(false, false); - docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.addRight : undefined; + docView.props.Document.followLinkLocation = linkButton ? OpenWhere.addRight : undefined; break; } }); @@ -326,7 +327,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { ]; const list = buttonList.map(value => { const click = () => this.handleOptionChange(value[0]); - const linkButton = BoolCast(this.selectedDoc._isLinkButton); + const linkButton = BoolCast(ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('followLink(')); const followLoc = this.selectedDoc._followLinkLocation; const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index f7b9420a7..8d67f0528 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -4,7 +4,7 @@ import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; -import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; @@ -279,7 +279,8 @@ export function DefaultStyleProvider(doc: Opt, props: Opt {}), initialize: emptyFunction, }; - _openLinkInCommand = { - params: ['target', 'container'], - title: 'link follow target', - script: `{ if (self.container?.length) { - getProto(self.target).linkContainer = self.container[0]; - getProto(self.target).isLinkButton = true; - getProto(self.target).onClick = makeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])"); - }}`, - immediate: undoBatch((container: Doc[]) => { - if (container.length) { - Doc.GetProto(this.target).linkContainer = container[0]; - Doc.GetProto(this.target).isLinkButton = true; - Doc.GetProto(this.target).onClick = ScriptField.MakeScript('getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])'); - } - }), - initialize: emptyFunction, - }; _viewCommand = { params: ['target'], title: 'bookmark view', @@ -328,7 +311,7 @@ export class CollectionViewBaseChrome extends React.Component (this.docsDraggedRowCol.length ? 'none' : undefined); // getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView) getDisplayDoc(doc: Doc, width: () => number) { - const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; + const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); let dref: Opt; const noteTakingDocTransform = () => this.getDocTransform(doc, dref); @@ -293,7 +293,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { getDocHeight(d?: Doc) { if (!d || d.hidden) return 0; const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); - const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc; + const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc; const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 642d29d2a..5891cfe78 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -7,7 +7,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { Cast, NumCast } from '../../../fields/Types'; +import { Cast, NumCast, ScriptCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils'; import { Docs } from '../../documents/Documents'; @@ -406,7 +406,7 @@ export class CollectionStackedTimeline extends CollectionSubView { - if (anchorDoc.isLinkButton) { + if (ScriptCast(anchorDoc.onClick)?.script.originalScript.includes('followLink(')) { LinkFollower.FollowLink(undefined, anchorDoc, false); } const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 1e02fc9d4..a85ee0e02 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -305,7 +305,7 @@ export class CollectionStackingView extends CollectionSubView number, count: number) { - const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; + const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); let dref: Opt; @@ -323,7 +323,7 @@ export class CollectionStackingView extends CollectionSubView (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1ecc69174..7ae7be3c8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -349,6 +349,7 @@ export class CollectionFreeFormView extends CollectionSubView (doc.zIndex = doc.isInkMask ? 5000 : index + 1)); const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000)); const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; + for (let i = 0; i < docDragData.droppedDocuments.length; i++) { const d = docDragData.droppedDocuments[i]; const layoutDoc = Doc.Layout(d); @@ -370,6 +371,30 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(a.layout.y) - NumCast(b.layout.y)); + sorted.splice( + sorted.findIndex(pair => pair.layout === refDoc), + 1 + ); + if (sorted.length && refDoc && NumCast(sorted[0].layout.y) < NumCast(refDoc.y)) { + const topIndexed = NumCast(refDoc.y) < NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height); + const deltay = sorted.length > 1 ? NumCast(refDoc.y) - (NumCast(sorted[0].layout.y) + (topIndexed ? 0 : NumCast(sorted[0].layout._height))) : 0; + const deltax = sorted.length > 1 ? NumCast(refDoc.x) - NumCast(sorted[0].layout.x) : 0; + + let lastx = NumCast(refDoc.x); + let lasty = NumCast(refDoc.y) + (topIndexed ? 0 : NumCast(refDoc._height)); + setTimeout( + action(() => + sorted.slice(1).forEach((pair, i) => { + lastx = pair.layout.x = lastx + deltax; + lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height)); + }) + ) + ); + } + } + (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments); return true; } @@ -1409,24 +1434,6 @@ export class CollectionFreeFormView extends CollectionSubView) { - if (this.layoutDoc._autoArrange) { - const sorted = this.childLayoutPairs.slice().sort((a, b) => (NumCast(a.layout.y) < NumCast(b.layout.y) ? -1 : 1)); - if (sorted.length > 1) { - const deltay = sorted.length > 1 ? NumCast(sorted[1].layout.y) - (NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height)) : 0; - const deltax = sorted.length > 1 ? NumCast(sorted[1].layout.x) - NumCast(sorted[0].layout.x) : 0; - - let lastx = NumCast(sorted[0].layout.x); - let lasty = NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height); - setTimeout( - action(() => - sorted.slice(1).forEach((pair, i) => { - lastx = pair.layout.x = lastx + deltax; - lasty = (pair.layout.y = lasty + deltay) + NumCast(pair.layout._height); - }) - ) - ); - } - } this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document }))); return [] as ViewDefResult[]; } @@ -1712,11 +1719,12 @@ export class CollectionFreeFormView extends CollectionSubView (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange), - 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) { !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); return; @@ -1729,7 +1737,7 @@ export class CollectionFreeFormView extends CollectionSubView TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' }); !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' }); - 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(false), icon: 'font' }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 9e56de8c2..2d12005fb 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -139,7 +139,6 @@ export class DocumentContentsView extends React.Component< return proto instanceof Promise ? undefined : proto; } get layoutDoc() { - const params = StrCast(this.props.Document.PARAMS); // bcz: replaced this with below : is it correct? change was made to accommodate passing fieldKey's from a layout script // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined); const template: Doc = @@ -147,7 +146,7 @@ export class DocumentContentsView extends React.Component< (this.props.LayoutTemplateString && this.props.Document) || (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined); - return Doc.expandTemplateLayout(template, this.props.Document, params ? '(' + params + ')' : this.props.layoutKey); + return Doc.expandTemplateLayout(template, this.props.Document, this.props.layoutKey); } CreateBindings(onClick: Opt, onInput: Opt): JsxBindings { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e24bf35f5..a39c3cf8f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -469,11 +469,6 @@ export class DocumentViewInternal extends DocComponent (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click')); - } else if (!this.disableClickScriptFunc && this.allLinks.length && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { - clickFunc = () => { - SelectionManager.DeselectAll(); - LinkFollower.FollowLink(undefined, this.Document, e.altKey); - }; } else { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { @@ -563,48 +558,24 @@ export class DocumentViewInternal extends DocComponent { - this.Document.ignoreClick = false; - if (setTargetToggle) { - this.Document.followLinkToggle = !this.Document.followLinkToggle; - this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton; - } else { - this.Document._isLinkButton = !this.Document._isLinkButton; - } - if (this.Document._isLinkButton && !this.onClickHandler) { - zoom !== undefined && (this.Document.followLinkZoom = zoom); - } else if (this.Document._isLinkButton && this.onClickHandler) { - this.Document._isLinkButton = false; - this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined; - } - }; - @undoBatch - @action - toggleTargetOnClick = (): void => { - this.Document.ignoreClick = false; - this.Document._isLinkButton = true; - this.Document.followLinkToggle = true; + const hadOnClick = this.rootDoc.onClick; + this.noOnClick(); + this.Document.onClick = hadOnClick ? undefined : ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }); + this.Document.waitForDoubleClickToClick = hadOnClick ? undefined : 'never'; }; @undoBatch @action - followLinkOnClick = (location: Opt, zoom: boolean): void => { + followLinkOnClick = (): void => { this.Document.ignoreClick = false; - this.Document._isLinkButton = true; + this.Document.onClick = ScriptField.MakeScript('followLink(this,altKey)', { altKey: 'boolean' }); this.Document.followLinkToggle = false; - this.Document.followLinkZoom = zoom; - this.Document.followLinkLocation = location; - }; - @undoBatch - @action - selectOnClick = (): void => { - this.Document.ignoreClick = false; - this.Document._isLinkButton = false; - this.Document.followLinkToggle = false; - this.Document.onClick = this.layoutDoc.onClick = undefined; + this.Document.followLinkZoom = false; + this.Document.followLinkLocation = undefined; }; @undoBatch noOnClick = (): void => { this.Document.ignoreClick = false; - this.Document._isLinkButton = false; + this.Document.onClick = Doc.GetProto(this.Document).onClick = undefined; }; @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document); @@ -652,8 +623,7 @@ export class DocumentViewInternal extends DocComponent { @@ -744,29 +714,20 @@ export class DocumentViewInternal extends DocComponent (this.Document.followLinkZoom = !this.Document.followLinkZoom), - icon: this.Document.ignoreClick ? 'unlock' : 'lock', - }); if (!this.Document.annotationOn) { const options = cm.findByDescription('Options...'); const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); - onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' }); - onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' }); - onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(false, true), icon: 'map-pin' }); + onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' }); onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' }); !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); } else if (LinkManager.Links(this.Document).length) { - onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' }); - onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' }); - onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' }); + onClicks.push({ description: 'Select on Click', event: () => this.noOnClick(), icon: 'link' }); + onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' }); !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, subitems: onClicks, icon: 'mouse-pointer' }); } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f38ebba27..22ecaa299 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -202,7 +202,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { if (separation > 100) { const dragData = new DragManager.DocumentDragData([this.rootDoc]); dragData.dropAction = 'alias'; - dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'isLinkButton']; + dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'onClick']; DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); return true; } else { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e38d7e2a8..502811e35 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -119,7 +119,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent 1 ? new List(splits) : newText; if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal; Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 361e000f9..809315963 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -17,7 +17,7 @@ import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from '../../../../fields/RichTextField'; import { RichTextUtils } from '../../../../fields/RichTextUtils'; -import { ComputedField } from '../../../../fields/ScriptField'; +import { ComputedField, ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; @@ -1933,7 +1933,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 9a74b5dba..92696240b 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -97,7 +97,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
1 ? args.split('=')[0] : 'PARAMS'; + const params = args.split('=').length > 1 ? args.split('=')[0] : ''; const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc); const expandedLayoutFieldKey = (templateField || layoutFielddKey) + '-layout[' + templateLayoutDoc[Id] + (args ? `(${args})` : '') + ']'; let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey]; @@ -945,7 +945,7 @@ export namespace Doc { expandedTemplateLayout = undefined; _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) { - if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) { + if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc))) { expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params } else { templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype @@ -981,8 +981,8 @@ export namespace Doc { console.log('No, no, no!'); return { layout: childDoc, data: childDoc }; } - const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc; - return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '(' + StrCast(containerDoc.PARAMS) + ')'), data: resolvedDataDoc }; + const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc; + return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '()'), data: resolvedDataDoc }; } export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc { diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 5b489a96c..b7fd06973 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -89,7 +89,6 @@ export const documentSchema = createSchema({ hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints. isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations - isLinkButton: 'boolean', // whether document functions as a link follow button to follow the first link on the document when clicked layers: listSpec('string'), // which layers the document is part of _lockedPosition: 'boolean', // whether the document can be moved (dragged) _lockedTransform: 'boolean', // whether a freeformview can pan/zoom -- cgit v1.2.3-70-g09d2 From 650cc00d18c97f89aef77f50703aa9b525dd0368 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 14 Apr 2023 11:12:49 -0400 Subject: got rid of CollectionView prop. fixed DocFocusOrOpen to toggle targets and zoom correctly. --- src/client/util/DocumentManager.ts | 12 +++++++----- .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/LinkFollower.ts | 2 +- src/client/views/MainView.tsx | 1 - src/client/views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 21 +-------------------- src/client/views/SidebarAnnos.tsx | 1 - src/client/views/StyleProvider.tsx | 4 ++-- src/client/views/TemplateMenu.tsx | 1 - src/client/views/collections/CollectionMenu.tsx | 1 - .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 6 +++--- src/client/views/collections/CollectionTreeView.tsx | 6 +++--- src/client/views/collections/CollectionView.tsx | 1 - src/client/views/collections/TabDocView.tsx | 1 - src/client/views/collections/TreeView.tsx | 6 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 +++++++---- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 14 +++++++------- src/client/views/nodes/ImageBox.tsx | 1 - src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 1 - src/client/views/nodes/PDFBox.tsx | 1 - src/client/views/nodes/ScreenshotBox.tsx | 1 - src/client/views/nodes/VideoBox.tsx | 2 -- src/client/views/nodes/WebBox.tsx | 1 - .../views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 1 - 29 files changed, 39 insertions(+), 69 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ad89e8653..e01457b4f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -227,6 +227,7 @@ export class DocumentManager { let rootContextView = docViewPath.shift(); await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined }))); if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden; + else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) MainView.addDocTabFunc(rootContextView.rootDoc, options.openLocation); }; // shows a document by first: @@ -246,10 +247,9 @@ export class DocumentManager { const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc)); if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!); options.didMove = true; - docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation as OpenWhere); + docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); }); - docContextPath.shift(); const childViewIterator = async (docView: DocumentView) => { const innerDoc = docContextPath.shift(); @@ -297,15 +297,17 @@ export class DocumentManager { } } } -export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, openLocation: OpenWhere.addRight }, containingDoc?: Doc) { +export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) { const func = () => { const cv = DocumentManager.Instance.getDocumentView(containingDoc); const dv = DocumentManager.Instance.getDocumentView(doc, cv); if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) { DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc)); } else { - const showDoc = Doc.BestAlias(DocCast((containingDoc ?? doc.context) !== Doc.MyFilesystem ? containingDoc ?? doc.context : undefined, doc)); - DocumentManager.Instance.showDocument(showDoc, { ...options, toggleTarget: undefined }, () => DocumentManager.Instance.showDocument(doc, options)).then(() => { + const container = DocCast(containingDoc ?? doc.context); + const showDoc = !Doc.IsSystem(container) ? container : doc; + options.toggleTarget = undefined; + DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { const cv = DocumentManager.Instance.getDocumentView(containingDoc); const dv = DocumentManager.Instance.getDocumentView(doc, cv); dv && Doc.linkFollowHighlight(dv.rootDoc); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 76b1323c4..b9bb22564 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -157,7 +157,7 @@ export class DirectoryImportBox extends React.Component { x: NumCast(doc.x), y: NumCast(doc.y) + offset, }; - const parent = _.nth(this.props.docViewPath(), -2); // last element of path is this box's document view, 2nd to last is any collection or other document that may contain it. + const parent = this.props.DocumentView?.().props.docViewPath().lastElement(); if (parent?.rootDoc.type === DocumentType.COL) { let importContainer: Doc; if (docs.length < 50) { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index d4d7c66f5..ba2edb65c 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -75,7 +75,7 @@ export class LinkFollower { zoomTime: NumCast(sourceDoc.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, - openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox), + openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox) as OpenWhere, effect: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), }; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0384d925e..ccc9a7215 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -883,7 +883,6 @@ export class MainView extends React.Component { isSelected={returnFalse} docViewPath={returnEmptyDoclist} moveDocument={this.moveButtonDoc} - CollectionView={undefined} addDocument={this.addButtonDoc} addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index 00c3400cb..935d551c6 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component { if (!this.props.DocView) return; col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; - if (col._viewType === CollectionViewType.Freeform) { - col._panX = NumCast(target.x) + NumCast(target._width) / 2; - col._panY = NumCast(target.y) + NumCast(target._height) / 2; - } - col.hidden = false; - //this.props.addDocTab(col, (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere); - setTimeout( - () => - this.props.DocView && - DocFocusOrOpen( - Doc.GetProto(this.props.DocView.props.Document), - { - // - willZoomCentered: true, - openLocation: (OpenWhere.toggle + ':' + OpenWhereMod.right) as OpenWhere, - }, - col - ), - 100 - ); + DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col); }; render() { diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 2d2b0f83e..b0aab968e 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -243,7 +243,6 @@ export class SidebarAnnos extends React.Component { removeDocument={this.removeDocument} moveDocument={this.moveDocument} addDocument={this.addDocument} - CollectionView={undefined} ScreenToLocalTransform={this.screenToLocalTransform} renderDepth={this.props.renderDepth + 1} viewType={CollectionViewType.Stacking} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 739d6d819..5f16e0ebd 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -5,7 +5,7 @@ import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; -import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; @@ -383,7 +383,7 @@ export function DashboardStyleProvider(doc: Opt, props: Opt {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => { - DocFocusOrOpen(doc, { toggleTarget: true, willPan: true, openLocation: OpenWhere.addRight }, props?.docViewPath().lastElement()?.rootDoc); + DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.context ?? doc?.annotationOn)); })} ); diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index c5a501aa6..1c816aa05 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -113,7 +113,6 @@ export class TemplateMenu extends React.Component { {templateMenu} { isSelected={returnFalse} docViewPath={returnEmptyDoclist} moveDocument={returnFalse} - CollectionView={undefined} addDocument={returnFalse} addDocTab={returnFalse} pinToPres={emptyFunction} diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 80e81bc1c..99d4d0bee 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -162,7 +162,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } @action - moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => { + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => { return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false; }; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 67f5dc9f4..bdad325d5 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -230,7 +230,7 @@ export class CollectionStackingView extends CollectionSubView this.props.isAnyChildContentActive(); @action - moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => { + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false; }; createRef = (ele: HTMLDivElement | null) => { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 132ed6fb6..5581ac8fe 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -19,7 +19,6 @@ import { DocComponent } from '../DocComponent'; import React = require('react'); export interface SubCollectionViewProps extends CollectionViewProps { - CollectionView: Opt; isAnyChildContentActive: () => boolean; } @@ -201,8 +200,9 @@ export function CollectionSubView(moreProps?: X) { } } - addDocument = (doc: Doc | Doc[]) => this.props.addDocument?.(doc) || false; - + addDocument = (doc: Doc | Doc[], annotationKey?: string) => this.props.addDocument?.(doc, annotationKey) || false; + removeDocument = (doc: Doc | Doc[], annotationKey?: string) => this.props.removeDocument?.(doc, annotationKey) || false; + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string) => this.props.moveDocument?.(doc, targetCollection, addDocument); @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const docDragData = de.complete.docDragData; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 92932fb61..f81c17a7b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -374,10 +374,10 @@ export class CollectionTreeView extends CollectionSubView this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1)); - addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false; - remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false; + addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}-annotations`) || false; + remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false; moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => - this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false; + this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false; @observable _headerHeight = 0; @computed get content() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 53fbcc3cc..d7a889362 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -276,7 +276,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent {
boolean; panelWidth: () => number; panelHeight: () => number; - addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; + addDocument: (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => boolean; removeDoc: ((doc: Doc | Doc[]) => boolean) | undefined; moveDocument: DragManager.MoveFunction; isContentActive: (outsideReaction?: boolean) => boolean; @@ -373,7 +373,7 @@ export class TreeView extends React.Component { dropping: boolean = false; dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, removeDocument: DragManager.RemoveFunction | undefined, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) { - const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); + const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, undefined, before); const localAdd = (doc: Doc | Doc[]) => { const innerAdd = (doc: Doc) => { const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField; @@ -1186,7 +1186,7 @@ export class TreeView extends React.Component { }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined); - const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); + const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); const childLayout = Doc.Layout(pair.layout); const rowHeight = () => { const aspect = Doc.NativeAspect(childLayout); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a3f5e73fb..33fc2ddf3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -311,7 +311,7 @@ export class CollectionFreeFormView extends CollectionSubView boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: OpenWhere) => boolean; filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) - addDocument?: (doc: Doc | Doc[]) => boolean; - removeDocument?: (doc: Doc | Doc[]) => boolean; - moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; + addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; + removeDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; + moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => boolean; pinToPres: (document: Doc, pinProps: PinProps) => void; ScreenToLocalTransform: () => Transform; bringToFront: (doc: Doc, sendToBack?: boolean) => void; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 22ecaa299..7c98aa6e4 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -478,7 +478,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent <> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b144c9318..7a7d4fe37 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -963,7 +963,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent { renderDepth={this.props.renderDepth + 1} isAnnotationOverlay={true} fieldKey={this.props.fieldKey + '-annotations'} - CollectionView={undefined} getScrollHeight={this.getScrollHeight} setPreviewCursor={this.setPreviewCursor} setBrushViewer={this.setBrushViewer} -- cgit v1.2.3-70-g09d2 From 0a66b0f369a13d5f399bf125727aff73cd6fd1b4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 19 Apr 2023 13:01:37 -0400 Subject: fixed image importing metadata for gpt. fixed showTitle UI to be able to turn off titles on templates (like icon views). added ui for choosing primary/alternate image and a drop target for setting alternate. --- src/client/documents/Documents.ts | 2 + src/client/views/MainView.tsx | 2 + src/client/views/PropertiesButtons.tsx | 6 ++- src/client/views/nodes/ImageBox.scss | 8 +++ src/client/views/nodes/ImageBox.tsx | 58 +++++++++++++++++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 ++- src/client/views/nodes/trails/PresBox.tsx | 4 +- 7 files changed, 73 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 09b45a481..a0149eadf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -147,6 +147,8 @@ export class DocumentOptions { _panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); _width?: NUMt = new NumInfo('displayed width of a document'); _height?: NUMt = new NumInfo('displayed height of document'); + 'data-nativeWidth'?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)'); + 'data-nativeHeight'?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)'); _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)'); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)'); _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers'); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index acbe0cbc3..4cbf8a811 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -314,6 +314,8 @@ export class MainView extends React.Component { fa.faArrowUp, fa.faBolt, fa.faBullseye, + fa.faTurnUp, + fa.faTurnDown, fa.faCaretUp, fa.faCat, fa.faCheck, diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 031d501ad..98dcf4f21 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -177,7 +177,11 @@ export class PropertiesButtons extends React.Component<{}, {}> { '_showTitle', on => 'Switch between title styles', on => 'text-width', - (dv, doc) => ((dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? 'title' : (dv?.rootDoc || doc)._showTitle === 'title' ? 'title:hover' : undefined) + (dv, doc) => { + const tdoc = dv?.rootDoc || doc; + const newtitle = !tdoc._showTitle ? 'title' : tdoc._showTitle === 'title' ? 'title:hover' : ''; + tdoc._showTitle = newtitle; + } ); } @computed get autoHeightButton() { diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 6359a9491..22dbc1e80 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -119,6 +119,14 @@ } } } +.imageBox-alternateDropTarget { + position: absolute; + color: white; + background: black; + right: 0; + bottom: 0; + z-index: 2; +} .imageBox-fader img { position: absolute; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e8d4be1fd..98df777cb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,3 +1,5 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; @@ -8,7 +10,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -19,6 +21,7 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../../views/ContextMenu'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -57,6 +60,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent>, addAsAnnotation: boolean) => Opt = () => undefined; + private _overlayIconRef = React.createRef(); @observable _curSuffix = ''; @observable _uploadIcon = uploadIcons.idle; @@ -131,10 +135,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent { if (de.complete.docDragData) { - if (de.metaKey) { + const targetIsBullseye = (ele: HTMLElement): boolean => { + if (!ele) return false; + if (ele === this._overlayIconRef.current) return true; + return targetIsBullseye(ele.parentElement as HTMLElement); + }; + if (de.metaKey || targetIsBullseye(e.target as HTMLElement)) { de.complete.docDragData.droppedDocuments.forEach( action((drop: Doc) => { Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop); + this.rootDoc[this.fieldKey + '-usePath'] = 'alternate:hover'; e.stopPropagation(); }) ); @@ -154,8 +164,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes); - @undoBatch - setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']); @undoBatch setNativeSize = action(() => { @@ -239,7 +247,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(this.choosePath(field.url)), icon: 'copy' }); if (!Doc.noviceMode) { funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' }); @@ -356,6 +363,41 @@ export class ImageBox extends ViewBoxAnnotatableComponent + toggle between + + primary, + + + alternate, + + and show + + alternate on hover + +
+ }> +
setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} + style={{ + display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none', + width: 'min(10%, 25px)', + height: 'min(10%, 25px)', + background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', + color: usePath === undefined ? 'black' : 'white', + }}> + +
+ + ); + } @computed get paths() { const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc @@ -389,15 +431,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
{fadepath === srcpath ? null : ( -
+
)} @@ -405,6 +446,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent + {this.overlayImageIcon}
); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 3e60441aa..bbe38cf99 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -885,13 +885,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { bestTarget._dataTransition = `all ${transTime}ms`; const fkey = Doc.LayoutFieldKey(bestTarget); Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; - bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt; + bestTarget[fkey + '-usePath'] = activeItem.presUsePath; setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); } if ((pinDataTypes?.textview && activeItem.presData !== undefined) || (!pinDataTypes && activeItem.presData !== undefined)) { @@ -613,7 +613,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinProps?.activeFrame !== undefined; const fkey = Doc.LayoutFieldKey(targetDoc); if (pinProps.pinData.dataview) { - pinDoc.presUseAlt = targetDoc[fkey + '-useAlt']; + pinDoc.presUsePath = targetDoc[fkey + '-usePath']; pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data; } if (pinProps.pinData.dataannos) { -- cgit v1.2.3-70-g09d2 From 33299a19d86948051eef442825c0b3241d1ac619 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 24 Apr 2023 11:55:53 -0400 Subject: fixed isContentActive=false to apply to stacking collections. fixed pile view to be faster and to work in fit content panels. fixed issues with hidden docs and freeformviews that act as lightboxes - hidden docs can be shown as the lightbox doc without modifying the hidden flag to allow collection state to be restored. --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 12 +-- src/client/util/DocumentManager.ts | 15 ++- src/client/views/DocComponent.tsx | 104 ++++++++++---------- src/client/views/LightboxView.tsx | 6 +- src/client/views/MainView.tsx | 18 ++-- src/client/views/PropertiesButtons.tsx | 7 +- src/client/views/StyleProvider.tsx | 5 +- .../views/collections/CollectionPileView.tsx | 35 +++---- .../views/collections/CollectionStackingView.tsx | 15 ++- .../views/collections/CollectionTimeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 1 + src/client/views/collections/TreeView.tsx | 2 - .../CollectionFreeFormLayoutEngines.tsx | 23 ++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 107 ++++++++++++++------- .../collections/collectionFreeForm/MarqueeView.tsx | 8 +- src/client/views/nodes/DocumentView.tsx | 57 ++++++----- src/client/views/nodes/ImageBox.scss | 4 +- src/client/views/nodes/ImageBox.tsx | 1 + src/client/views/nodes/WebBox.scss | 1 + .../views/nodes/formattedText/DashDocView.tsx | 2 + .../nodes/formattedText/FormattedTextBox.scss | 30 ++++++ .../views/nodes/formattedText/FormattedTextBox.tsx | 47 ++++++++- src/fields/util.ts | 3 + 24 files changed, 315 insertions(+), 192 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a0149eadf..dee5feebc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1066,7 +1066,7 @@ export namespace Docs { } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', enableDragWhenActive: true, _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index abf7313a4..cdb12624f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -619,12 +619,12 @@ export class CurrentUserUtils { } static viewTools(): Button[] { return [ - { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Snap\xA0Lines",icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "View\xA0All", icon: "object-group",toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Arrange", icon: "window", toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Reset", icon: "check", toolTip: "Reset View", btnType: ButtonType.ClickButton, expertMode: false, backgroundColor:"transparent", scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform + { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Snap\xA0Lines", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "View\xA0All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Arrange",icon: "arrow-down-short-wide",toolTip: "Toggle Auto Arrange",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Reset", icon: "check", toolTip: "Reset View", btnType: ButtonType.ClickButton, expertMode: false, backgroundColor:"transparent", scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform ] } static textTools():Button[] { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index e01457b4f..4542c1c05 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -8,8 +8,7 @@ import { CollectionViewType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; -import { MainView } from '../views/MainView'; -import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { PresBox } from '../views/nodes/trails'; @@ -227,7 +226,7 @@ export class DocumentManager { let rootContextView = docViewPath.shift(); await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined }))); if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden; - else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) MainView.addDocTabFunc(rootContextView.rootDoc, options.openLocation); + else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) DocumentViewInternal.addDocTabFunc(rootContextView.rootDoc, options.openLocation); }; // shows a document by first: @@ -247,9 +246,17 @@ export class DocumentManager { const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc)); if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!); options.didMove = true; - docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); + docContextPath.some(doc => TabDocView.Activate(doc)) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); }); + if (options.openLocation === OpenWhere.lightbox) { + // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox) + const target = DocCast(targetDoc.annotationOn, targetDoc); + const contextView = this.getDocumentView(DocCast(target.context)); + if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) { + await new Promise(waitres => setTimeout(() => waitres())); + } + } docContextPath.shift(); const childViewIterator = async (docView: DocumentView) => { const innerDoc = docContextPath.shift(); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index a59189fd2..d60ad68c6 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -141,29 +141,25 @@ export function ViewBoxAnnotatableComponent

() const effectiveAcl = GetEffectiveAcl(this.dataDoc); const indocs = doc instanceof Doc ? [doc] : doc; const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); - if (docs.length) { - docs.map(doc => { - Doc.SetInPlace(doc, 'followLinkToggle', undefined, true); - doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true); + + docs.forEach(doc => doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true)); + const targetDataDoc = this.dataDoc; + const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); + const toRemove = value.filter(v => docs.includes(v)); + + if (toRemove.length !== 0) { + const recent = Doc.MyRecentlyClosed; + toRemove.forEach(doc => { + leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); + Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + doc.context = undefined; + if (recent) { + Doc.RemoveDocFromList(recent, 'data', doc); + doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true); + } }); - const targetDataDoc = this.dataDoc; - const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); - const toRemove = value.filter(v => docs.includes(v)); - - if (toRemove.length !== 0) { - const recent = Doc.MyRecentlyClosed; - toRemove.forEach(doc => { - leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); - Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); - doc.context = undefined; - if (recent) { - Doc.RemoveDocFromList(recent, 'data', doc); - doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true); - } - }); - this.isAnyChildContentActive() && this.props.select(false); - return true; - } + this.isAnyChildContentActive() && this.props.select(false); + return true; } return false; @@ -190,46 +186,44 @@ export function ViewBoxAnnotatableComponent

() return false; } const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); - const added = docs.filter(d => !docList.includes(d)); const effectiveAcl = GetEffectiveAcl(targetDataDoc); + if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { + return false; + } + const added = docs; if (added.length) { - if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { - return false; - } else { - if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) { - added.forEach(d => { - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); - } - }); - } + if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) { + added.forEach(d => { + for (const key of Object.keys(this.props.Document[AclSym])) { + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); + } + }); + } - if (effectiveAcl === AclAugment) { - added.map(doc => { - if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc); + if (effectiveAcl === AclAugment) { + added.map(doc => { + if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc); + doc.context = this.props.Document; + if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; + Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + }); + } else { + added + .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) + .map(doc => { + // only make a pushpin if we have acl's to edit the document + //DocUtils.LeavePushpin(doc); + doc._stayInCollection = undefined; doc.context = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; - Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + + Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc); }); - } else { - added - .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) - .map(doc => { - // only make a pushpin if we have acl's to edit the document - //DocUtils.LeavePushpin(doc); - doc._stayInCollection = undefined; - doc.context = this.props.Document; - if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; - - Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc); - }); - const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List; - if (annoDocs instanceof List) annoDocs.push(...added); - else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); - targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now())); - } + const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List; + if (annoDocs instanceof List) annoDocs.push(...added); + else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); + targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now())); } } return true; diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 69eec8456..c18a89481 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -63,10 +63,8 @@ export class LightboxView extends React.Component { Doc.ActiveTool = InkTool.None; MainView.Instance._exploreMode = false; } else { - if (doc) { - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); - l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); - } + const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); //TabDocView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4cbf8a811..e4554c339 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -49,7 +49,7 @@ import { LinkMenu } from './linking/LinkMenu'; import './MainView.scss'; import { AudioBox } from './nodes/AudioBox'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; @@ -222,6 +222,7 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); + DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl; MainView.Instance = this; DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any); @@ -245,6 +246,7 @@ export class MainView extends React.Component { ...[ fa.faExclamationCircle, fa.faEdit, + fa.faArrowDownShortWide, fa.faTrash, fa.faTrashAlt, fa.faShare, @@ -584,7 +586,7 @@ export class MainView extends React.Component { Document={this.headerBarDoc} DataDoc={undefined} addDocument={undefined} - addDocTab={MainView.addDocTabFunc} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={DefaultStyleProvider} @@ -619,7 +621,7 @@ export class MainView extends React.Component { Document={this.mainContainer!} DataDoc={undefined} addDocument={undefined} - addDocTab={MainView.addDocTabFunc} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={this._hideUI ? DefaultStyleProvider : undefined} @@ -688,7 +690,7 @@ export class MainView extends React.Component { sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); - static addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => { + static addDocTabFunc_impl = (doc: Doc, location: OpenWhere): boolean => { const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':'); const keyValue = whereFields[1]?.includes('KeyValue'); const whereMods: OpenWhereMod = whereFields.length > 1 ? (whereFields[1].replace('KeyValue', '') as OpenWhereMod) : OpenWhereMod.none; @@ -716,7 +718,7 @@ export class MainView extends React.Component { Document={this._sidebarContent.proto || this._sidebarContent} DataDoc={undefined} addDocument={undefined} - addDocTab={MainView.addDocTabFunc} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} @@ -748,7 +750,7 @@ export class MainView extends React.Component { Document={Doc.MyLeftSidebarMenu} DataDoc={undefined} addDocument={undefined} - addDocTab={MainView.addDocTabFunc} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} rootSelected={returnTrue} removeDocument={returnFalse} @@ -810,7 +812,7 @@ export class MainView extends React.Component {

)}
- {this.propertiesWidth() < 10 ? null : } + {this.propertiesWidth() < 10 ? null : }
@@ -889,7 +891,7 @@ export class MainView extends React.Component { docViewPath={returnEmptyDoclist} moveDocument={this.moveButtonDoc} addDocument={this.addButtonDoc} - addDocTab={MainView.addDocTabFunc} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} removeDocument={this.remButtonDoc} ScreenToLocalTransform={this.buttonBarXf} diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 98dcf4f21..cf808f801 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -136,10 +136,11 @@ export class PropertiesButtons extends React.Component<{}, {}> { // containerDoc.noShadow = // containerDoc.disableDocBrushing = // containerDoc._forceActive = - containerDoc._fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; - containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; + //containerDoc._fitContentsToBox = + containerDoc._isLightbox = !containerDoc._isLightbox; + //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); - dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); + //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false))); }); } diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5f16e0ebd..d98e5aa80 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -30,7 +30,6 @@ export enum StyleProp { TreeViewSortings = 'treeViewSortings', // options for how to sort tree view items DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view Opacity = 'opacity', // opacity of the document view - Hidden = 'hidden', // whether the document view should not be isplayed BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views BorderRounding = 'borderRounding', // border radius of the document view Color = 'color', // foreground color of Document view items @@ -172,8 +171,6 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt this.childDocs.length, - num => !num && this.props.CollectionFreeFormDocumentView?.().props.removeDocument?.(this.props.Document) - ); } componentWillUnmount() { this.layoutDoc._chromeHidden = this._originalChrome; @@ -48,13 +42,15 @@ export class CollectionPileView extends CollectionSubView() { @undoBatch removePileDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { - (doc instanceof Doc ? [doc] : doc).map(undoBatch(d => Doc.deiconifyView(d))); - return this.props.moveDocument?.(doc, targetCollection, addDoc) || false; + (doc instanceof Doc ? [doc] : doc).forEach(d => Doc.deiconifyView(d)); + const ret = this.props.moveDocument?.(doc, targetCollection, addDoc) || false; + if (ret && !DocListCast(this.rootDoc[this.fieldKey ?? 'data']).length) this.props.DocumentView?.().props.removeDocument?.(this.rootDoc); + return ret; }; - toggleIcon = () => { + @computed get toggleIcon() { return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' }); - }; + } // returns the contents of the pileup in a CollectionFreeFormView @computed get contents() { @@ -63,11 +59,11 @@ export class CollectionPileView extends CollectionSubView() {
@@ -87,15 +83,12 @@ export class CollectionPileView extends CollectionSubView() { this.layoutDoc._panY = -10; this.props.Document._pileLayoutEngine = computePassLayout.name; } else { - const defaultSize = 25; - !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 250); + const defaultSize = 500; !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); - if (this.layoutEngine() === computePassLayout.name) { - this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2; - this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; - this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); - this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym](); - } + this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2; + this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; + this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); + this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym](); this.layoutDoc._panX = this.layoutDoc._panY = 0; this.layoutDoc._width = this.layoutDoc._height = defaultSize; this.props.Document._pileLayoutEngine = computeStarburstLayout.name; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index bdad325d5..020fe1cb4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -10,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -238,9 +238,7 @@ export class CollectionStackingView extends CollectionSubView this.props.childClickScript || ScriptCast(this.Document.onChildClick); - } + onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); } @@ -300,7 +298,13 @@ export class CollectionStackingView extends CollectionSubView - this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; + this.props.isContentActive?.() === false + ? false + : this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) + ? true + : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false + ? false + : undefined; isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined); // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list @@ -320,6 +324,7 @@ export class CollectionStackingView extends CollectionSubView boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) + childContentsActive?: () => boolean | undefined; childFitWidth?: (child: Doc) => boolean; childShowTitle?: () => string; childOpacity?: () => number; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 8fb610b87..4adf86683 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -801,7 +801,6 @@ export class TreeView extends React.Component { case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1; case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); case StyleProp.Highlighting: if (this.props.treeView.outlineMode) return undefined; - case StyleProp.Hidden: return false; case StyleProp.BoxShadow: return undefined; case StyleProp.DocContents: const highlightIndex = this.props.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.isBrushedHighlightedDegree(doc); @@ -827,7 +826,6 @@ export class TreeView extends React.Component { }; embeddedStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (property.startsWith(StyleProp.Decorations)) return null; - if (property.startsWith(StyleProp.Hidden)) return false; return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView }; onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 81b0c4d8a..2549ee0b3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -93,6 +93,7 @@ export function computePassLayout(poolData: Map, pivotDoc: Doc width: layout[WidthSym](), height: layout[HeightSym](), pair: { layout, data }, + transition: 'all .3s', replica: '', }); }); @@ -100,28 +101,27 @@ export function computePassLayout(poolData: Map, pivotDoc: Doc } export function computeStarburstLayout(poolData: Map, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { - const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel const docMap = new Map(); - const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75 - const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize]; - const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize]; + const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)]; childPairs.forEach(({ layout, data }, i) => { - const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75 + const aspect = layout[HeightSym]() / layout[WidthSym](); + const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect); const deg = (i / childPairs.length) * Math.PI * 2; docMap.set(layout[Id], { - x: Math.cos(deg) * burstRadius[0] - docSize / 2, - y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2, - width: docSize, //layout[WidthSym](), - height: (docSize * layout[HeightSym]()) / layout[WidthSym](), + x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)), + y: Math.min(burstDiam[1] / 2 - docSize * aspect, Math.max(-burstDiam[1] / 2, (Math.sin(deg) * burstDiam[1]) / 2 - (docSize / 2) * aspect)), + width: docSize, + height: docSize * aspect, zIndex: NumCast(layout.zIndex), pair: { layout, data }, replica: '', color: 'white', backgroundColor: 'white', + transition: 'all 0.3s', }); }); - const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined }; - return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]); + const divider = { type: 'div', color: 'transparent', x: -burstDiam[0] / 2, y: -burstDiam[1] / 2, width: 15, height: 15, payload: undefined }; + return normalizeResults(burstDiam, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]); } export function computePivotLayout(poolData: Map, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { @@ -424,6 +424,7 @@ function normalizeResults( color: newPosRaw.color, pair: ele[1].pair, }; + if (newPosRaw.transition) newPos.transition = newPosRaw.transition; poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos }); } }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ff0d01f29..1fc4d9259 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -15,7 +15,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -308,6 +308,7 @@ export class CollectionFreeFormView extends CollectionSubView { + if (this._lightboxDoc) 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); @@ -327,7 +328,7 @@ export class CollectionFreeFormView extends CollectionSubView> => { return new Promise>(res => { - doc.hidden && (doc.hidden = false); + if (doc.hidden && this._lightboxDoc !== doc) doc.hidden = false; const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -778,7 +779,9 @@ export class CollectionFreeFormView extends CollectionSubView { + if (this._lightboxDoc) this._lightboxDoc = undefined; if (this.onBrowseClickHandler()) { if (this.props.DocumentView?.()) { this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY }); @@ -1274,7 +1277,7 @@ export class CollectionFreeFormView extends CollectionSubView(doc instanceof Doc ? [doc] : doc); + this._lightboxDoc = doc; + return true; + } else if (this.childDocList?.includes(doc)) { + if (doc.hidden) doc.hidden = false; return true; } } return this.props.addDocTab(doc, where); }); + @observable _lightboxDoc: Opt; getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData { const childDoc = params.pair.layout; @@ -1936,7 +1942,9 @@ export class CollectionFreeFormView extends CollectionSubView Math.max(0, this.props.PanelWidth() - 30); + lightboxPanelHeight = () => Math.max(0, this.props.PanelHeight() - 30); + lightboxScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-15, -15); render() { TraceMobx(); return ( @@ -1965,36 +1973,65 @@ export class CollectionFreeFormView extends CollectionSubView - {this._firstRender ? this.placeholder : this.marqueeView} - {this.props.noOverlay ? null : } - - {/* // uncomment to show snap lines */} -
- - {this._hLines?.map(l => ( - - ))} - {this._vLines?.map(l => ( - - ))} - -
+ {this._lightboxDoc ? ( +
+ +
+ ) : ( + <> + {this._firstRender ? this.placeholder : this.marqueeView} + {this.props.noOverlay ? null : } + + {/* // uncomment to show snap lines */} +
+ + {this._hLines?.map(l => ( + + ))} + {this._vLines?.map(l => ( + + ))} + +
- {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? ( -
- ) : null} + {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? ( +
+ ) : null} + + )}
); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d443df0f3..eaeb5f933 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -371,10 +371,10 @@ export class MarqueeView extends React.Component { + delete = (e?: React.PointerEvent | KeyboardEvent | undefined, hide?: boolean) => { const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - selected.forEach(doc => this.props.removeDocument?.(doc)); + selected.forEach(doc => (hide ? (doc.hidden = true) : this.props.removeDocument?.(doc))); this.cleanupInteractions(false); MarqueeOptionsMenu.Instance.fadeOut(true); @@ -550,11 +550,11 @@ export class MarqueeView extends React.Component this._titleRef.current?.setIsFocused(true)); // use timeout in case title wasn't shown to allow re-render so that titleref will be defined }; + public static addDocTabFunc: (doc: Doc, location: OpenWhere) => boolean = returnFalse; + onClick = action((e: React.MouseEvent | React.PointerEvent) => { if (!this.Document.ignoreClick && this.pointerEvents !== 'none' && this.props.renderDepth >= 0 && Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { let stopPropagate = true; @@ -446,24 +447,33 @@ export class DocumentViewInternal extends DocComponent any); if (!this.disableClickScriptFunc && this.onClickHandler?.script) { const { clientX, clientY, shiftKey, altKey, metaKey } = e; - const func = () => - this.onClickHandler?.script.run( - { - this: this.layoutDoc, - self: this.rootDoc, - _readOnly_: false, - scriptContext: this.props.scriptContext, - documentView: this.props.DocumentView(), - clientX, - clientY, - shiftKey, - altKey, - metaKey, - }, - console.log - ).result?.select === true - ? this.props.select(false) - : ''; + const func = () => { + // replace default add doc func with this view's add doc func. + // to allow override behaviors for how to display links to undisplayed documents. + // e.g., if this document is part of a labeled 'lightbox' container, then documents will be shown in place + // instead of in the global lightbox + const oldFunc = DocumentViewInternal.addDocTabFunc; + DocumentViewInternal.addDocTabFunc = this.props.addDocTab; + const res = + this.onClickHandler?.script.run( + { + this: this.layoutDoc, + self: this.rootDoc, + _readOnly_: false, + scriptContext: this.props.scriptContext, + documentView: this.props.DocumentView(), + clientX, + clientY, + shiftKey, + altKey, + metaKey, + }, + console.log + ).result?.select === true + ? this.props.select(false) + : ''; + DocumentViewInternal.addDocTabFunc = oldFunc; + }; clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click')); } else { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part @@ -1318,9 +1328,6 @@ export class DocumentView extends React.Component { const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton; return hideCount ? null : ; } - @computed get hidden() { - return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); - } @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; } @@ -1497,7 +1504,7 @@ export class DocumentView extends React.Component { const xshift = Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined; const yshift = Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined; - return this.hidden ? null : ( + return (
(this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))}> {!this.props.Document || !this.props.PanelWidth() ? null : (
{ height: this._height, position: 'absolute', display: 'inline-block', + left: 0, + top: 0, }} onPointerLeave={this.onPointerLeave} onPointerEnter={this.onPointerEnter} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index fd7fbb333..3b42c41a5 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -24,6 +24,27 @@ audiotag:hover { transform: scale(2); transform-origin: bottom center; } +.formattedTextBox { + touch-action: none; + background: inherit; + padding: 0; + border-width: 0px; + border-radius: inherit; + border-color: $medium-gray; + box-sizing: border-box; + background-color: inherit; + border-style: solid; + overflow-y: auto; + overflow-x: hidden; + color: inherit; + display: flex; + flex-direction: row; + transition: opacity 1s; + width: 100%; + position: absolute; + top: 0; + left: 0; +} .formattedTextBox-cont { touch-action: none; @@ -51,6 +72,15 @@ audiotag:hover { position: absolute; } } +.formattedTextBox-alternateButton { + position: absolute; + color: white; + background: black; + right: 0; + bottom: 0; + width: 15; + height: 15; +} .formattedTextBox-outer-selected, .formattedTextBox-outer { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index bbe38cf99..2755d5100 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1,9 +1,9 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; import { isEqual } from 'lodash'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { Configuration, OpenAIApi } from 'openai'; import { baseKeymap, selectAll } from 'prosemirror-commands'; import { history } from 'prosemirror-history'; import { inputRules } from 'prosemirror-inputrules'; @@ -1928,6 +1928,45 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ); } + @computed get overlayAlternateIcon() { + const usePath = this.rootDoc[`${this.props.fieldKey}-usePath`]; + return ( + + toggle between + + primary, + + + alternate, + + and show + + alternate on hover + +
+ }> +
+ setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.props.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined)) + } + style={{ + display: this.props.isContentActive() ? 'block' : 'none', + background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', + color: usePath === undefined ? 'black' : 'white', + }}> + +
+ + ); + } + @computed get fieldKey() { + const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}-usePath`]); + return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `-${usePath.replace(':hover', '')}` : ''); + } + @observable _isHovering = false; render() { TraceMobx(); const active = this.props.isContentActive() || this.props.isSelected(); @@ -1944,7 +1983,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent return styleFromLayoutString?.height === '0px' ? null : (
(this._isHovering = true))} + onPointerLeave={action(() => (this._isHovering = false))} ref={r => r?.addEventListener( 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) @@ -1966,6 +2007,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
); diff --git a/src/fields/util.ts b/src/fields/util.ts index 70d9ed61f..92f3a69eb 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -107,8 +107,11 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number redo: () => (receiver[prop] = value), undo: () => { const wasUpdate = receiver[UpdatingFromServer]; + const wasForce = receiver[ForceServerWrite]; + receiver[ForceServerWrite] = true; // needed since writes aren't propagated to server if UpdatingFromServerIsSet receiver[UpdatingFromServer] = true; // needed if the event caused ACL's to change such that the doc is otherwise no longer editable. receiver[prop] = curValue; + receiver[ForceServerWrite] = wasForce; receiver[UpdatingFromServer] = wasUpdate; }, prop: prop?.toString(), -- cgit v1.2.3-70-g09d2 From c83c44313b0d3e6168408fadd0c01d99057c325a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 24 Apr 2023 12:45:38 -0400 Subject: fixed pile view to be resizable. --- src/client/views/collections/CollectionPileView.tsx | 6 ++++-- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 3 ++- src/client/views/nodes/ImageBox.tsx | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index e5d89865b..5b96a8682 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -73,6 +73,9 @@ export class CollectionPileView extends CollectionSubView() { // toggles the pileup between starburst to compact toggleStarburst = action(() => { if (this.layoutEngine() === computeStarburstLayout.name) { + if (this.rootDoc[WidthSym]() !== NumCast(this.rootDoc._starburstDiameter, 500)) { + this.rootDoc._starburstDiameter = this.rootDoc[WidthSym](); + } const defaultSize = 110; this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; @@ -83,8 +86,7 @@ export class CollectionPileView extends CollectionSubView() { this.layoutDoc._panY = -10; this.props.Document._pileLayoutEngine = computePassLayout.name; } else { - const defaultSize = 500; - !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); + const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500); this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 2549ee0b3..c1f3c5aa6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -103,9 +103,10 @@ export function computePassLayout(poolData: Map, pivotDoc: Doc export function computeStarburstLayout(poolData: Map, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) { const docMap = new Map(); const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)]; + const burstScale = NumCast(pivotDoc._starburstDocScale, 1); childPairs.forEach(({ layout, data }, i) => { const aspect = layout[HeightSym]() / layout[WidthSym](); - const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect); + const docSize = Math.min(Math.min(400, layout[WidthSym]()), Math.min(400, layout[WidthSym]()) / aspect) * burstScale; const deg = (i / childPairs.length) * Math.PI * 2; docMap.set(layout[Id], { x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)), diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 20e5f5eca..c9be10d3a 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -407,7 +407,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent url) .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; - return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; + return paths.length ? paths : [Utils.CorsProxy('https://cs.brown.edu/~bcz/noImage.png')]; } @observable _isHovering = false; // flag to switch between primary and alternate images on hover -- cgit v1.2.3-70-g09d2 From 42afc0250de658fc3e924864bfae5afb4edec335 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 14 May 2023 12:03:40 -0400 Subject: major overhaul of field naming conventions. --- .../apis/google_docs/GooglePhotosClientUtils.ts | 88 +++--- src/client/documents/DocumentTypes.ts | 2 +- src/client/documents/Documents.ts | 324 ++++++++++----------- src/client/documents/Gitlike.ts | 26 +- src/client/util/CaptureManager.tsx | 2 +- src/client/util/CurrentUserUtils.ts | 179 ++++++------ src/client/util/DictationManager.ts | 2 +- src/client/util/DocumentManager.ts | 22 +- src/client/util/DragManager.ts | 12 +- src/client/util/DropConverter.ts | 4 +- src/client/util/GroupManager.tsx | 8 +- src/client/util/HypothesisUtils.ts | 2 +- src/client/util/Import & Export/ImageUtils.ts | 32 +- src/client/util/InteractionUtils.tsx | 1 + src/client/util/LinkFollower.ts | 34 +-- src/client/util/LinkManager.ts | 32 +- src/client/util/ReplayMovements.ts | 4 +- src/client/util/SearchUtil.ts | 94 +++--- src/client/util/SettingsManager.tsx | 4 +- src/client/util/SharingManager.tsx | 4 +- src/client/util/SnappingManager.ts | 4 +- src/client/util/TrackMovements.ts | 2 +- src/client/views/AudioWaveform.tsx | 124 ++++---- src/client/views/DashboardView.tsx | 18 +- src/client/views/DocComponent.tsx | 21 +- src/client/views/DocumentButtonBar.tsx | 22 +- src/client/views/DocumentDecorations.tsx | 70 ++--- src/client/views/FilterPanel.tsx | 6 +- src/client/views/GestureOverlay.tsx | 14 +- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/InkStrokeProperties.ts | 14 +- src/client/views/InkTranscription.tsx | 2 +- src/client/views/InkingStroke.tsx | 30 +- src/client/views/LightboxView.tsx | 44 +-- src/client/views/MainView.tsx | 32 +- src/client/views/MarqueeAnnotator.tsx | 12 +- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/PropertiesButtons.tsx | 36 +-- .../views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 10 +- src/client/views/PropertiesView.tsx | 78 ++--- src/client/views/SidebarAnnos.tsx | 20 +- src/client/views/StyleProvider.tsx | 38 +-- src/client/views/TemplateMenu.tsx | 4 +- src/client/views/animationtimeline/Timeline.tsx | 14 +- src/client/views/animationtimeline/Track.tsx | 261 +++++++++-------- .../views/collections/CollectionCarouselView.tsx | 14 +- .../views/collections/CollectionDockingView.tsx | 22 +- .../collections/CollectionMasonryViewFieldRow.tsx | 44 +-- src/client/views/collections/CollectionMenu.tsx | 85 +++--- .../views/collections/CollectionNoteTakingView.tsx | 78 ++--- .../collections/CollectionNoteTakingViewColumn.tsx | 23 +- .../views/collections/CollectionPileView.tsx | 6 +- .../collections/CollectionStackedTimeline.tsx | 39 +-- .../views/collections/CollectionStackingView.tsx | 82 +++--- .../CollectionStackingViewFieldColumn.tsx | 30 +- src/client/views/collections/CollectionSubView.tsx | 14 +- .../views/collections/CollectionTimeView.tsx | 8 +- .../views/collections/CollectionTreeView.tsx | 28 +- src/client/views/collections/CollectionView.tsx | 16 +- src/client/views/collections/TabDocView.tsx | 34 +-- src/client/views/collections/TreeView.tsx | 64 ++-- .../CollectionFreeFormLinkView.tsx | 62 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 186 ++++++------ .../collections/collectionFreeForm/MarqueeView.tsx | 32 +- .../collectionSchema/CollectionSchemaView.tsx | 34 +-- .../collections/collectionSchema/SchemaRowBox.tsx | 2 +- .../collectionSchema/SchemaTableCell.tsx | 4 +- src/client/views/linking/LinkMenuGroup.tsx | 21 +- src/client/views/linking/LinkMenuItem.tsx | 30 +- .../views/linking/LinkRelationshipSearch.tsx | 52 ++-- src/client/views/nodes/AudioBox.tsx | 37 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 16 +- src/client/views/nodes/ColorBox.tsx | 4 +- src/client/views/nodes/ComparisonBox.tsx | 28 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 18 +- .../nodes/DataVizBox/components/LineChart.tsx | 14 +- .../views/nodes/DataVizBox/components/TableBox.tsx | 12 +- src/client/views/nodes/DocumentContentsView.tsx | 12 +- src/client/views/nodes/DocumentLinksButton.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 145 ++++----- src/client/views/nodes/EquationBox.tsx | 2 +- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FunctionPlotBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 90 +++--- src/client/views/nodes/KeyValueBox.tsx | 32 +- src/client/views/nodes/KeyValuePair.scss | 4 + src/client/views/nodes/KeyValuePair.tsx | 6 +- src/client/views/nodes/LabelBox.tsx | 8 +- src/client/views/nodes/LinkAnchorBox.tsx | 16 +- src/client/views/nodes/LinkBox.tsx | 4 +- src/client/views/nodes/LinkDescriptionPopup.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 20 +- src/client/views/nodes/MapBox/MapBox.tsx | 42 +-- src/client/views/nodes/MapBox/MapBox2.tsx | 42 +-- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 6 +- src/client/views/nodes/PDFBox.tsx | 72 ++--- .../views/nodes/RecordingBox/RecordingBox.tsx | 2 +- src/client/views/nodes/ScreenshotBox.tsx | 10 +- src/client/views/nodes/ScriptingBox.tsx | 10 +- src/client/views/nodes/VideoBox.tsx | 102 +++---- src/client/views/nodes/WebBox.tsx | 123 ++++---- src/client/views/nodes/button/FontIconBox.tsx | 245 ++++++++-------- .../views/nodes/formattedText/DashDocView.tsx | 45 +-- .../views/nodes/formattedText/DashFieldView.tsx | 13 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 223 +++++++------- .../views/nodes/formattedText/RichTextMenu.tsx | 14 +- .../views/nodes/formattedText/RichTextRules.ts | 28 +- src/client/views/nodes/formattedText/nodes_rts.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 101 ++++--- src/client/views/nodes/trails/PresElementBox.tsx | 8 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 4 +- src/client/views/pdf/PDFViewer.tsx | 56 ++-- src/client/views/search/SearchBox.tsx | 44 +-- src/fields/Doc.ts | 117 ++++---- src/fields/RichTextUtils.ts | 6 +- src/fields/Types.ts | 5 +- src/fields/documentSchemas.ts | 52 ++-- src/fields/util.ts | 4 +- src/mobile/AudioUpload.tsx | 10 +- src/server/ApiManagers/SearchManager.ts | 151 +++++----- src/server/updateProtos.ts | 24 +- src/server/websocket.ts | 4 +- 123 files changed, 2424 insertions(+), 2391 deletions(-) (limited to 'src/client/views/nodes/ImageBox.tsx') diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index ff9460b62..e8fd8fb8a 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -1,27 +1,26 @@ -import { AssertionError } from "assert"; -import { EditorState } from "prosemirror-state"; -import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc"; -import { Id } from "../../../fields/FieldSymbols"; -import { RichTextField } from "../../../fields/RichTextField"; -import { RichTextUtils } from "../../../fields/RichTextUtils"; -import { Cast, StrCast } from "../../../fields/Types"; -import { ImageField } from "../../../fields/URLField"; -import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes"; -import { Utils } from "../../../Utils"; -import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; -import { Networking } from "../../Network"; -import { FormattedTextBox } from "../../views/nodes/formattedText/FormattedTextBox"; -import { GoogleAuthenticationManager } from "../GoogleAuthenticationManager"; +import { AssertionError } from 'assert'; +import { EditorState } from 'prosemirror-state'; +import { Doc, DocListCastAsync, Opt } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { RichTextField } from '../../../fields/RichTextField'; +import { RichTextUtils } from '../../../fields/RichTextUtils'; +import { Cast, StrCast } from '../../../fields/Types'; +import { ImageField } from '../../../fields/URLField'; +import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes'; +import { Utils } from '../../../Utils'; +import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents'; +import { Networking } from '../../Network'; +import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox'; +import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager'; import Photos = require('googlephotos'); export namespace GooglePhotos { - const endpoint = async () => new Photos(await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken()); export enum MediaType { ALL_MEDIA = 'ALL_MEDIA', PHOTO = 'PHOTO', - VIDEO = 'VIDEO' + VIDEO = 'VIDEO', } export type AlbumReference = { id: string } | { title: string }; @@ -58,11 +57,10 @@ export namespace GooglePhotos { HOUSES: 'HOUSES', GARDENS: 'GARDENS', FLOWERS: 'FLOWERS', - HOLIDAYS: 'HOLIDAYS' + HOLIDAYS: 'HOLIDAYS', }; export namespace Export { - export interface AlbumCreationResult { albumId: string; mediaItems: MediaItem[]; @@ -82,20 +80,20 @@ export namespace GooglePhotos { if (!images || !images.length) { return undefined; } - const resolved = title ? title : (StrCast(collection.title) || `Dash Collection (${collection[Id]}`); + const resolved = title ? title : StrCast(collection.title) || `Dash Collection (${collection[Id]}`; const { id, productUrl } = await Create.Album(resolved); const response = await Transactions.UploadImages(images, { id }, descriptionKey); if (response) { const { results, failed } = response; let index: Opt; while ((index = failed.pop()) !== undefined) { - Doc.RemoveDocFromList(dataDocument, "data", images.splice(index, 1)[0]); + Doc.RemoveDocFromList(dataDocument, 'data', images.splice(index, 1)[0]); } const mediaItems: MediaItem[] = results.map(item => item.mediaItem); if (mediaItems.length !== images.length) { throw new AssertionError({ actual: mediaItems.length, expected: images.length }); } - const idMapping = new Doc; + const idMapping = new Doc(); for (let i = 0; i < images.length; i++) { const image = Doc.GetProto(images[i]); const mediaItem = mediaItems[i]; @@ -117,38 +115,30 @@ export namespace GooglePhotos { return { albumId: id, mediaItems }; } }; - } export namespace Import { - export type CollectionConstructor = (data: Array, options: DocumentOptions, ...args: any) => Doc; export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt>): Promise => { await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); const response = await Query.ContentSearch(requested); const uploads = await Transactions.WriteMediaItemsToServer(response); - const children = uploads.map((upload: Transactions.UploadInformation) => { - const document = Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean)); - document.contentSize = upload.contentSize; - return document; - }); + const children = uploads.map((upload: Transactions.UploadInformation) => Docs.Create.ImageDocument(Utils.fileUrl(upload.fileNames.clean) /*, {"data_contentSize":upload.contentSize}*/)); const options = { _width: 500, _height: 500 }; return constructor(children, options); }; - } export namespace Query { - - const delimiter = ", "; - const comparator = (a: string, b: string) => (a < b) ? -1 : (a > b ? 1 : 0); + const delimiter = ', '; + const comparator = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0); export const TagChildImages = async (collection: Doc) => { await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); const idMapping = await Cast(collection.googlePhotosIdMapping, Doc); if (!idMapping) { - throw new Error("Appending image metadata requires that the targeted collection have already been mapped to an album!"); + throw new Error('Appending image metadata requires that the targeted collection have already been mapped to an album!'); } const tagMapping = new Map(); const images = (await DocListCastAsync(collection.data))!.map(Doc.GetProto); @@ -169,7 +159,7 @@ export namespace GooglePhotos { const concatenated = tagMapping.get(image[Id])!; const tags = concatenated.split(delimiter); if (tags.length > 1) { - const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, ""); + const cleaned = concatenated.replace(ContentCategories.NONE + delimiter, ''); image.googlePhotosTags = cleaned.split(delimiter).sort(comparator).join(delimiter); } else { image.googlePhotosTags = ContentCategories.NONE; @@ -253,19 +243,15 @@ export namespace GooglePhotos { export const GetImage = async (mediaItemId: string): Promise => { return (await endpoint()).mediaItems.get(mediaItemId); }; - } namespace Create { - export const Album = async (title: string) => { return (await endpoint()).albums.create(title); }; - } export namespace Transactions { - export interface UploadInformation { mediaPaths: string[]; fileNames: { [key: string]: string }; @@ -295,18 +281,20 @@ export namespace GooglePhotos { }; export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise => { - const uploads = await Networking.PostToServer("/googlePhotosMediaGet", body); + const uploads = await Networking.PostToServer('/googlePhotosMediaGet', body); return uploads; }; - export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption") => { + export const UploadThenFetch = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption') => { const response = await UploadImages(sources, album, descriptionKey); if (!response) { return undefined; } - const baseUrls: string[] = await Promise.all(response.results.map(item => { - return new Promise(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl))); - })); + const baseUrls: string[] = await Promise.all( + response.results.map(item => { + return new Promise(resolve => Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl))); + }) + ); return baseUrls; }; @@ -315,9 +303,9 @@ export namespace GooglePhotos { failed: number[]; } - export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise> => { + export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = 'caption'): Promise> => { await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); - if (album && "title" in album) { + if (album && 'title' in album) { album = await Create.Album(album.title); } const media: MediaInput[] = []; @@ -327,13 +315,13 @@ export namespace GooglePhotos { return; } const url = data.url.href; - const target = Doc.MakeAlias(source); + const target = Doc.MakeEmbedding(source); const description = parseDescription(target, descriptionKey); await DocUtils.makeCustomViewClicked(target, Docs.Create.FreeformDocument); media.push({ url, description }); } if (media.length) { - const results = await Networking.PostToServer("/googlePhotosMediaPost", { media, album }); + const results = await Networking.PostToServer('/googlePhotosMediaPost', { media, album }); return results; } }; @@ -341,14 +329,12 @@ export namespace GooglePhotos { const parseDescription = (document: Doc, descriptionKey: string) => { let description: string = Utils.prepend(`/doc/${document[Id]}?sharing=true`); const target = document[descriptionKey]; - if (typeof target === "string") { + if (typeof target === 'string') { description = target; } else if (target instanceof RichTextField) { description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data))); } return description; }; - } - -} \ No newline at end of file +} diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index d99cd2dac..2da3a24fd 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -11,7 +11,7 @@ export enum DocumentType { AUDIO = 'audio', REC = 'recording', PDF = 'pdf', - INK = 'inks', + INK = 'ink', SCREENSHOT = 'screenshot', FONTICON = 'fonticonbox', FILTER = 'filter', diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e28e29267..849a5d6ae 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,7 +37,6 @@ import { FontIconBox } from '../views/nodes/button/FontIconBox'; import { ColorBox } from '../views/nodes/ColorBox'; import { ComparisonBox } from '../views/nodes/ComparisonBox'; import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; -import { OpenWhereMod } from '../views/nodes/DocumentView'; import { EquationBox } from '../views/nodes/EquationBox'; import { FieldViewProps } from '../views/nodes/FieldView'; import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; @@ -120,7 +119,7 @@ class PEInfo extends FInfo { } class DAInfo extends FInfo { fieldType? = 'dropActionType'; - values? = ['alias', 'copy', 'move', 'same', 'proto', 'none']; + values? = ['embed', 'copy', 'move', 'same', 'proto', 'none']; readOnly = true; } class DateInfo extends FInfo { @@ -139,7 +138,7 @@ export class DocumentOptions { x?: NUMt = new NumInfo('x coordinate of document in a freeform view'); y?: NUMt = new NumInfo('y coordinage of document in a freeform view'); z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]); - system?: BOOLt = new BoolInfo('is this a system created/owned doc', true); + isSystem?: BOOLt = new BoolInfo('is this a system created/owned doc', true); type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType))); title?: string; creationDate?: DATEt = new DateInfo('date the document was created', true); @@ -150,33 +149,33 @@ export class DocumentOptions { userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)'); color?: STRt = new StrInfo('foreground color data doc'); backgroundColor?: STRt = new StrInfo('background color for data doc'); - _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); + _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title'); _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes'); _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume'); - _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view'); - _panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); + _freeform_panX?: NUMt = new NumInfo('horizontal pan location of a freeform view'); + _freeform_panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); _width?: NUMt = new NumInfo('displayed width of a document'); _height?: NUMt = new NumInfo('displayed height of document'); - 'data-nativeWidth'?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)'); - 'data-nativeHeight'?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)'); + data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)'); + data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)'); _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)'); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)'); _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers'); _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers'); _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); - _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); - _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); + _layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); + _layoutFitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); _contentBounds?: List; // the (forced) bounds of the document to display. format is: [left, top, right, bottom] _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged - _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed + _lockedTransform?: boolean; // lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target - _showTitle?: string; // field name to display in header (:hover is an optional suffix) - _showAltContentUI?: boolean; // whether to show alternate content button + _layout_showTitle?: string; // field name to display in header (:hover is an optional suffix) + _layout_altContentUI?: boolean; // whether to show alternate content button _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target - _showCaption?: string; // which field to display in the caption area. leave empty to have no caption - _scrollTop?: number; // scroll location for pdfs + _layout_showCaption?: string; // which field to display in the caption area. leave empty to have no caption + _layoutScrollTop?: number; // scroll location for pdfs _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged _chromeHidden?: boolean; // whether the editing chrome for a document is hidden _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view) @@ -188,15 +187,16 @@ export class DocumentOptions { _viewType?: string; // sub type of a collection viewType?: string; // sub type of a collection _gridGap?: number; // gap between items in masonry view - _viewScale?: number; // how much a freeform view has been scaled (zoomed) + _freeform_scale?: number; // how much a freeform view has been scaled (zoomed) _overflow?: string; // set overflow behavior _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts _xPadding?: number; _yPadding?: number; _itemIndex?: number; // which item index the carousel viewer is showing - _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts - _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document) + _layout_showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts + _singleLine?: boolean; // whether label box is restricted to one line of text + _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents _minFontSize?: number; // minimum font size for labelBoxes _maxFontSize?: number; // maximum font size for labelBoxes _columnWidth?: number; @@ -205,24 +205,24 @@ export class DocumentOptions { _fontFamily?: string; _fontWeight?: string; _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views - _curPage?: number; // current page of a PDF or other? paginated document - _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds + _layout_curPage?: number; // current page of a PDF or other? paginated document + _layout_currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide) _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays) _timecodeToHide?: number; // the time that a document should be hidden _timelineLabel?: boolean; // whether the document exists on a timeline - '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection', true); - '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection', true); - 'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view', true); - 'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view', true); - 'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); + _caption_xMargin?: NUMt = new NumInfo('x margin of caption inside of a carousel collection', true); + _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', true); + icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', true); + icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', true); + dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true); openFactoryLocation?: string; // an OpenWhere value to place the factory created document openFactoryAsDelegate?: boolean; // lat?: number; lng?: number; infoWindowOpen?: boolean; author?: string; - _layoutKey?: string; + _layout_fieldKey?: string; fieldValues?: List; // possible field values used by fieldInfos fieldType?: string; // type of afield used by fieldInfos unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf) @@ -249,7 +249,6 @@ export class DocumentOptions { defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) - description?: string; // added for links layout?: string | Doc; // default layout string for a document contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width @@ -263,11 +262,11 @@ export class DocumentOptions { childContextMenuLabels?: List; childContextMenuIcons?: List; followLinkZoom?: boolean; // whether to zoom to the target of a link - hideLinkButton?: boolean; // whether the blue link counter button should be hidden + layout_hideLinkButton?: boolean; // whether the blue link counter button should be hidden disableDocBrushing?: boolean; // whether to suppress border highlighting - hideDecorationTitle?: boolean; + layout_hideDecorationTitle?: boolean; hideOpenButton?: boolean; - hideResizeHandles?: boolean; + layout_hideResizeHandles?: boolean; hideDocumentButtonBar?: boolean; hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template @@ -278,8 +277,8 @@ export class DocumentOptions { caption?: RichTextField; opacity?: number; defaultBackgroundColor?: string; - _linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint - hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed + _layout_autoMoveAnchors?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint + layout_hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed isFolder?: boolean; lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide) activeFrame?: number; // the active frame of a document in a frame base collection @@ -294,12 +293,11 @@ export class DocumentOptions { borderRounding?: string; boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow data?: any; - baseProto?: boolean; // is this a base prototoype + isBaseProto?: boolean; // this Doc is base level prototype for data documents as opposed to data documents which are prototypes for layout documents. base protos are not cloned during a deep dontRegisterView?: boolean; lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document. see PresBox. columnHeaders?: List; // headers for stacking views schemaHeaders?: List; // headers for schema view - clipWidth?: number; // percent transition from before to after in comparisonBox dockingConfig?: string; annotationOn?: Doc; followLinkToggle?: boolean; @@ -307,7 +305,7 @@ export class DocumentOptions { _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document noteType?: string; // BACKGROUND GRID - _backgroundGridShow?: boolean; + _freeform_backgroundGrid?: boolean; //BUTTONS buttonText?: string; @@ -318,7 +316,6 @@ export class DocumentOptions { userColorBtn?: string; canClick?: string; script?: ScriptField; - numBtnType?: string; numBtnMax?: number; numBtnMin?: number; switchToggle?: boolean; @@ -335,12 +332,14 @@ export class DocumentOptions { layout_linkView?: Doc; // view template for a link document layout_keyValue?: string; // view tempalte for key value docs - linkRelationship?: string; // type of relatinoship a link represents - linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors - anchor1?: Doc; - anchor2?: Doc; - 'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) - 'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) + link_description?: string; // added for links + link_relationship?: string; // type of relatinoship a link represents + layout_linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors + layout_linkDisplayArrow?: boolean; // whether to display link's directional arrowhead + link_anchor_1?: Doc; + link_anchor_2?: Doc; + link_anchor_1_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection) + link_anchor_2_useLinkSmallAnchor?: boolean; // whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection) ignoreClick?: boolean; onClick?: ScriptField; onDoubleClick?: ScriptField; @@ -354,13 +353,13 @@ export class DocumentOptions { onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; filterBoolean?: string; - useCors?: boolean; + data_useCors?: boolean; icon?: string; target?: Doc; // available for use in scripts as the primary target document sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script searchFileTypes?: List; // file types allowed in a search query - strokeWidth?: number; + stroke_width?: number; freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'unrendered' tag unless they also have a treeViewFieldKey tag (presBox) @@ -395,8 +394,8 @@ export class DocumentOptions { useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered - linkRelationshipList?: List; // for storing different link relationships (when set by user in the link editor) - linkRelationshipSizes?: List; //stores number of links contained in each relationship + link_relationshipList?: List; // for storing different link relationships (when set by user in the link editor) + link_relationshipSizes?: List; //stores number of links contained in each relationship linkColorList?: List; // colors of links corresponding to specific link relationships } export namespace Docs { @@ -427,7 +426,7 @@ export namespace Docs { _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, - forceReflow: true, + layout_forceReflow: true, defaultDoubleClick: 'ignore', }, }, @@ -457,35 +456,35 @@ export namespace Docs { DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' }, + options: { _height: 300, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' }, }, ], [ DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 }, + options: { _layout_fitWidth: true, _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1 }, }, ], [ DocumentType.KVP, { layout: { view: KeyValueBox, dataField: defaultDataKey }, - options: { _fitWidth: true, _height: 150 }, + options: { _layout_fitWidth: true, _height: 150 }, }, ], [ DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0 }, + options: { _layout_currentTimecode: 0 }, }, ], [ DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true }, + options: { _height: 100, layout_fitWidth: true, layout_forceReflow: true, nativeDimModifiable: true }, }, ], [ @@ -499,7 +498,7 @@ export namespace Docs { DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, + options: { _layout_curPage: 1, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, }, ], [ @@ -519,14 +518,15 @@ export namespace Docs { [ DocumentType.LINK, { - layout: { view: LinkBox, dataField: defaultDataKey }, + layout: { view: LinkBox, dataField: 'link' }, options: { childDontRegisterViews: true, onClick: FollowLinkScript(), - hideLinkAnchors: true, + layout_hideLinkAnchors: true, _height: 150, - description: '', - showCaption: 'description', + link: '', + link_description: '', + layout_showCaption: 'link_description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area _removeDropProperties: new List(['onClick']), }, @@ -537,7 +537,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'alias', title: 'Global Link Database' }, + options: { childDropAction: 'embed', title: 'Global Link Database' }, }, ], [ @@ -545,7 +545,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'alias', title: 'Global Script Database' }, + options: { childDropAction: 'embed', title: 'Global Script Database' }, }, ], [ @@ -571,8 +571,8 @@ export namespace Docs { [ DocumentType.EQUATION, { - layout: { view: EquationBox, dataField: defaultDataKey }, - options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true }, + layout: { view: EquationBox, dataField: 'text' }, + options: { nativeDimModifiable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true }, }, ], [ @@ -600,14 +600,14 @@ export namespace Docs { DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { defaultDoubleClick: 'ignore', hideLinkAnchors: true }, + options: { defaultDoubleClick: 'ignore', layout_hideLinkAnchors: true }, }, ], [ DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: 'icon' }, - options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, hideLinkButton: true, _width: 40, _height: 40 }, + options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, }, ], [ @@ -621,21 +621,21 @@ export namespace Docs { DocumentType.PRESELEMENT, { layout: { view: PresElementBox, dataField: defaultDataKey }, - options: { title: 'pres element template', _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, + options: { title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, }, ], [ DocumentType.MARKER, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { hideLinkButton: true, pointerEvents: 'none' }, + options: { layout_hideLinkButton: true, pointerEvents: 'none' }, }, ], [ DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method - layout: { view: InkingStroke, dataField: defaultDataKey }, + layout: { view: InkingStroke, dataField: 'stroke' }, options: {}, }, ], @@ -649,8 +649,9 @@ export namespace Docs { [ DocumentType.COMPARISON, { + data: '', layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' }, + options: { nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'embed' }, }, ], [ @@ -658,7 +659,7 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: 'alias', title: 'Global Group Database' }, + options: { childDropAction: 'embed', title: 'Global Group Database' }, }, ], [ @@ -672,14 +673,14 @@ export namespace Docs { DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, nativeDimModifiable: true }, + options: { _layout_fitWidth: true, nativeDimModifiable: true }, }, ], [ DocumentType.LOADING, { layout: { view: LoadingBox, dataField: '' }, - options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true }, + options: { _layout_fitWidth: true, _fitHeight: true, nativeDimModifiable: true }, }, ], ]); @@ -773,11 +774,11 @@ export namespace Docs { // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype const options: DocumentOptions = { - system: true, - _layoutKey: 'layout', + isSystem: true, + _layout_fieldKey: 'layout', title, type, - baseProto: true, + isBaseProto: true, x: 0, y: 0, _width: 300, @@ -818,24 +819,24 @@ export namespace Docs { * main document. */ function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) { - const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_" + const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); // dataProps['acl-Override'] = SharingPermissions.Unset; dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; - dataProps.system = viewProps.system; - dataProps.isPrototype = true; + dataProps.isSystem = viewProps.isSystem; + dataProps.isDataDoc = true; dataProps.author = Doc.CurrentUserEmail; dataProps.creationDate = new DateField(); if (fieldKey) { - dataProps[`${fieldKey}-lastModified`] = new DateField(); + dataProps[`${fieldKey}_modificationDate`] = new DateField(); dataProps[fieldKey] = data; // so that the list of annotations is already initialised, prevents issues in addonly. // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do. - dataProps[fieldKey + '-annotations'] = new List(); - dataProps[fieldKey + '-sidebar'] = new List(); + dataProps[fieldKey + '_annotations'] = new List(); + dataProps[fieldKey + '_sidebar'] = new List(); } // users placeholderDoc as proto if it exists @@ -901,7 +902,7 @@ export namespace Docs { } export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) { - return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options); + return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options); } export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { @@ -954,11 +955,12 @@ export namespace Docs { Prototypes.get(DocumentType.LINK), undefined, { - anchor1: source, - anchor2: target, + link_anchor_1: source, + link_anchor_2: target, ...options, }, - id + id, + 'link' ); LinkManager.Instance.addLink(linkDoc); @@ -966,36 +968,24 @@ export namespace Docs { return linkDoc; } - export function InkDocument( - color: string, - tool: string, - strokeWidth: number, - strokeBezier: string, - fillColor: string, - arrowStart: string, - arrowEnd: string, - dash: string, - points: PointData[], - isInkMask: boolean, - options: DocumentOptions = {} - ) { + export function InkDocument(color: string, strokeWidth: number, stroke_bezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], isInkMask: boolean, options: DocumentOptions = {}) { const I = new Doc(); I[Initializing] = true; - I.isInkMask = isInkMask; I.type = DocumentType.INK; - I.layout = InkingStroke.LayoutString('data'); - I.color = color; - I.hideDecorationTitle = true; // don't show title when selected + I.layout = InkingStroke.LayoutString('stroke'); + I.layout_fitWidth = true; + I.layout_hideDecorationTitle = true; // don't show title when selected // I.hideOpenButton = true; // don't show open full screen button when selected + I.color = color; I.fillColor = fillColor; - I.strokeWidth = strokeWidth; - I.strokeBezier = strokeBezier; - I.strokeStartMarker = arrowStart; - I.strokeEndMarker = arrowEnd; - I.strokeDash = dash; - I.tool = tool; - I.fitWidth = true; - I['text-align'] = 'center'; + I.stroke = new InkField(points); + I.stroke_width = strokeWidth; + I.stroke_bezier = stroke_bezier; + I.stroke_startMarker = arrowStart; + I.stroke_endMarker = arrowEnd; + I.stroke_dash = dash; + I.stroke_isInkMask = isInkMask; + I.text_align = 'center'; I.title = 'ink'; I.x = options.x as number; I.y = options.y as number; @@ -1004,7 +994,6 @@ export namespace Docs { I._fontFamily = 'cursive'; I.author = Doc.CurrentUserEmail; I.rotation = 0; - I.data = new InkField(points); I.defaultDoubleClick = 'click'; I.creationDate = new DateField(); I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; @@ -1050,7 +1039,7 @@ export namespace Docs { export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id); - documents.map(d => (d.context = inst)); + documents.map(d => (d.embedContainer = inst)); return inst; } @@ -1136,7 +1125,7 @@ export namespace Docs { } export function EquationDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) }); + return InstanceFromProto(Prototypes.get(DocumentType.EQUATION), undefined, { ...(options || {}) }, undefined, 'text'); } export function FunctionPlotDocument(documents: Array, options?: DocumentOptions) { @@ -1263,7 +1252,7 @@ export namespace DocUtils { ? true : matches.some(value => { if (facetKey.startsWith('*')) { - // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc + // fields starting with a '*' are used to match families of related fields. ie, *modificationDate will match text_modificationDate, data_modificationDate, etc const allKeys = Array.from(Object.keys(d)); allKeys.push(...Object.keys(Doc.GetProto(d))); const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); @@ -1304,12 +1293,12 @@ export namespace DocUtils { broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' }); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { layout_linkDisplay: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); }); } - export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, showPopup?: number[]) { - if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; layout_linkDisplay?: boolean }, id?: string, showPopup?: number[]) { + if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; const sv = DocumentManager.Instance.getDocumentView(source); if (target.doc === Doc.UserDoc()) return undefined; @@ -1350,16 +1339,16 @@ export namespace DocUtils { target, { title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, - 'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined, - 'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined, + link_anchor_1_useLinkSmallAnchor: source.useLinkSmallAnchor ? true : undefined, + link_anchor_2_useLinkSmallAnchor: target.useLinkSmallAnchor ? true : undefined, 'acl-Public': SharingPermissions.Augment, '_acl-Public': SharingPermissions.Augment, - linkDisplay: linkSettings.linkDisplay, - _linkAutoMove: true, - linkRelationship: linkSettings.linkRelationship, - _showCaption: 'description', - _showTitle: 'linkRelationship', - description: linkSettings.description, + layout_linkDisplay: linkSettings.layout_linkDisplay, + link_relationship: linkSettings.link_relationship, + link_description: linkSettings.link_description, + _layout_autoMoveAnchors: true, + _layout_showCaption: 'link_description', + _layout_showTitle: 'link_relationship', }, id ), @@ -1443,13 +1432,13 @@ export namespace DocUtils { created = Docs.Create.AudioDocument(field.url.href, resolved); created.layout = AudioBox.LayoutString(fieldKey); } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved); created.layout = InkingStroke.LayoutString(fieldKey); } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); created.layout = CollectionView.LayoutString(fieldKey); } else { - created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved }); + created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved }); created.layout = FormattedTextBox.LayoutString(fieldKey); } if (created) { @@ -1503,12 +1492,12 @@ export namespace DocUtils { const id = s[s.length - 1]; return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { - const alias = Doc.MakeAlias(field); - alias.x = (options.x as number) || 0; - alias.y = (options.y as number) || 0; - alias._width = (options._width as number) || 300; - alias._height = (options._height as number) || (options._width as number) || 300; - return alias; + const embedding = Doc.MakeEmbedding(field); + embedding.x = (options.x as number) || 0; + embedding.y = (options.y as number) || 0; + embedding._width = (options._width as number) || 300; + embedding._height = (options._height as number) || (options._width as number) || 300; + return embedding; } return undefined; }); @@ -1531,11 +1520,11 @@ export namespace DocUtils { _width: 200, x, y, - _autoHeight: note._autoHeight !== false, - title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1), + _layout_autoHeight: note._layout_autoHeight !== false, + title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1), }); - textDoc.layoutKey = 'layout_' + note.title; - textDoc[textDoc.layoutKey] = note; + textDoc.layout_fieldKey = 'layout_' + note.title; + textDoc[textDoc.layout_fieldKey] = note; if (pivotField) { textDoc[pivotField] = pivotValue; } @@ -1576,7 +1565,7 @@ export namespace DocUtils { export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { const batch = UndoManager.StartBatch('makeCustomViewClicked'); runInAction(() => { - doc.layoutKey = 'layout_' + templateSignature; + doc.layout_fieldKey = 'layout_' + templateSignature; createCustomView(doc, creator, templateSignature, docLayoutTemplate); }); batch.end(); @@ -1607,7 +1596,7 @@ export namespace DocUtils { const customName = 'layout_' + templateSignature; const _width = NumCast(doc._width); const _height = NumCast(doc._height); - const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false }; + const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false }; if (docLayoutTemplate) { if (docLayoutTemplate !== doc[customName]) { @@ -1638,9 +1627,9 @@ export namespace DocUtils { } } export function iconify(doc: Doc) { - const layoutKey = Cast(doc.layoutKey, 'string', null); + const layout_fieldKey = Cast(doc.layout_fieldKey, 'string', null); DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); - if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', ''); + if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') doc.deiconifyLayout = layout_fieldKey.replace('layout_', ''); } export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { @@ -1680,8 +1669,8 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; - const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = LinkManager.Links(doc).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); + const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); + const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: 'pushpin', @@ -1700,7 +1689,7 @@ export namespace DocUtils { _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); - const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, ''); + const pushpinLink = DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); doc._timecodeToShow = undefined; return pushpin; } @@ -1745,21 +1734,17 @@ export namespace DocUtils { if (doc) { const proto = Doc.GetProto(doc); proto.text = result.rawText; - proto.fileUpload = pathname - .replace(/.*\//, '') - .replace('upload_', '') - .replace(/\.[a-z0-9]*$/, ''); if (Upload.isImageInformation(result)) { const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); - proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); if (NumCast(proto['data-nativeOrientation']) >= 5) { - proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); } - proto.contentSize = result.contentSize; + proto.data_contentSize = result.contentSize; // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates const latitude = result.exifData?.data?.GPSLatitude; const latitudeDirection = result.exifData?.data?.GPSLatitudeRef; @@ -1771,12 +1756,12 @@ export namespace DocUtils { } } if (Upload.isVideoInformation(result)) { - proto['data-duration'] = result.duration; + proto.data_duration = result.duration; } if (overwriteDoc) { Doc.removeCurrentlyLoading(overwriteDoc); // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view. - DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layoutKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); + DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())); } generatedDocuments.push(doc); } @@ -1793,16 +1778,16 @@ export namespace DocUtils { _height: 35, x: x, y: y, - _fitWidth: true, - _autoHeight: true, - _showAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), + _layout_fitWidth: true, + _layout_autoHeight: true, + _layout_altContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards), title, }); const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { tbox._width = NumCast(template._width); - tbox.layoutKey = 'layout_' + StrCast(template.title); - Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template; + tbox.layout_fieldKey = 'layout_' + StrCast(template.title); + Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; } return tbox; } @@ -1857,9 +1842,9 @@ export namespace DocUtils { if (!dragFactory) return undefined; const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc)); - if (ndoc && dragFactory['dragFactory-count'] !== undefined) { - dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1; - Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true); + if (ndoc && dragFactory['dragFactory_count'] !== undefined) { + dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1; + Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true); } if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc); @@ -1868,9 +1853,9 @@ export namespace DocUtils { } export function delegateDragFactory(dragFactory: Doc) { const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory['dragFactory-count'] !== undefined) { - dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1; - Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(); + if (ndoc && dragFactory['dragFactory_count'] !== undefined) { + dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1; + Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(); } return ndoc; } @@ -1885,11 +1870,8 @@ ScriptingGlobals.add(function makeDelegate(proto: any) { return d; }); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { - const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null)?.title : ''; - const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null)?.title : ''; - const relation = self.linkRelationship || 'to'; - return `${anchor1title} (${relation}) ${anchor2title}`; -}); -ScriptingGlobals.add(function openTabAlias(tab: Doc) { - CollectionDockingView.AddSplit(Doc.MakeAlias(tab), OpenWhereMod.right); + const link_anchor_1title = self.link_anchor_1 && self.link_anchor_1 !== self ? Cast(self.link_anchor_1, Doc, null)?.title : ''; + const link_anchor_2title = self.link_anchor_2 && self.link_anchor_2 !== self ? Cast(self.link_anchor_2, Doc, null)?.title : ''; + const relation = self.link_relationship || 'to'; + return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`; }); diff --git a/src/client/documents/Gitlike.ts b/src/client/documents/Gitlike.ts index 5e2baf924..f35f33f65 100644 --- a/src/client/documents/Gitlike.ts +++ b/src/client/documents/Gitlike.ts @@ -9,24 +9,24 @@ // // we don't have individual timestamps for all fields -- this is a problematic design issue. // function GitlikeSynchDocs(bd: Doc, md: Doc) { // const fieldKey = Doc.LayoutFieldKey(md); -// const bdate = DateCast(bd[`${fieldKey}-lastModified`])?.date; -// const mdate = DateCast(md[`${fieldKey}-lastModified`])?.date; +// const bdate = DateCast(bd[`${fieldKey}_modificationDate`])?.date; +// const mdate = DateCast(md[`${fieldKey}_modificationDate`])?.date; // const bdproto = bd && Doc.GetProto(bd); // if (bdate !== mdate && bdate <= mdate) { // if (bdproto && md) { // bdproto[fieldKey] = Field.Copy(md[fieldKey]); -// bdproto[`${fieldKey}-lastModified`] = new DateField(); +// bdproto[`${fieldKey}_modificationDate`] = new DateField(); // } // } -// const bldate = DateCast(bd._lastModified)?.date; -// const mldate = DateCast(md._lastModified)?.date; +// const bldate = DateCast(bd._layout_modificationDate)?.date; +// const mldate = DateCast(md._layout_modificationDate)?.date; // if (bldate === mldate || bldate > mldate) return; // if (bdproto && md) { // bd.x = Field.Copy(md.x); // bd.y = Field.Copy(md.y); // bd.width = Field.Copy(md.width); // bd.height = Field.Copy(md.height); -// bdproto._lastModified = new DateField(); +// bdproto._layout_modificationDate = new DateField(); // } // } @@ -53,10 +53,10 @@ // const newlyBranchedDocs = await Promise.all(newDocsFromMaster?.map(async md => (await Doc.MakeClone(md, false, true, cloneMap)).clone) || []); // newlyBranchedDocs.forEach(nd => { // Doc.AddDocToList(branch, Doc.LayoutFieldKey(branch) + suffix, nd); -// nd.context = branch; +// nd.embedContainer = branch; // }); -// // if a branch doc's corresponding main branch doc doesn't have a context, then it was deleted. -// const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.context); +// // if a branch doc's corresponding main branch doc doesn't have a embedContainer, then it was deleted. +// const remDocsFromMaster = branchMainDocs?.filter(bd => Cast(bd.branchOf, Doc, null) && !Cast(bd.branchOf, Doc, null)?.embedContainer); // // so then remove all the deleted main docs from this branch. // remDocsFromMaster?.forEach(rd => Doc.RemoveDocFromList(branch, Doc.LayoutFieldKey(branch) + suffix, rd)); // } @@ -87,14 +87,14 @@ // branchDocProto.branchOf = masterChild; // the branch child is now a branch of the master child // } // Doc.AddDocToList(master, Doc.LayoutFieldKey(master) + suffix, masterChild); // add the masterChild to master (if it's already there, this is a no-op) -// masterChild.context = master; +// masterChild.embedContainer = master; // GitlikeSynchDocs(masterChild, bd);//Doc.GetProto(masterChild), bd); // })); // const masterChildren = await DocListCastAsync(master[Doc.LayoutFieldKey(master) + suffix]); // masterChildren?.forEach(mc => { // see if any master children // if (!branchChildren?.find(bc => bc.branchOf === mc)) { // are not in the list of children for this branch. // Doc.RemoveDocFromList(master, Doc.LayoutFieldKey(master) + suffix, mc); // if so, delete the master child since the branch has deleted it. -// mc.context = undefined; // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp +// mc.embedContainer = undefined; // NOTE if we merge a branch that didn't do a pull, it will look like the branch deleted documents -- need edit timestamps that prevent merging if branch isn't up-to-date with last edit timestamp // } // }); // }); @@ -109,8 +109,8 @@ // export async function BranchTask(target: Doc, action: "pull" | "merge") { // const func = action === "pull" ? GitlikePullFromMaster : GitlikeMergeWithMaster; // await func(target, ""); -// await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-annotations")); -// await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "-sidebar")); +// await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_annotations")); +// await DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(async targetChild => func(targetChild, "_sidebar")); // } // export async function BranchCreate(target: Doc) { diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index c9fcc84a3..d68761ba7 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -53,7 +53,7 @@ export class CaptureManager extends React.Component<{}> { order.push(
{i}
- {StrCast(DocCast(l.anchor1)?.title)} + {StrCast(DocCast(l.link_anchor_1)?.title)}
) ); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index fbfe306a4..5cfd77937 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -20,7 +20,7 @@ import { TreeViewType } from "../views/collections/CollectionTreeView"; import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; -import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; +import { ButtonType } from "../views/nodes/button/FontIconBox"; import { OpenWhere } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; import { DragManager } from "./DragManager"; @@ -37,7 +37,6 @@ interface Button { toolTip?: string; icon?: string; btnType?: ButtonType; - numBtnType?: NumButtonType; numBtnMin?: number; numBtnMax?: number; switchToggle?: boolean; @@ -65,11 +64,11 @@ export class CurrentUserUtils { const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [ { btnOpts: { title: "slide", icon: "address-card" }, - templateOpts: { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }, + templateOpts: { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, isSystem: true }, template: (opts:DocumentOptions) => Docs.Create.MultirowDocument( [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _fitWidth:true, _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, isSystem: true }), + Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) ], opts) }, { @@ -97,9 +96,9 @@ export class CurrentUserUtils { }); const reqdOpts:DocumentOptions = { - title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true, - _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, + _stayInCollection: true, _hideContextMenu: true, _forceActive: true, isSystem: true, + _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; const reqdFuncs = { hidden: "IsNoviceMode()" }; @@ -109,7 +108,7 @@ export class CurrentUserUtils { /// Initializes templates for editing click funcs of a document static setupChildClickEditors(doc: Doc, field = "clickFuncs-child") { const tempClicks = DocCast(doc[field]); - const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, system: true}; + const reqdClickOpts:DocumentOptions = {_width: 300, _height:200, isSystem: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ { opts: { title: "Open In Target", targetScriptKey: "onChildClick"}, script: "docCast(documentView?.props.docViewPath().lastElement()?.rootDoc.target).then((target) => target && (target.proto.data = new List([self])))"}, { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(self.doubleClickView.${OpenWhere.addRight})`}]; @@ -119,14 +118,14 @@ export class CurrentUserUtils { return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts); }); - const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, system: true}; + const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, isSystem: true}; return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts)); } /// Initializes templates for editing click funcs of a document static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") { const tempClicks = DocCast(doc[field]); - const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, system: true}; + const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true}; const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [ { opts: { title: "onClick"}, script: "console.log('click')"}, { opts: { title: "onDoubleClick"}, script: "console.log('click')"}, @@ -140,7 +139,7 @@ export class CurrentUserUtils { return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title); }); - const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, system: true}; + const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true}; return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts)); } @@ -152,12 +151,12 @@ export class CurrentUserUtils { { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" }, { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; const reqdNoteList = reqdTempOpts.map(opts => { - const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true}; + const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true}; const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined; return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note"); }); - const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true }; + const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true }; return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts)); } @@ -169,14 +168,14 @@ export class CurrentUserUtils { CurrentUserUtils.setupClickEditorTemplates(doc) ]; CurrentUserUtils.setupChildClickEditors(doc) - const reqdOpts = { title: "template layouts", _xMargin: 0, system: true, }; + const reqdOpts = { title: "template layouts", _xMargin: 0, isSystem: true, }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts); } // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc, field="template-icons") { - const reqdOpts = { title: "icon templates", _height: 75, system: true }; + const reqdOpts = { title: "icon templates", _height: 75, isSystem: true }; const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => { @@ -187,7 +186,7 @@ export class CurrentUserUtils { case DocumentType.IMG : creator = imageBox; break; case DocumentType.FONTICON: creator = fontBox; break; } - const allopts = {system: true, onClickScriptDisable: "never", ...opts}; + const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts}; return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", }); @@ -195,14 +194,14 @@ export class CurrentUserUtils { const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); - const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); + const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts }); const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}), makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}), makeIconTemplate(DocumentType.PDF, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}), makeIconTemplate(DocumentType.WEB, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}), - makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}), + makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _layout_showTitle: "creationDate"}), makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}), makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), @@ -220,7 +219,7 @@ export class CurrentUserUtils { title: string, toolTip: string, icon: string, ignoreClick?: boolean, dragFactory?: Doc, backgroundColor?: string, openFactoryAsDelegate?:boolean, openFactoryLocation?:string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string}, }[] { - const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: false, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); + const standardOps = (key:string) => ({ title : "Untitled "+ key, _layout_fitWidth: false, isSystem: true, "dragFactory_count": 0, cloneFieldFilter: new List(["isSystem"]) }); const json = { doc: { type: "doc", @@ -247,7 +246,7 @@ export class CurrentUserUtils { "" + ` ` + " " + - ` Metadata` + + ` Metadata` + "" }, "header"); @@ -263,27 +262,27 @@ export class CurrentUserUtils { funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist }[] = [ - {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true }}, - {key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true, _showAltContentUI: true}}, - {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, - {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _fitWidth: true}}, - {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100, _fitWidth: true }}, - {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, + {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true }}, + {key: "Flashcard", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true, _layout_altContentUI: true}}, + {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, }}, + {key: "Noteboard", creator: opts => Docs.Create.NoteTakingDocument([], opts), opts: { _width: 250, _height: 200, _layout_fitWidth: true}}, + {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100, _layout_fitWidth: true }}, + {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, data_useCors: true, }}, {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, - {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _fitWidth: true, _showSidebar: true, }}, + {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _layout_fitWidth: true, _layout_showSidebar: true, }}, {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, - {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, + {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, isSystem: true, cloneFieldFilter: new List(["isSystem"]) }}, {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, onClick: FollowLinkScript()}}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }}, - {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}}, - {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }}, - {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _fitWidth: true, _backgroundGridShow: true, }}, + {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}}, + {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "embed" as any, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }}, + {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, - treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, + treeViewHasOverlay: true, _fontSize: "20px", _layout_autoHeight: true, allowOverlayDrop: true, treeViewType: TreeViewType.outline, - backgroundColor: "white", _xMargin: 0, _yMargin: 0, _singleLine: true + backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true }, funcs: {title: 'self.text?.Text'}}, ]; @@ -320,16 +319,16 @@ export class CurrentUserUtils { const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, - btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true, + btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true, _removeDropProperties: new List(["_stayInCollection"]), }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs); }); const reqdOpts:DocumentOptions = { - title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, - childDropAction: 'alias' + title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, isSystem: true, + _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, + childDropAction: 'embed' }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts); @@ -354,7 +353,7 @@ export class CurrentUserUtils { /// the empty panel that is filled with whichever left menu button's panel has been selected static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") { - DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true}); + DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true}); } /// Initializes the left sidebar menu buttons and the panels they open up @@ -364,8 +363,8 @@ export class CurrentUserUtils { const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => { const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { - title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true, dontRegisterView: true, - _width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true, + _width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, _removeDropProperties: new List(["_stayInCollection"]), }; return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs); @@ -373,14 +372,14 @@ export class CurrentUserUtils { const reqdStackOpts:DocumentOptions ={ title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, - _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true, + _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" }); } // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") { - const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,}; + const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, isSystem: true, _chromeHidden: true,}; DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts); } @@ -401,9 +400,9 @@ export class CurrentUserUtils { title: data.title, _lockedPosition: true, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, - backgroundColor: data.backgroundColor, system: true + backgroundColor: data.backgroundColor, isSystem: true }, - [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) + [this.createToolButton({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", isSystem: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) ); } @@ -411,51 +410,51 @@ export class CurrentUserUtils { static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { ...opts, _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, - borderRounding: "5px", boxShadow: "0 0", system: true + borderRounding: "5px", boxShadow: "0 0", isSystem: true }) as any as Doc // sets up the text container for the information contained within the mobile button static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { ...opts, _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, - backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true + backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, isSystem: true }) as any as Doc // Sets up the title of the button static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { ...opts, - title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true + title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", isSystem: true }) as any as Doc // Sets up the description of the button static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { ...opts, - title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true + title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, isSystem: true }) as any as Doc static setupMobileInkingDoc(userDoc: Doc) { - return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true }); + return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", isSystem: true }); } static setupMobileUploadDoc(userDoc: Doc) { // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" }) const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", { - title: "Upload Images From the Web", _lockedPosition: true, system: true + title: "Upload Images From the Web", _lockedPosition: true, isSystem: true }); const uploadDoc = Docs.Create.StackingDocument([], { - title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, system: true, _chromeHidden: true, + title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, isSystem: true, _chromeHidden: true, }); return Docs.Create.StackingDocument([webDoc, uploadDoc], { - _width: screen.width, _lockedPosition: true, title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true, _chromeHidden: true, + _width: screen.width, _lockedPosition: true, title: "Upload", _layout_autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", isSystem: true, _chromeHidden: true, }); } /// Search option on the left side button panel static setupSearcher(doc: Doc, field:string) { return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), { - dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", + dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, }); } @@ -465,8 +464,8 @@ export class CurrentUserUtils { const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined); const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined); const reqdToolOps:DocumentOptions = { - title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0", - _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + title: "My Tools", isSystem: true, ignoreClick: true, boxShadow: "0 0", + _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]); } @@ -479,7 +478,7 @@ export class CurrentUserUtils { const newDashboard = `createNewDashboard()`; const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, - title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true }; + title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true }; const reqdBtnScript = {onClick: newDashboard,} const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); @@ -492,9 +491,9 @@ export class CurrentUserUtils { const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters const reqdOpts:DocumentOptions = { title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true, - targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, - buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias", - _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, + targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, + buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "embed", + _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, contextMenuLabels:new List(contextMenuLabels), contextMenuIcons:new List(contextMenuIcons), childContextMenuLabels:new List(childContextMenuLabels), @@ -517,20 +516,20 @@ export class CurrentUserUtils { /// initializes the left sidebar File system pane static setupFilesystem(doc: Doc, field:string) { var myFilesystem = DocCast(doc[field]); - const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); + const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, isSystem: true, isFolder: true }); const newFolder = `TreeView_addNewFolder()`; const newFolderOpts: DocumentOptions = { _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30, - title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true + title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true }; const newFolderScript = { onClick: newFolder}; const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript); - const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, - title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true, + const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, + title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", isSystem: true, isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, - treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed", childContextMenuLabels: new List(["Create new folder"]), childContextMenuIcons: new List(["plus"]), explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." @@ -545,8 +544,8 @@ export class CurrentUserUtils { /// initializes the panel displaying docs that have been recently closed static setupRecentlyClosed(doc: Doc, field:string) { - const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, - title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true, + const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, + title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "embed", isSystem: true, treeViewTruncateTitleWidth: 350, ignoreClick: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", contextMenuLabels: new List(["Empty recently closed"]), contextMenuIcons:new List(["trash"]), @@ -556,7 +555,7 @@ export class CurrentUserUtils { const clearAll = (target:string) => `getProto(${target}).data = new List([])`; const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, - title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", system: true, + title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", isSystem: true, toolTip: "Empty recently closed",}; const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")}); @@ -572,7 +571,7 @@ export class CurrentUserUtils { static setupUserDocView(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view", - boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true, + boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true, treeViewHideTitle: true, treeViewTruncateTitleWidth: 350 }; if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }); @@ -582,13 +581,13 @@ export class CurrentUserUtils { static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - _lockedPosition: true, system: true, flexDirection: "row" + _lockedPosition: true, isSystem: true, flexDirection: "row" }) static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ btnType: ButtonType.ToolButton, _forceActive: true, _hideContextMenu: true, _removeDropProperties: new List([ "_hideContextMenu", "stayInCollection"]), - _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts, + _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts, }) /// initializes the required buttons in the expanding button menu at the bottom of the Dash window @@ -606,7 +605,7 @@ export class CurrentUserUtils { ]; const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { - title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'alias', + title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }; reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); @@ -623,7 +622,7 @@ export class CurrentUserUtils { } static viewTools(): Button[] { return [ - { title: "Snap\xA0Lines", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Snap\xA0Lines", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Grid", icon: "border-all", toolTip: "Show Grid", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "View\xA0All", icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform @@ -635,7 +634,7 @@ export class CurrentUserUtils { return [ { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, toolType:"font", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, - { title: "Size", toolTip: "Font size (%size)", btnType: ButtonType.NumberButton, width: 75, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, + { title: "Size", toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, width: 75, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 }, { title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'}}, { title: "Highlight",toolTip:"Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} }, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} }, @@ -663,7 +662,7 @@ export class CurrentUserUtils { { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType:GestureUtils.Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType:GestureUtils.Gestures.Line, scripts: {onClick:`{ return setActiveTool(self.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(self.toolType, true, _readOnly_);}`} }, { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, - { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'}, numBtnMin: 1}, { title: "Ink", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(self.toolType, value, _readOnly_);}'} }, ]; } @@ -712,7 +711,7 @@ export class CurrentUserUtils { const reqdOpts:DocumentOptions = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, backgroundColor: params.backgroundColor ??"transparent", /// a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below). Otherwise, assume a transparent background - color: Colors.WHITE, system: true, dontUndo: true, + color: Colors.WHITE, isSystem: true, dontUndo: true, _nativeWidth: params.width ?? 30, _width: params.width ?? 30, _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, @@ -728,7 +727,7 @@ export class CurrentUserUtils { /// Initializes all the default buttons for the top bar context menu static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { - const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'alias', childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined); const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); @@ -750,11 +749,11 @@ export class CurrentUserUtils { /// collection of documents rendered in the overlay layer above all tabs and other UI static setupOverlays(doc: Doc, field = "myOverlayDocs") { - return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true }); + return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", isSystem: true }); } static setupPublished(doc:Doc, field = "myPublishedDocs") { - return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true }); + return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", isSystem: true }); } /// The database of all links on all documents @@ -788,9 +787,9 @@ export class CurrentUserUtils { childContextMenuLabels: new List(["Add to Dashboards",]), childContextMenuIcons: new List(["user-plus",]), "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, - // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar - _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, + childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, + // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar + _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" }; @@ -800,16 +799,16 @@ export class CurrentUserUtils { /// Import option on the left side button panel static setupImportSidebar(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { - title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", + title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _layout_showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, - childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, + childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true, dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." }; const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts); const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, - buttonText: "Import", icon: "upload", system: true }; + buttonText: "Import", icon: "upload", isSystem: true }; DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); return myImports; } @@ -818,13 +817,13 @@ export class CurrentUserUtils { /// whether to revert to "default" values, or to leave them as the user/system last set them. static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {}); - reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), + reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data_modificationDate"]), async () => { const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); }, { fireImmediately: true }); - doc.system ?? (doc.system = true); + doc.isSystem ?? (doc.isSystem = true); doc.title ?? (doc.title = Doc.CurrentUserEmail); Doc.noviceMode ?? (Doc.noviceMode = true); doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true); @@ -860,7 +859,7 @@ export class CurrentUserUtils { this.setupDocTemplates(doc); // sets up the template menu of templates this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); - DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents + DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs) @@ -874,13 +873,13 @@ export class CurrentUserUtils { return doc; } static setupFieldInfos(doc:Doc, field="fieldInfos") { - const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object + const fieldInfoOpts = { title: "Field Infos", isSystem: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts); const entries = Object.entries(new DocumentOptions()); entries.forEach(pair => { if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) { const options = pair[1] as FInfo; - const opts:DocumentOptions = { system: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")}; + const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")}; switch (options.fieldType) { case "boolean": opts.fieldValues = new List(options.values as any); break; case "number": opts.fieldValues = new List(options.values as any); break; diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index e116ae14b..1cce21034 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -327,7 +327,7 @@ export namespace DictationManager { 'new outline', { action: (target: DocumentView) => { - const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _autoHeight: true }); + const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true }); const proto = newBox.proto!; const prompt = 'Press alt + r to start dictating here...'; const head = 3; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 695b003a6..6eed4dc86 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -69,12 +69,12 @@ export class DocumentManager { public AddView = (view: DocumentView) => { //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString); if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1'; + const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1'; const link = view.rootDoc; this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView => this.LinkedDocumentViews.push({ - a: viewAnchorIndex === 'anchor2' ? otherView : view, - b: viewAnchorIndex === 'anchor2' ? view : otherView, + a: viewAnchorIndex === 'link_anchor_2' ? otherView : view, + b: viewAnchorIndex === 'link_anchor_2' ? view : otherView, l: link, }) ); @@ -173,7 +173,7 @@ export class DocumentManager { // heuristic to return the "best" documents first: // choose a document in the lightbox first - // choose an exact match over an alias match + // choose an exact match over an embedding match lightViews.map(view => view.rootDoc === toFind && toReturn.push(view)); lightViews.map(view => view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view)); docViews.map(view => view.rootDoc === toFind && toReturn.push(view)); @@ -184,15 +184,15 @@ export class DocumentManager { static GetContextPath(doc: Opt, includeExistingViews?: boolean) { if (!doc) return []; - const srcContext = DocCast(doc.annotationOn, DocCast(doc.context)); + const srcContext = DocCast(doc.annotationOn, DocCast(doc.embedContainer)); var containerDocContext = srcContext ? [srcContext, doc] : [doc]; while ( containerDocContext.length && - containerDocContext[0]?.context && - DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking && + containerDocContext[0]?.embedContainer && + DocCast(containerDocContext[0].embedContainer)?.viewType !== CollectionViewType.Docking && (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0])) ) { - containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext]; + containerDocContext = [Cast(containerDocContext[0].embedContainer, Doc, null), ...containerDocContext]; } return containerDocContext; } @@ -256,7 +256,7 @@ export class DocumentManager { if (options.openLocation === OpenWhere.lightbox) { // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox) const target = DocCast(targetDoc.annotationOn, targetDoc); - const contextView = this.getDocumentView(DocCast(target.context)); + const contextView = this.getDocumentView(DocCast(target.embedContainer)); if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) { await new Promise(waitres => setTimeout(() => waitres())); } @@ -275,7 +275,7 @@ export class DocumentManager { focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt; childDocView: Opt }>) => { let contextView: DocumentView | undefined; // view containing context that contains target while (true) { - docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; + docView.rootDoc.layout_fieldKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; docView.props.focus(docView.rootDoc, options); // focus the view within its container const { childDocView, viewSpec } = await iterator(docView); if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView }; @@ -315,7 +315,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) { DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc)); } else { - const container = DocCast(containingDoc ?? doc.context); + const container = DocCast(containingDoc ?? doc.embedContainer); const showDoc = !Doc.IsSystem(container) ? container : doc; options.toggleTarget = undefined; DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index b6de5604d..070e0f918 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; -export type dropActionType = 'alias' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties +export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call removeDropProperties /** * Initialize drag @@ -131,7 +131,7 @@ export namespace DragManager { canEmbed?: boolean; userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action - dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'alias', but the document is dropped within the same collection, the drop action will be switched to 'move' + dropAction: dropActionType; // a drop action request by the initiating code. the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move' removeDropProperties?: string[]; moveDocument?: MoveFunction; removeDocument?: RemoveFunction; @@ -193,7 +193,7 @@ export namespace DragManager { }; } - // drag a document and drop it (or make an alias/copy on drop) + // drag a document and drop it (or make an embed/copy on drop) export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, dropEvent?: () => any) { const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField()); @@ -210,8 +210,8 @@ export namespace DragManager { dragData.draggedDocuments.map(async d => !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) - : docDragData.dropAction === 'alias' - ? Doc.BestAlias(d) + : docDragData.dropAction === 'embed' + ? Doc.BestEmbedding(d) : docDragData.dropAction === 'proto' ? Doc.GetProto(d) : docDragData.dropAction === 'copy' @@ -487,7 +487,7 @@ export namespace DragManager { const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop if (dragData instanceof DocumentDragData) { - dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'alias' : dragData.defaultDropAction; + dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.ctrlKey ? 'embed' : dragData.defaultDropAction; } if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header' || e?.shiftKey) && dragData.draggedDocuments.length === 1) { if (!startWindowDragTimer) { diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 2e1d6ba23..f46ea393a 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -58,8 +58,8 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) { if (data.removeDropProperties || dbox.removeDropProperties) { - //dbox = Doc.MakeAlias(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon - dbox = Doc.MakeAlias(dbox); + //dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon + dbox = Doc.MakeEmbedding(dbox); const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []); const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps)); remProps.map(prop => (dbox[prop] = undefined)); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c8b784390..da947aba6 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -155,7 +155,7 @@ export class GroupManager extends React.Component<{}> { addGroup(groupDoc: Doc): boolean { if (this.GroupManagerDoc) { Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc); - this.GroupManagerDoc['data-lastModified'] = new DateField(); + this.GroupManagerDoc['data_modificationDate'] = new DateField(); return true; } return false; @@ -176,7 +176,7 @@ export class GroupManager extends React.Component<{}> { const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group); index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1); } - this.GroupManagerDoc['data-lastModified'] = new DateField(); + this.GroupManagerDoc['data_modificationDate'] = new DateField(); if (group === this.currentGroup) { this.currentGroup = undefined; } @@ -197,7 +197,7 @@ export class GroupManager extends React.Component<{}> { !memberList.includes(email) && memberList.push(email); groupDoc.members = JSON.stringify(memberList); SharingManager.Instance.shareWithAddedMember(groupDoc, email); - this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField()); + this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField()); } } @@ -214,7 +214,7 @@ export class GroupManager extends React.Component<{}> { const user = memberList.splice(index, 1)[0]; groupDoc.members = JSON.stringify(memberList); SharingManager.Instance.removeMember(groupDoc, email); - this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField()); + this.GroupManagerDoc && (this.GroupManagerDoc['data_modificationDate'] = new DateField()); } } } diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index 297520d5d..151f18d6f 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -20,7 +20,7 @@ export namespace Hypothesis { export const getSourceWebDoc = async (uri: string) => { const result = await findWebDoc(uri); console.log(result ? 'existing doc found' : 'existing doc NOT found'); - return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found + return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, data_useCors: true }); // create and return a new Web doc with given uri if no matching docs are found }; /** diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index 9bd92a316..55d37f544 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -1,12 +1,11 @@ -import { Doc } from "../../../fields/Doc"; -import { ImageField } from "../../../fields/URLField"; -import { Cast, StrCast, NumCast } from "../../../fields/Types"; -import { Networking } from "../../Network"; -import { Id } from "../../../fields/FieldSymbols"; -import { Utils } from "../../../Utils"; +import { Doc } from '../../../fields/Doc'; +import { ImageField } from '../../../fields/URLField'; +import { Cast, StrCast, NumCast } from '../../../fields/Types'; +import { Networking } from '../../Network'; +import { Id } from '../../../fields/FieldSymbols'; +import { Utils } from '../../../Utils'; export namespace ImageUtils { - export const ExtractExif = async (document: Doc): Promise => { const field = Cast(document.data, ImageField); if (!field) { @@ -17,23 +16,22 @@ export namespace ImageUtils { contentSize, nativeWidth, nativeHeight, - exifData: { error, data } - } = await Networking.PostToServer("/inspectImage", { source }); + exifData: { error, data }, + } = await Networking.PostToServer('/inspectImage', { source }); document.exif = error || Doc.Get.FromJson({ data }); const proto = Doc.GetProto(document); - nativeWidth && (document._height = NumCast(document._width) * nativeHeight / nativeWidth); - proto["data-nativeWidth"] = nativeWidth; - proto["data-nativeHeight"] = nativeHeight; - proto["data-path"] = source; - proto.contentSize = contentSize ? contentSize : undefined; + nativeWidth && (document._height = (NumCast(document._width) * nativeHeight) / nativeWidth); + proto['data_nativeWidth'] = nativeWidth; + proto['data_nativeHeight'] = nativeHeight; + proto['data-path'] = source; + proto.data_contentSize = contentSize ? contentSize : undefined; return data !== undefined; }; export const ExportHierarchyToFileSystem = async (collection: Doc): Promise => { - const a = document.createElement("a"); + const a = document.createElement('a'); a.href = Utils.prepend(`/imageHierarchyExport/${collection[Id]}`); a.download = `Dash Export [${StrCast(collection.title)}].zip`; a.click(); }; - -} \ No newline at end of file +} diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 9591dbed3..043f0f1f3 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -161,6 +161,7 @@ export namespace InteractionUtils { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index ba2edb65c..246f5bb8c 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -12,8 +12,8 @@ import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; /* * link doc: - * - anchor1: doc - * - anchor2: doc + * - link_anchor_1: doc + * - link_anchor_2: doc * * group doc: * - type: string representing the group type/name/category @@ -43,10 +43,10 @@ export class LinkFollower { public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); - const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1 - const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2 - const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0); - const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor1 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor1 as Doc).annotationOn) : (d.anchor1 as Doc)).length === 0); + const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_1 + const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.link_anchor_2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.link_anchor_2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is link_anchor_2 + const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_2 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_2 as Doc).annotationOn) : (d.link_anchor_2 as Doc)).length === 0); + const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews((d.link_anchor_1 as Doc).type === DocumentType.MARKER ? DocCast((d.link_anchor_1 as Doc).annotationOn) : (d.link_anchor_1 as Doc)).length === 0); const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs; const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1); @@ -55,13 +55,13 @@ export class LinkFollower { if (!followLinks.length) finished?.(); followLinks.forEach(async linkDoc => { const target = ( - sourceDoc === linkDoc.anchor1 - ? linkDoc.anchor2 - : sourceDoc === linkDoc.anchor2 - ? linkDoc.anchor1 - : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc) - ? linkDoc.anchor2 - : linkDoc.anchor1 + sourceDoc === linkDoc.link_anchor_1 + ? linkDoc.link_anchor_2 + : sourceDoc === linkDoc.link_anchor_2 + ? linkDoc.link_anchor_1 + : Doc.AreProtosEqual(sourceDoc, linkDoc.link_anchor_1 as Doc) || Doc.AreProtosEqual((linkDoc.link_anchor_1 as Doc).annotationOn as Doc, sourceDoc) + ? linkDoc.link_anchor_2 + : linkDoc.link_anchor_1 ) as Doc; if (target) { const doFollow = (canToggle?: boolean) => { @@ -92,16 +92,16 @@ export class LinkFollower { }; let movedTarget = false; if (sourceDoc.followLinkLocation === OpenWhere.inParent) { - const sourceDocParent = DocCast(sourceDoc.context); - if (target.context instanceof Doc && target.context !== sourceDocParent) { - Doc.RemoveDocFromList(target.context, Doc.LayoutFieldKey(target.context), target); + const sourceDocParent = DocCast(sourceDoc.embedContainer); + if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) { + Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target); movedTarget = true; } if (!DocListCast(sourceDocParent[Doc.LayoutFieldKey(sourceDocParent)]).includes(target)) { Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target); movedTarget = true; } - target.context = sourceDocParent; + target.embedContainer = sourceDocParent; const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)]; if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) { target.x = moveTo[0]; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 555215417..5e6107e5f 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -7,8 +7,8 @@ import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types'; import { ScriptingGlobals } from './ScriptingGlobals'; /* * link doc: - * - anchor1: doc - * - anchor2: doc + * - link_anchor_1: doc + * - link_anchor_2: doc * * group doc: * - type: string representing the group type/name/category @@ -33,7 +33,7 @@ export class LinkManager { await Promise.all( ((await DocListCastAsync(linkDb.data)) ?? []).map(link => // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager - [PromiseValue(link?.anchor1), PromiseValue(link?.anchor2)] + [PromiseValue(link?.link_anchor_1), PromiseValue(link?.link_anchor_2)] ) ); LinkManager.userLinkDBs.push(linkDb); @@ -42,13 +42,13 @@ export class LinkManager { static _links: Doc[] = []; constructor() { LinkManager._instance = this; - this.createLinkrelationshipLists(); + this.createlink_relationshipLists(); LinkManager.userLinkDBs = []; const addLinkToDoc = (link: Doc) => { Promise.all([link]).then(linkdoc => { const link = DocCast(linkdoc[0]); Promise.all([link.proto]).then(linkproto => { - Promise.all([link.anchor1, link.anchor2]).then(linkdata => { + Promise.all([link.link_anchor_1, link.link_anchor_2]).then(linkdata => { const a1 = DocCast(linkdata[0]); const a2 = DocCast(linkdata[1]); a1 && @@ -64,8 +64,8 @@ export class LinkManager { }); }; const remLinkFromDoc = (link: Doc) => { - const a1 = link?.anchor1; - const a2 = link?.anchor2; + const a1 = link?.link_anchor_1; + const a2 = link?.link_anchor_2; Promise.all([a1, a2]).then( action(() => { if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { @@ -130,11 +130,11 @@ export class LinkManager { LinkManager.addLinkDB(Doc.LinkDBDoc()); } - public createLinkrelationshipLists = () => { + public createlink_relationshipLists = () => { //create new lists for link relations and their associated colors if the lists don't already exist - !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List()); + !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List()); !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List()); - !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List()); + !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List()); }; public addLink(linkDoc: Doc, checkExists = false) { @@ -162,7 +162,7 @@ export class LinkManager { return []; } const dirLinks = Doc.GetProto(anchor)[DirectLinksSym]; - const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '-annotations']); + const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']); if (!annos) debugger; return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice()); }, true); @@ -171,9 +171,9 @@ export class LinkManager { public getRelatedGroupedLinks(anchor: Doc): Map> { const anchorGroups = new Map>(); this.relatedLinker(anchor).forEach(link => { - if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') { - const relation = StrCast(link.linkRelationship); - const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation; + if (link.link_relationship && link.link_relationship !== '-ungrouped-') { + const relation = StrCast(link.link_relationship); + const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), anchor) ? 0 : 1] : relation; const group = anchorGroups.get(anchorRelation); anchorGroups.set(anchorRelation, group ? [...group, link] : [link]); } else { @@ -189,8 +189,8 @@ export class LinkManager { //TODO This should probably return undefined if there isn't an opposite anchor //TODO This should also await the return value of the anchor so we don't filter out promises public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined { - const a1 = Cast(linkDoc.anchor1, Doc, null); - const a2 = Cast(linkDoc.anchor2, Doc, null); + const a1 = Cast(linkDoc.link_anchor_1, Doc, null); + const a2 = Cast(linkDoc.link_anchor_2, Doc, null); if (Doc.AreProtosEqual(anchor, a1)) return a2; if (Doc.AreProtosEqual(anchor, a2)) return a1; if (Doc.AreProtosEqual(anchor, a1.annotationOn as Doc)) return a2; diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index 22cca4a2e..cbc465d6a 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -127,8 +127,8 @@ export class ReplayMovements { zoomAndPan = (movement: Movement, document: CollectionFreeFormView) => { const { panX, panY, scale } = movement; scale !== 0 && document.zoomSmoothlyAboutPt([panX, panY], scale, 0); - document.Document._panX = panX; - document.Document._panY = panY; + document.Document._freeform_panX = panX; + document.Document._freeform_panY = panY; }; getFirstMovements = (movements: Movement[]): Map => { diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 79759a71d..d154c48a4 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -24,51 +24,57 @@ export namespace SearchUtil { export interface SearchParams { hl?: string; - "hl.fl"?: string; + 'hl.fl'?: string; start?: number; rows?: number; fq?: string; sort?: string; - allowAliases?: boolean; - onlyAliases?: boolean; - "facet"?: string; - "facet.field"?: string; + allowEmbeddings?: boolean; + onlyEmbeddings?: boolean; + facet?: string; + 'facet.field'?: string; } export function Search(query: string, returnDocs: true, options?: SearchParams): Promise; export function Search(query: string, returnDocs: false, options?: SearchParams): Promise; export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) { - query = query || "*"; //If we just have a filter query, search for * as the query - const rpquery = Utils.prepend("/dashsearch"); + query = query || '*'; //If we just have a filter query, search for * as the query + const rpquery = Utils.prepend('/dashsearch'); let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}*:* AND ${arg}`); - if (options.onlyAliases) { - const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery; + if (options.onlyEmbeddings) { + const header = query.match(/_[atnb]?:/) ? replacedQuery : 'DEFAULT:' + replacedQuery; replacedQuery = `{!join from=id to=proto_i}* AND ${header}`; } //console.log("Q: " + replacedQuery + " fq: " + options.fq); const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } }); - const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); + const result: IdSearchResult = gotten.startsWith('<') ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); if (!returnDocs) { return result; } const { ids, highlighting } = result; - const txtresult = query !== "*" && JSON.parse(await rp.get(Utils.prepend("/textsearch"), { - qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, "") }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures - })); + const txtresult = + query !== '*' && + JSON.parse( + await rp.get(Utils.prepend('/textsearch'), { + qs: { ...options, q: query.replace(/^[ \+\?\*\|]*/, '') }, // a leading '+' leads to a server crash since findInFiles doesn't handle regex failures + }) + ); const fileids = txtresult ? txtresult.ids : []; const newIds: string[] = []; const newLines: string[][] = []; - if (fileids) { - await Promise.all(fileids.map(async (tr: string, i: number) => { - const docQuery = "fileUpload_t:" + tr.substr(0, 7); //If we just have a filter query, search for * as the query - const docResult = JSON.parse(await rp.get(Utils.prepend("/dashsearch"), { qs: { ...options, q: docQuery } })); - newIds.push(...docResult.ids); - newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i])); - })); - } - + // bcz: we stopped storing fileUpload id's, so this won't find anything + // if (fileids) { + // await Promise.all( + // fileids.map(async (tr: string, i: number) => { + // const docQuery = 'fileUpload_t:' + tr.substr(0, 7); //If we just have a filter query, search for * as the query + // const docResult = JSON.parse(await rp.get(Utils.prepend('/dashsearch'), { qs: { ...options, q: docQuery } })); + // newIds.push(...docResult.ids); + // newLines.push(...docResult.ids.map((dr: any) => txtresult.lines[i])); + // }) + // ); + // } const theDocs: Doc[] = []; const theLines: string[][] = []; @@ -86,7 +92,7 @@ export namespace SearchUtil { const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc); for (let i = 0; i < ids.length; i++) { const testDoc = docs[i]; - if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { + if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowEmbeddings || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) { theDocs.push(testDoc); theLines.push([]); } else { @@ -97,48 +103,46 @@ export namespace SearchUtil { return { docs: theDocs, numFound: Math.max(0, result.numFound), highlighting, lines: theLines }; } - export async function GetAliasesOfDocument(doc: Doc): Promise; - export async function GetAliasesOfDocument(doc: Doc, returnDocs: false): Promise; - export async function GetAliasesOfDocument(doc: Doc, returnDocs = true): Promise { + export async function GetEmbeddingsOfDocument(doc: Doc): Promise; + export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs: false): Promise; + export async function GetEmbeddingsOfDocument(doc: Doc, returnDocs = true): Promise { const proto = Doc.GetProto(doc); const protoId = proto[Id]; if (returnDocs) { - return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).docs; + return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).docs; } else { - return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).ids; + return (await Search('', returnDocs, { fq: `proto_i:"${protoId}"`, allowEmbeddings: true })).ids; } // return Search(`{!join from=id to=proto_i}id:${protoId}`, true); } export async function GetViewsOfDocument(doc: Doc): Promise { - const results = await Search("", true, { fq: `proto_i:"${doc[Id]}"` }); + const results = await Search('', true, { fq: `proto_i:"${doc[Id]}"` }); return results.docs; } - export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> { - const docContexts = (await Search("", true, { fq: `data_l:"${doc[Id]}"` })).docs; - const aliases = await GetAliasesOfDocument(doc, false); - const aliasContexts = (await Promise.all(aliases.map(doc => Search("", true, { fq: `data_l:"${doc}"` })))); - const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] }; - aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs)); + export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[]; embeddingContexts: Doc[] }> { + const docContexts = (await Search('', true, { fq: `data_l:"${doc[Id]}"` })).docs; + const embeddings = await GetEmbeddingsOfDocument(doc, false); + const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', true, { fq: `data_l:"${doc}"` }))); + const contexts = { contexts: docContexts, embeddingContexts: [] as Doc[] }; + embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.docs)); return contexts; } - export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> { - const docContexts = (await Search("", false, { fq: `data_l:"${doc[Id]}"` })).ids; - const aliases = await GetAliasesOfDocument(doc, false); - const aliasContexts = (await Promise.all(aliases.map(doc => Search("", false, { fq: `data_l:"${doc}"` })))); - const contexts = { contexts: docContexts, aliasContexts: [] as string[] }; - aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids)); + export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[]; embeddingContexts: string[] }> { + const docContexts = (await Search('', false, { fq: `data_l:"${doc[Id]}"` })).ids; + const embeddings = await GetEmbeddingsOfDocument(doc, false); + const embeddingContexts = await Promise.all(embeddings.map(doc => Search('', false, { fq: `data_l:"${doc}"` }))); + const contexts = { contexts: docContexts, embeddingContexts: [] as string[] }; + embeddingContexts.forEach(result => contexts.embeddingContexts.push(...result.ids)); return contexts; } export async function GetAllDocs() { - const query = "*"; + const query = '*'; const response = await rp.get(Utils.prepend('/dashsearch'), { - qs: - { start: 0, rows: 10000, q: query }, - + qs: { start: 0, rows: 10000, q: query }, }); const result: IdSearchResult = JSON.parse(response); const { ids, numFound, highlighting } = result; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 396d754b6..a3a924b75 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -74,7 +74,7 @@ export class SettingsManager extends React.Component<{}> { }; @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice')); - @undoBatch changeShowTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().showTitle = (e.currentTarget as any).value ? 'title' : undefined)); + @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined)); @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value)); @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value)); @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex))); @@ -176,7 +176,7 @@ export class SettingsManager extends React.Component<{}> { return (
- (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} /> + (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().layout_showTitle !== undefined} />
Show doc header
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 4937866f8..0eecb31cf 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -172,7 +172,7 @@ export class SharingManager extends React.Component<{}> { this.setDashboardBackground(doc, permission as SharingPermissions); if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); - else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); + return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); }) .some(success => !success); }; @@ -215,7 +215,7 @@ export class SharingManager extends React.Component<{}> { return users .map(({ user, sharingDoc }) => { if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added - else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); // remove the doc from the list if it already exists + else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists }) .some(success => !success); } diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 2c0a1da8b..ed9819fc0 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -40,9 +40,9 @@ export namespace SnappingManager { } export function SetShowSnapLines(show: boolean) { - runInAction(() => (Doc.UserDoc().showSnapLines = show)); + runInAction(() => (Doc.UserDoc().freeform_snapLines = show)); } export function GetShowSnapLines() { - return Doc.UserDoc().showSnapLines; + return Doc.UserDoc().freeform_snapLines; } } diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts index cb8225643..a755c5dc9 100644 --- a/src/client/util/TrackMovements.ts +++ b/src/client/util/TrackMovements.ts @@ -68,7 +68,7 @@ export class TrackMovements { } const disposeFunc = reaction( - () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }), + () => ({ x: NumCast(doc.freeform_panX, -1), y: NumCast(doc.freeform_panY, -1), scale: NumCast(doc.freeform_scale, 0) }), res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, doc, res.scale) ); this.recordingFFViews?.set(doc, disposeFunc); diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx index 525c0ce5a..14c922526 100644 --- a/src/client/views/AudioWaveform.tsx +++ b/src/client/views/AudioWaveform.tsx @@ -1,27 +1,25 @@ -import React = require("react"); -import axios from "axios"; -import { action, computed, IReactionDisposer, reaction } from "mobx"; -import { observer } from "mobx-react"; -import Waveform from "react-audio-waveform"; -import { Doc } from "../../fields/Doc"; -import { List } from "../../fields/List"; -import { listSpec } from "../../fields/Schema"; -import { Cast } from "../../fields/Types"; -import { numberRange } from "../../Utils"; -import "./AudioWaveform.scss"; -import { Colors } from "./global/globalEnums"; - +import React = require('react'); +import axios from 'axios'; +import { action, computed, IReactionDisposer, reaction } from 'mobx'; +import { observer } from 'mobx-react'; +import Waveform from 'react-audio-waveform'; +import { Doc } from '../../fields/Doc'; +import { List } from '../../fields/List'; +import { listSpec } from '../../fields/Schema'; +import { Cast } from '../../fields/Types'; +import { numberRange } from '../../Utils'; +import './AudioWaveform.scss'; +import { Colors } from './global/globalEnums'; /** * AudioWaveform - * + * * Used in CollectionStackedTimeline to render a canvas with a visual of an audio waveform for AudioBox and VideoBox documents. * Uses react-audio-waveform package. * Bins the audio data into audioBuckets which are passed to package to render the lines. * Calculates new buckets each time a new zoom factor or new set of trim bounds is created and stores it in a field on the layout doc with a title indicating the bounds and zoom for that list (see audioBucketField) */ - export interface AudioWaveformProps { duration: number; // length of media clip rawDuration: number; // length of underlying media data @@ -32,6 +30,7 @@ export interface AudioWaveformProps { zoomFactor: number; PanelHeight: number; PanelWidth: number; + fieldKey: string; } @observer @@ -40,80 +39,71 @@ export class AudioWaveform extends React.Component { _disposer: IReactionDisposer | undefined; - @computed get waveHeight() { return Math.max(50, this.props.PanelHeight); } - - @computed get clipStart() { return this.props.clipStart; } - @computed get clipEnd() { return this.props.clipEnd; } - @computed get zoomFactor() { return this.props.zoomFactor; } + @computed get waveHeight() { + return Math.max(50, this.props.PanelHeight); + } - @computed get audioBuckets() { return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec("number"), []); } - audioBucketField = (start: number, end: number, zoomFactor: number) => "audioBuckets/" + "/" + start.toFixed(2).replace(".", "_") + "/" + end.toFixed(2).replace(".", "_") + "/" + (zoomFactor * 10); + @computed get clipStart() { + return this.props.clipStart; + } + @computed get clipEnd() { + return this.props.clipEnd; + } + @computed get zoomFactor() { + return this.props.zoomFactor; + } + @computed get audioBuckets() { + return Cast(this.props.layoutDoc[this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor)], listSpec('number'), []); + } + audioBucketField = (start: number, end: number, zoomFactor: number) => this.props.fieldKey + '_audioBuckets/' + '/' + start.toFixed(2).replace('.', '_') + '/' + end.toFixed(2).replace('.', '_') + '/' + zoomFactor * 10; componentWillUnmount() { this._disposer?.(); } componentDidMount() { - this._disposer = reaction(() => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }), + this._disposer = reaction( + () => ({ clipStart: this.clipStart, clipEnd: this.clipEnd, fieldKey: this.audioBucketField(this.clipStart, this.clipEnd, this.zoomFactor), zoomFactor: this.props.zoomFactor }), ({ clipStart, clipEnd, fieldKey, zoomFactor }) => { if (!this.props.layoutDoc[fieldKey]) { // setting these values here serves as a "lock" to prevent multiple attempts to create the waveform at nerly the same time. - const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec("number")); - this.props.layoutDoc[fieldKey] = waveform && new List(waveform.slice(clipStart / this.props.rawDuration * waveform.length, clipEnd / this.props.rawDuration * waveform.length)); + const waveform = Cast(this.props.layoutDoc[this.audioBucketField(0, this.props.rawDuration, 1)], listSpec('number')); + this.props.layoutDoc[fieldKey] = waveform && new List(waveform.slice((clipStart / this.props.rawDuration) * waveform.length, (clipEnd / this.props.rawDuration) * waveform.length)); setTimeout(() => this.createWaveformBuckets(fieldKey, clipStart, clipEnd, zoomFactor)); } - }, { fireImmediately: true }); + }, + { fireImmediately: true } + ); } // decodes the audio file into peaks for generating the waveform createWaveformBuckets = async (fieldKey: string, clipStart: number, clipEnd: number, zoomFactor: number) => { - axios({ url: this.props.mediaPath, responseType: "arraybuffer" }).then( - (response) => { - const context = new window.AudioContext(); - context.decodeAudioData( - response.data, - action((buffer) => { - const rawDecodedAudioData = buffer.getChannelData(0); - const startInd = clipStart / this.props.rawDuration; - const endInd = clipEnd / this.props.rawDuration; - const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length)); - const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor); + axios({ url: this.props.mediaPath, responseType: 'arraybuffer' }).then(response => { + const context = new window.AudioContext(); + context.decodeAudioData( + response.data, + action(buffer => { + const rawDecodedAudioData = buffer.getChannelData(0); + const startInd = clipStart / this.props.rawDuration; + const endInd = clipEnd / this.props.rawDuration; + const decodedAudioData = rawDecodedAudioData.slice(Math.floor(startInd * rawDecodedAudioData.length), Math.floor(endInd * rawDecodedAudioData.length)); + const numBuckets = Math.floor(AudioWaveform.NUMBER_OF_BUCKETS * zoomFactor); - const bucketDataSize = Math.floor( - decodedAudioData.length / numBuckets - ); - const brange = Array.from(Array(bucketDataSize)); - const bucketList = numberRange(numBuckets).map( - (i: number) => - brange.reduce( - (p, x, j) => - Math.abs( - Math.max(p, decodedAudioData[i * bucketDataSize + j]) - ), - 0 - ) / 2 - ); - this.props.layoutDoc[fieldKey] = new List(bucketList); - }) - ); - } - ); - } + const bucketDataSize = Math.floor(decodedAudioData.length / numBuckets); + const brange = Array.from(Array(bucketDataSize)); + const bucketList = numberRange(numBuckets).map((i: number) => brange.reduce((p, x, j) => Math.abs(Math.max(p, decodedAudioData[i * bucketDataSize + j])), 0) / 2); + this.props.layoutDoc[fieldKey] = new List(bucketList); + }) + ); + }); + }; render() { return (
- +
); } -} \ No newline at end of file +} diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index dee161931..02c5085a2 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -358,15 +358,15 @@ export class DashboardView extends React.Component { y: 400, _width: 1500, _height: 1000, - _fitWidth: true, - _backgroundGridShow: true, + _layout_fitWidth: true, + _freeform_backgroundGrid: true, backgroundColor: background, title: `Untitled Tab 1`, }; const title = name ? name : `Dashboard ${dashboardCount}`; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); - freeformDoc.context = dashboardDoc; + freeformDoc.embedContainer = dashboardDoc; // switching the tabs from the datadoc to the regular doc const dashboardTabs = DocListCast(dashboardDoc[DataSym].data); @@ -396,7 +396,7 @@ export class DashboardView extends React.Component { btnType: ButtonType.ClickButton, buttonText: 'New trail', icon: 'plus', - system: true, + isSystem: true, }; const reqdBtnScript = { onClick: `createNewPresentation()` }; const myTrailsBtn = DocUtils.AssignScripts(Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); @@ -406,13 +406,13 @@ export class DashboardView extends React.Component { // but we don't want to create the list of trails here-- but rather in createDashboard const reqdOpts: DocumentOptions = { title: 'My Trails', - _showTitle: 'title', + _layout_showTitle: 'title', _height: 100, treeViewHideTitle: true, - _fitWidth: true, + _layout_fitWidth: true, _gridGap: 5, _forceActive: true, - childDropAction: 'alias', + childDropAction: 'embed', treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, @@ -423,7 +423,7 @@ export class DashboardView extends React.Component { boxShadow: '0 0', childDontRegisterViews: true, targetDropAction: 'same', - system: true, + isSystem: true, explainer: 'All of the trails that you have created will appear here.', }; const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); @@ -453,7 +453,7 @@ ScriptingGlobals.add(function resetDashboard(dashboard: Doc) { DashboardView.resetDashboard(dashboard); }, 'move all dashboard tabs to single stack'); ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { - DashboardView.openDashboard(Doc.MakeAlias(dashboard)); + DashboardView.openDashboard(Doc.MakeEmbedding(dashboard)); }, 'adds Dashboard to set of Dashboards'); ScriptingGlobals.add(function snapshotDashboard() { DashboardView.snapshotDashboard(); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index eba55e30c..ffb2d92f6 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -133,7 +133,7 @@ export function ViewBoxAnnotatableComponent

() protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @computed public get annotationKey() { - return this.fieldKey + (this._annotationKeySuffix() ? '-' + this._annotationKeySuffix() : ''); + return this.fieldKey + (this._annotationKeySuffix() ? '_' + this._annotationKeySuffix() : ''); } @action.bound @@ -152,7 +152,7 @@ export function ViewBoxAnnotatableComponent

() toRemove.forEach(doc => { leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); - doc.context = undefined; + doc.embedContainer = undefined; if (recent) { Doc.RemoveDocFromList(recent, 'data', doc); doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true); @@ -193,18 +193,19 @@ export function ViewBoxAnnotatableComponent

() } const added = docs; if (added.length) { - if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) { + const aclKeys = Object.keys(this.props.Document[AclSym] ?? {}); + aclKeys.forEach(key => added.forEach(d => { - for (const key of Object.keys(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); + if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) { + distributeAcls(key, SharingPermissions.Admin, d); } - }); - } + }) + ); if (effectiveAcl === AclAugment) { added.map(doc => { if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc); - doc.context = this.props.Document; + doc.embedContainer = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); }); @@ -215,7 +216,7 @@ export function ViewBoxAnnotatableComponent

() // only make a pushpin if we have acl's to edit the document //DocUtils.LeavePushpin(doc); doc._stayInCollection = undefined; - doc.context = this.props.Document; + doc.embedContainer = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc; Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc); @@ -223,7 +224,7 @@ export function ViewBoxAnnotatableComponent

() const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List; if (annoDocs instanceof List) annoDocs.push(...added); else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); - targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now())); + targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now())); } } return true; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 30e41b06c..547d844ca 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -178,7 +178,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV e.preventDefault(); let googleDoc = await Cast(dataDoc.googleDoc, Doc); if (!googleDoc) { - const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false }; + const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, data_useCors: false }; googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } @@ -473,17 +473,17 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV ); } - @observable _aliasDown = false; + @observable _embedDown = false; onTemplateButton = action((e: React.PointerEvent): void => { this._tooltipOpen = false; - setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction); }); - onAliasButtonMoved = () => { + onEmbedButtonMoved = () => { if (this._dragRef.current) { const dragDocView = this.view0!; const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]); const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - dragData.defaultDropAction = 'alias'; + dragData.defaultDropAction = 'embed'; dragData.canEmbed = true; DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false }); return true; @@ -498,14 +498,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const view0 = this.view0; const views = this.props.views(); return !view0 ? null : ( - Tap to Customize Layout. Drag an embeddable alias

} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom"> + Tap to Customize Layout. Drag an embedding
} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
!this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}> (this._aliasDown = true))} - onClose={action(() => (this._aliasDown = false))} + onOpen={action(() => (this._embedDown = true))} + onClose={action(() => (this._embedDown = false))} content={ - !this._aliasDown ? null : ( + !this._embedDown ? null : (
{' '} v).map(v => v as DocumentView)} /> @@ -564,7 +564,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc; const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true); if (trail !== anchor.presTrail) { - DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' }); + DocUtils.MakeLink(anchor, trail, { link_relationship: 'link trail' }); anchor.presTrail = trail; } Doc.ActivePresentation = trail; @@ -589,7 +589,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV (link.linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} + linkCreated={link => (link.layout_linkDisplay = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this.props.views().lastElement()?.rootDoc} /> diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 26b64bfdd..39073d763 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -121,7 +121,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P titleFieldKey && SelectionManager.Views().forEach(d => { if (titleFieldKey === 'title') { - d.dataDoc['title-custom'] = !this._accumulatedTitle.startsWith('-'); + d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) { Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc); } @@ -138,12 +138,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (curKey !== title) { if (title) { if (d.dataDoc[title] === undefined || d.dataDoc[title] instanceof RichTextField || typeof d.dataDoc[title] === 'string') { - d.rootDoc.layoutKey = `layout_${title}`; + d.rootDoc.layout_fieldKey = `layout_${title}`; d.rootDoc[`layout_${title}`] = FormattedTextBox.LayoutString(title); - d.rootDoc[`${title}-nativeWidth`] = d.rootDoc[`${title}-nativeHeight`] = 0; + d.rootDoc[`${title}_nativeWidth`] = d.rootDoc[`${title}_nativeHeight`] = 0; } } else { - d.rootDoc.layoutKey = undefined; + d.rootDoc.layout_fieldKey = undefined; } } } else { @@ -272,22 +272,22 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const selectedDocs = SelectionManager.Views(); if (selectedDocs.length) { if (e.ctrlKey) { - // open an alias in a new tab with Ctrl Key - CollectionDockingView.AddSplit(Doc.BestAlias(selectedDocs[0].props.Document), OpenWhereMod.right); + // open an embedding in a new tab with Ctrl Key + CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].props.Document), OpenWhereMod.right); } else if (e.shiftKey) { // open centered in a new workspace with Shift Key - const alias = Doc.MakeAlias(selectedDocs[0].props.Document); - alias.context = undefined; - alias.x = -alias[WidthSym]() / 2; - alias.y = -alias[HeightSym]() / 2; - CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: 'Tab for ' + alias.title }), OpenWhereMod.right); + const embedding = Doc.MakeEmbedding(selectedDocs[0].props.Document); + embedding.embedContainer = undefined; + embedding.x = -embedding[WidthSym]() / 2; + embedding.y = -embedding[HeightSym]() / 2; + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right); } else if (e.altKey) { // open same document in new tab CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, OpenWhereMod.right); } else { var openDoc = selectedDocs[0].props.Document; - if (openDoc.layoutKey === 'layout_icon') { - openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc); + if (openDoc.layout_fieldKey === 'layout_icon') { + openDoc = DocListCast(openDoc.proto_embeddings).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc); Doc.deiconifyView(openDoc); } LightboxView.SetLightboxDoc( @@ -368,8 +368,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const newloccentern = seldocview.props.ScreenToLocalTransform().transformPoint(rotCenter[0], rotCenter[1]); const newlocenter = [newloccentern[0] - NumCast(seldocview.layoutDoc._width) / 2, newloccentern[1] - NumCast(seldocview.layoutDoc._height) / 2]; const final = Utils.rotPt(newlocenter[0], newlocenter[1], -(NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI); - seldocview.rootDoc.rotateCenterX = final.x / NumCast(seldocview.layoutDoc._width); - seldocview.rootDoc.rotateCenterY = final.y / NumCast(seldocview.layoutDoc._height); + seldocview.rootDoc.rotation_centerX = final.x / NumCast(seldocview.layoutDoc._width); + seldocview.rootDoc.rotation_centerY = final.y / NumCast(seldocview.layoutDoc._height); }; @action @@ -386,8 +386,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P action(action(() => (this._isRotating = false))), // upEvent action((e, doubleTap) => { if (doubleTap) { - seldocview.rootDoc.rotateCenterX = 0.5; - seldocview.rootDoc.rotateCenterY = 0.5; + seldocview.rootDoc.rotation_centerX = 0.5; + seldocview.rootDoc.rotation_centerY = 0.5; } }) ); @@ -586,7 +586,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P let dW = docwidth * (dWin / refWidth); let dH = docheight * (dHin / refHeight); const scale = docView.props.ScreenToLocalTransform().Scale; - const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen); + const modifyNativeDim = (e.ctrlKey || doc.layout_forceReflow) && doc.nativeDimModifiable && ((!dragBottom && !dragTop) || e.ctrlKey || doc.nativeHeightUnfrozen); if (nwidth && nheight) { if (nwidth / nheight !== docwidth / docheight && !dragBottom && !dragTop) { docheight = (nheight / nwidth) * docwidth; @@ -602,7 +602,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth)); let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight)); const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false; - const fixedAspect = nwidth && nheight && (!doc._fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); + const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { @@ -610,7 +610,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P doc._nativeWidth = (actualdW / (docwidth || 1)) * Doc.NativeWidth(doc); } } else { - if (!doc._fitWidth || preserveNativeDim) { + if (!doc._layout_fitWidth || preserveNativeDim) { actualdH = (nheight / nwidth) * actualdW; doc._height = actualdH; } else if (!modifyNativeDim || dragBotRight) { @@ -619,14 +619,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } doc._width = actualdW; } else { - if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { + if ((dragBottom || dragTop) && (modifyNativeDim || (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._layout_fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight // to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match // a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) doc._nativeHeight = (actualdH / (docheight || 1)) * Doc.NativeHeight(doc); - doc._autoHeight = false; + doc._layout_autoHeight = false; } else { - if (!doc._fitWidth || preserveNativeDim) { + if (!doc._layout_fitWidth || preserveNativeDim) { actualdW = (nwidth / nheight) * actualdH; doc._width = actualdW; } else if (!modifyNativeDim || dragBotRight) { @@ -642,10 +642,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const rotCtr = [docwidth / 2, docheight / 2]; const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI); - const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); + const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling(); dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH); dW && (doc._width = actualdW); - dH && (doc._autoHeight = false); + dH && (doc._layout_autoHeight = false); const rotCtr2 = [NumCast(doc._width) / 2, NumCast(doc._height) / 2]; const tlRotated2 = Utils.rotPt(-rotCtr2[0], -rotCtr2[1], (NumCast(doc._rotation) / 180) * Math.PI); @@ -654,7 +654,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } doc.x = NumCast(doc.x) + dX; doc.y = NumCast(doc.y) + dY; - doc._lastModified = new DateField(); + doc._layout_modificationDate = new DateField(); } const val = this._dragHeights.get(docView.layoutDoc); if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) }); @@ -670,11 +670,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P this._resizeUndo?.end(); SnappingManager.clearSnapLines(); - // detect autoHeight gesture and apply + // detect layout_autoHeight gesture and apply SelectionManager.Views() .map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) })) .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20) - .forEach(pair => (pair.doc._autoHeight = true)); + .forEach(pair => (pair.doc._layout_autoHeight = true)); //need to change points for resize, or else rotation/control points will fail. this._inkDragDocs .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) @@ -713,7 +713,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } @computed get hasIcons() { - return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === 'layout_icon'); + return SelectionManager.Views().some(docView => docView.rootDoc.layout_fieldKey === 'layout_icon'); } @observable _showRotCenter = false; @@ -722,8 +722,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (SelectionManager.Views().length) { const seldocview = SelectionManager.Views()[0]; const loccenter = Utils.rotPt( - NumCast(seldocview.rootDoc.rotateCenterX) * NumCast(seldocview.layoutDoc._width), - NumCast(seldocview.rootDoc.rotateCenterY) * NumCast(seldocview.layoutDoc._height), + NumCast(seldocview.rootDoc.rotation_centerX) * NumCast(seldocview.layoutDoc._width), + NumCast(seldocview.rootDoc.rotation_centerY) * NumCast(seldocview.layoutDoc._height), (NumCast(seldocview.rootDoc._rotation) / 180) * Math.PI ); return seldocview.props @@ -746,8 +746,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } // hide the decorations if the parent chooses to hide it or if the document itself hides it const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; - const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || this._isRounding || this._isRotating; - const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating; + const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; + const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button const hideOpenButton = @@ -765,7 +765,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P seldocview.rootDoc.hideDeleteButton || SelectionManager.Views().some(docView => { const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit; - return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); + return (docView.rootDoc.stayInCollection && !docView.rootDoc.timelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin); }); const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => ( @@ -871,7 +871,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {hideTitle ? null : titleArea} - {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')} + {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
{hideResizers ? null : ( <> diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 84b2e1407..75e0e7c4c 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -46,14 +46,14 @@ export class FilterPanel extends React.Component { @computed get _allFacets() { // trace(); const noviceReqFields = ['author', 'tags', 'text', 'type']; - const noviceLayoutFields: string[] = []; //["_curPage"]; + const noviceLayoutFields: string[] = []; //["_layout_curPage"]; const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; const keys = new Set(noviceFields); this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); const sortedKeys = Array.from(keys.keys()) .filter(key => key[0]) - .filter(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode) + .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode) .sort(); noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1)); return [...noviceFields, ...sortedKeys]; @@ -88,7 +88,7 @@ export class FilterPanel extends React.Component { (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal)); const fieldKey = Doc.LayoutFieldKey(t); const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView'); - DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); + DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); }); subDocs = newarray; } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index b04769c1e..141e99c66 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -102,7 +102,7 @@ export class GestureOverlay extends Touchable { icon: 'clipboard', pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()', pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', - clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), + clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }), backgroundColor: 'orange', }, { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' }, @@ -125,7 +125,7 @@ export class GestureOverlay extends Touchable { onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, backgroundColor: data.backgroundColor, dragFactory: data.dragFactory, - system: true, + isSystem: true, }) ); } @@ -138,19 +138,19 @@ export class GestureOverlay extends Touchable { ignoreClick: true, _lockedPosition: true, title: 'buttons', - _autoHeight: true, + _layout_autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: 'white', - system: true, + isSystem: true, }); thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], { _width: 300, _height: 25, - _autoHeight: true, + _layout_autoHeight: true, linearViewIsExpanded: true, flexDirection: 'column', - system: true, + isSystem: true, }); userDoc.thumbDoc = thumbDoc; } @@ -916,7 +916,7 @@ export class GestureOverlay extends Touchable { @computed get elements() { const selView = GestureOverlay.DownDocView; - const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1); + const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._freeform_scale, 1); // * (selView?.props.ScreenToLocalTransform().Scale || 1); const rect = this._overlayRef.current?.getBoundingClientRect(); const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true); B.left = B.left - width / 2; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 0f8b46dbe..b9b92dd2b 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -374,10 +374,10 @@ export class KeyManager { if (count === docids.length) { const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, true)).clone : d))); if (added.length) { - added.map(doc => (doc.context = targetDataDoc)); + added.map(doc => (doc.embedContainer = targetDataDoc)); undoBatch(() => { targetDataDoc[fieldKey] = new List([...docList, ...added]); - targetDataDoc[fieldKey + '-lastModified'] = new DateField(new Date(Date.now())); + targetDataDoc[fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())); })(); } } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 156825f41..d28981e17 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -50,21 +50,21 @@ export class InkStrokeProperties { if (!requireCurrPoint || this._currentPoint !== -1) { const doc = inkView.rootDoc; if (doc.type === DocumentType.INK && doc.width && doc.height) { - const ink = Cast(doc.data, InkField)?.inkData; + const ink = Cast(doc.stroke, InkField)?.inkData; if (ink) { const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); - const ptsXscale = (NumCast(doc._width) - NumCast(doc.strokeWidth)) / (oldXrange.max - oldXrange.min || 1) || 1; - const ptsYscale = (NumCast(doc._height) - NumCast(doc.strokeWidth)) / (oldYrange.max - oldYrange.min || 1) || 1; - const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth)); + const ptsXscale = (NumCast(doc._width) - NumCast(doc.stroke_width)) / (oldXrange.max - oldXrange.min || 1) || 1; + const ptsYscale = (NumCast(doc._height) - NumCast(doc.stroke_width)) / (oldYrange.max - oldYrange.min || 1) || 1; + const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.stroke_width)); if (newPoints) { const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X)); const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y)); - doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth); - doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth); + doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.stroke_width); + doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.stroke_width); doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; - Doc.GetProto(doc).data = new InkField(newPoints); + Doc.GetProto(doc).stroke = new InkField(newPoints); appliedFunc = true; } } diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 246b887a6..d6a6a64bf 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -172,7 +172,7 @@ export class InkTranscription extends React.Component { if (!selected) { return; } - const ctx = await Cast(selected[0].context, Doc); + const ctx = await Cast(selected[0].embedContainer, Doc); if (!ctx) { return; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 2fd6cc4d6..14c5dc58d 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -6,7 +6,7 @@ point 1, tangent pt 1, tangent pt 2, point 2, point 3, tangent pt 3, ... (Note that segment endpoints are duplicated ie Point2 = Point 3) brokenIndices - an array of indexes into the data field where the incoming and outgoing tangents are not constrained to be equal text - a text field that will be centered within a closed ink stroke - isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by + stroke_isInkMask - a flag that makes the ink stroke render as a mask over its collection where the stroke itself is mixBlendMode multiplied by the underlying collection content, and everything outside the stroke is covered by a semi-opaque dark gray mask. The coordinates of the ink data need to be mapped to the screen since ink points are not changed when the DocumentView is translated or scaled. @@ -134,7 +134,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { * dimmed by a semi-opaque overlay mask. */ public static toggleMask = action((inkDoc: Doc) => { - inkDoc.isInkMask = !inkDoc.isInkMask; + inkDoc.stroke_isInkMask = !inkDoc.stroke_isInkMask; }); @observable controlUndo: UndoManager.Batch | undefined; /** @@ -241,7 +241,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { */ inkScaledData = () => { const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; - const inkStrokeWidth = NumCast(this.rootDoc.strokeWidth, 1); + const inkStrokeWidth = NumCast(this.rootDoc.stroke_width, 1); const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2; const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2; const inkLeft = Math.min(...inkData.map(p => p.X)) - inkStrokeWidth / 2; @@ -310,8 +310,8 @@ export class InkingStroke extends ViewBoxBaseComponent() { .map(p => ({ X: p[0], Y: p[1] })); const screenHdlPts = screenPts; - const startMarker = StrCast(this.layoutDoc.strokeStartMarker); - const endMarker = StrCast(this.layoutDoc.strokeEndMarker); + const startMarker = StrCast(this.layoutDoc.stroke_startMarker); + const endMarker = StrCast(this.layoutDoc.stroke_endMarker); const markerScale = NumCast(this.layoutDoc.strokeMarkerScale); return SnappingManager.GetIsDragging() ? null : !InkStrokeProperties.Instance._controlButton ? ( !this.props.isSelected() || InkingStroke.IsClosed(inkData) ? null : ( @@ -330,12 +330,12 @@ export class InkingStroke extends ViewBoxBaseComponent() { screenSpaceCenterlineStrokeWidth, StrCast(inkDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), - StrCast(inkDoc.strokeBezier), + StrCast(inkDoc.stroke_bezier), 'none', startMarker, endMarker, markerScale * Math.min(screenSpaceCenterlineStrokeWidth, screenInkWidth[0] / screenSpaceCenterlineStrokeWidth), - StrCast(inkDoc.strokeDash), + StrCast(inkDoc.stroke_dash), 1, 1, '', @@ -355,11 +355,11 @@ export class InkingStroke extends ViewBoxBaseComponent() { TraceMobx(); const { inkData, inkStrokeWidth, inkLeft, inkTop, inkScaleX, inkScaleY, inkWidth, inkHeight } = this.inkScaledData(); - const startMarker = StrCast(this.layoutDoc.strokeStartMarker); - const endMarker = StrCast(this.layoutDoc.strokeEndMarker); + const startMarker = StrCast(this.layoutDoc.stroke_startMarker); + const endMarker = StrCast(this.layoutDoc.stroke_endMarker); const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1); const closed = InkingStroke.IsClosed(inkData); - const isInkMask = BoolCast(this.layoutDoc.isInkMask); + const isInkMask = BoolCast(this.layoutDoc.stroke_isInkMask); const fillColor = isInkMask ? DashColor(StrCast(this.layoutDoc.fillColor, 'transparent')).blacken(0).rgb().toString() : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FillColor) ?? 'transparent'; const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color); @@ -381,12 +381,12 @@ export class InkingStroke extends ViewBoxBaseComponent() { inkStrokeWidth, StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), - StrCast(this.layoutDoc.strokeBezier), + StrCast(this.layoutDoc.stroke_bezier), !closed ? 'none' : fillColor === 'transparent' ? 'none' : fillColor, startMarker, endMarker, markerScale, - StrCast(this.layoutDoc.strokeDash), + StrCast(this.layoutDoc.stroke_dash), inkScaleX, inkScaleY, '', @@ -408,7 +408,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { inkStrokeWidth + (fillColor ? (closed ? 2 : (highlightIndex ?? 0) + 2) : 2), StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), - StrCast(this.layoutDoc.strokeBezier), + StrCast(this.layoutDoc.stroke_bezier), !closed ? 'none' : !isInkMask && (fillColor === 'transparent' || suppressFill) ? 'none' : fillColor, '', '', @@ -444,7 +444,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { () { setContentView={this.setSubContentView} // this makes the inkingStroke the "dominant" component - ie, it will show the inking UI when selected (not text) yPadding={10} xPadding={10} - fieldKey={'text'} + fieldKey="text" fontSize={fsize} dontRegisterView={true} noSidebar={true} diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index c18a89481..1b309a6be 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -31,7 +31,7 @@ type LightboxSavedState = { panY: Opt; scale: Opt; scrollTop: Opt; - layoutKey: Opt; + layout_fieldKey: Opt; }; @observer export class LightboxView extends React.Component { @@ -51,11 +51,11 @@ export class LightboxView extends React.Component { static path: { doc: Opt; target: Opt; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt; saved: Opt }[] = []; @action public static SetLightboxDoc(doc: Opt, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) { if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) { - if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX; - if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY; - if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop; - if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale; - this.LightboxDoc.layoutKey = this._savedState.layoutKey; + if (this._savedState.panX !== undefined) this.LightboxDoc._freeform_panX = this._savedState.panX; + if (this._savedState.panY !== undefined) this.LightboxDoc._freeform_panY = this._savedState.panY; + if (this._savedState.scrollTop !== undefined) this.LightboxDoc._layout_scrollTop = this._savedState.scrollTop; + if (this._savedState.scale !== undefined) this.LightboxDoc._freeform_scale = this._savedState.scale; + this.LightboxDoc.layout_fieldKey = this._savedState.layout_fieldKey; } if (!doc) { this._docFilters && (this._docFilters.length = 0); @@ -64,17 +64,17 @@ export class LightboxView extends React.Component { MainView.Instance._exploreMode = false; } else { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); - l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); + l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); //TabDocView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { this._savedState = { - layoutKey: StrCast(doc.layoutKey), - panX: Cast(doc.panX, 'number', null), - panY: Cast(doc.panY, 'number', null), - scale: Cast(doc.viewScale, 'number', null), - scrollTop: Cast(doc.scrollTop, 'number', null), + layout_fieldKey: StrCast(doc.layout_fieldKey), + panX: Cast(doc.freeform_panX, 'number', null), + panY: Cast(doc.freeform_panY, 'number', null), + scale: Cast(doc.freeform_scale, 'number', null), + scrollTop: Cast(doc.layout_scrollTop, 'number', null), }; } } @@ -91,7 +91,7 @@ export class LightboxView extends React.Component { this._doc = doc; this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined; if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) { - doc.layoutKey = layoutTemplate; + doc.layout_fieldKey = layoutTemplate; } this._docTarget = target || doc; @@ -142,7 +142,7 @@ export class LightboxView extends React.Component { return LightboxView.SetLightboxDoc( doc, undefined, - [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort( + [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort( (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow) ), layoutTemplate @@ -156,17 +156,17 @@ export class LightboxView extends React.Component { const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); - l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); + l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target }); } else { if (!target && LightboxView.path.length) { const saved = LightboxView._savedState; if (LightboxView.LightboxDoc && saved) { - LightboxView.LightboxDoc._panX = saved.panX; - LightboxView.LightboxDoc._panY = saved.panY; - LightboxView.LightboxDoc._viewScale = saved.scale; - LightboxView.LightboxDoc._scrollTop = saved.scrollTop; + LightboxView.LightboxDoc._freeform_panX = saved.panX; + LightboxView.LightboxDoc._freeform_panY = saved.panY; + LightboxView.LightboxDoc._freeform_scale = saved.scale; + LightboxView.LightboxDoc._layout_scrollTop = saved.scrollTop; } const pop = LightboxView.path.pop(); if (pop) { @@ -210,7 +210,7 @@ export class LightboxView extends React.Component { const coll = LightboxView._docTarget; if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); - const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])]; + const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])]; const links = LinkManager.Links(coll) .map(link => LinkManager.getOppositeAnchor(link, coll)) .filter(doc => doc) @@ -305,9 +305,9 @@ export class LightboxView extends React.Component { title="toggle fit width" onClick={e => { e.stopPropagation(); - LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; + LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth; }}> - +
dv.rootDoc.showSnapLines) ? null : ( + return !SelectionManager.Views().some(dv => dv.rootDoc.freeform_snapLines) ? null : (
{SnappingManager.horizSnapLines().map(l => ( diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 556d9b6a0..eb0fd0c7a 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -123,9 +123,9 @@ export class MarqueeAnnotator extends React.Component { DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { dragComplete: e => { if (!e.aborted && e.linkDocument) { - Doc.GetProto(e.linkDocument).linkRelationship = 'cropped image'; + Doc.GetProto(e.linkDocument).link_relationship = 'cropped image'; Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title; - Doc.GetProto(e.linkDocument).linkDisplay = false; + Doc.GetProto(e.linkDocument).layout_linkDisplay = false; } }, }); @@ -153,10 +153,10 @@ export class MarqueeAnnotator extends React.Component { annotationOn: this.props.rootDoc, title: 'Annotation on ' + this.props.rootDoc.title, }); - marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1); - marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop); - marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1); - marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1); + marqueeAnno.x = NumCast(this.props.docView.props.Document.freeform_panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1); + marqueeAnno.y = NumCast(this.props.docView.props.Document.freeform_panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1) + NumCast(this.props.scrollTop); + marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1); + marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1); anno.remove(); savedAnnoMap.clear(); return marqueeAnno; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index c7a603897..f7fb443c0 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -57,7 +57,7 @@ export class PreviewCursor extends React.Component<{}> { title: url, _width: 500, _height: 300, - useCors: true, + data_useCors: true, x: newPoint[0], y: newPoint[1], }) diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index cf808f801..86230ca78 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -74,7 +74,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get maskButton() { return this.propertyToggleBtn( 'Mask', - 'isInkMask', + 'stroke_isInkMask', on => (on ? 'Make plain ink' : 'Make highlight mask'), on => 'paint-brush', (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc) @@ -91,7 +91,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get clustersButton() { return this.propertyToggleBtn( 'Clusters', - '_useClusters', + '_freeform_useClusters', on => `${on ? 'Hide' : 'Show'} clusters`, on => 'braille' ); @@ -115,7 +115,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get fitContentButton() { return this.propertyToggleBtn( 'View All', - '_fitContentsToBox', + '_freeform_fitContentsToBox', on => `${on ? "Don't" : 'Do'} fit content to container visible area`, on => 'object-group' ); @@ -136,20 +136,20 @@ export class PropertiesButtons extends React.Component<{}, {}> { // containerDoc.noShadow = // containerDoc.disableDocBrushing = // containerDoc._forceActive = - //containerDoc._fitContentsToBox = + //containerDoc._freeform_fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); - containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false))); + containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false))); }); } ); } - @computed get fitWidthButton() { + @computed get layout_fitWidthButton() { return this.propertyToggleBtn( 'Fit\xA0Width', - '_fitWidth', + '_layout_fitWidth', on => `${on ? "Don't" : 'Do'} fit content to width of container`, on => 'arrows-alt-h' ); @@ -157,10 +157,10 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get captionButton() { return this.propertyToggleBtn( 'Caption', - '_showCaption', + '_layout_showCaption', on => `${on ? 'Hide' : 'Show'} caption footer`, on => 'closed-captioning', - (dv, doc) => ((dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? 'caption' : undefined) + (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined) ); } @computed get chromeButton() { @@ -175,20 +175,20 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get titleButton() { return this.propertyToggleBtn( 'Title', - '_showTitle', + '_layout_showTitle', on => 'Switch between title styles', on => 'text-width', (dv, doc) => { const tdoc = dv?.rootDoc || doc; - const newtitle = !tdoc._showTitle ? 'title' : tdoc._showTitle === 'title' ? 'title:hover' : ''; - tdoc._showTitle = newtitle; + const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : ''; + tdoc._layout_showTitle = newtitle; } ); } - @computed get autoHeightButton() { + @computed get layout_autoHeightButton() { return this.propertyToggleBtn( 'Auto\xA0Size', - '_autoHeight', + '_layout_autoHeight', on => `Automatical vertical sizing to show all content`, on => 'arrows-alt-v' ); @@ -196,7 +196,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get gridButton() { return this.propertyToggleBtn( 'Grid', - '_backgroundGridShow', + '_freeform_backgroundGrid', on => `Display background grid in collection`, on => 'border-all' ); @@ -236,7 +236,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get snapButton() { return this.propertyToggleBtn( 'Snap\xA0Lines', - 'showSnapLines', + 'freeform_snapLines', on => `Display snapping lines when objects are dragged`, on => 'th', undefined, @@ -412,12 +412,12 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.captionButton)} {toggle(this.lockButton)} {toggle(this.onClickButton)} - {toggle(this.fitWidthButton)} + {toggle(this.layout_fitWidthButton)} {toggle(this.freezeThumb)} {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} - {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} + {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index 935d551c6..46e6fd188 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -25,7 +25,7 @@ export class PropertiesDocBacklinksSelector extends React.Component alias.context && alias.context instanceof Doc).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set()); - const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases)); + const embeddings = DocListCast(target.proto_embeddings); + const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set()); + const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.proto_embeddings)); const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); - const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases)); + const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.proto_embeddings)); const doclayouts = Array.from( doclayoutSets .reduce((p, set) => { @@ -48,7 +48,7 @@ export class PropertiesDocContextSelector extends React.Component { if (!this.props.DocView) return; - col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; + col = Doc.IsDocDataProto(col) ? Doc.MakeDelegate(col) : col; DocFocusOrOpen(Doc.GetProto(this.props.DocView.props.Document), undefined, col); }; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 0b14c7b10..19c138a21 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -123,7 +123,7 @@ export class PropertiesView extends React.Component { docWidth = () => { if (this.selectedDoc) { const layoutDoc = this.selectedDoc; - const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._fitWidth); + const aspect = Doc.NativeAspect(layoutDoc, undefined, !layoutDoc._layout_fitWidth); if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.width - 20)); return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20; } else { @@ -141,7 +141,7 @@ export class PropertiesView extends React.Component { this.MAX_EMBED_HEIGHT, Doc.NativeAspect(layoutDoc, undefined, true) ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true) - : layoutDoc._fitWidth + : layoutDoc._layout_fitWidth ? !Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) : Math.min((this.docWidth() * Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height)) @@ -199,8 +199,8 @@ export class PropertiesView extends React.Component { } @computed get noviceFields() { - const noviceReqFields = ['author', 'creationDate', 'tags', '_curPage']; - return this.editableFields(key => key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields); + const noviceReqFields = ['author', 'creationDate', 'tags', '_layout_curPage']; + return this.editableFields(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields); } @undoBatch @@ -576,7 +576,7 @@ export class PropertiesView extends React.Component { this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10)); break; case 'stk': - this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === 'up' ? 0.1 : -0.1)); + this.selectedDoc && (this.selectedDoc.stroke_width = NumCast(this.selectedDoc?.stroke_width) + (dirs === 'up' ? 0.1 : -0.1)); break; case 'wid': const oldWidth = NumCast(this.selectedDoc?._width); @@ -813,25 +813,25 @@ export class PropertiesView extends React.Component { } @computed get solidStk() { - return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === '0') ? true : false; + return this.selectedDoc?.color && (!this.selectedDoc?.stroke_dash || this.selectedDoc?.stroke_dash === '0') ? true : false; } @computed get dashdStk() { - return this.selectedDoc?.strokeDash || ''; + return this.selectedDoc?.stroke_dash || ''; } @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } @computed get widthStk() { - return this.getField('strokeWidth') || '1'; + return this.getField('stroke_width') || '1'; } @computed get markScal() { return Number(this.getField('strokeMakerScale') || '1'); } @computed get markHead() { - return this.getField('strokeStartMarker') || ''; + return this.getField('stroke_startMarker') || ''; } @computed get markTail() { - return this.getField('strokeEndMarker') || ''; + return this.getField('stroke_endMarker') || ''; } set solidStk(value) { this.dashdStk = ''; @@ -839,22 +839,22 @@ export class PropertiesView extends React.Component { } set dashdStk(value) { value && (this._lastDash = value) && (this.unStrokd = false); - this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined); + this.selectedDoc && (this.selectedDoc.stroke_dash = value ? this._lastDash : undefined); } set markScal(value) { this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value)); } set widthStk(value) { - this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); + this.selectedDoc && (this.selectedDoc.stroke_width = Number(value)); } set unStrokd(value) { this.colorStk = value ? '' : this._lastLine; } set markHead(value) { - this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); + this.selectedDoc && (this.selectedDoc.stroke_startMarker = value); } set markTail(value) { - this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); + this.selectedDoc && (this.selectedDoc.stroke_endMarker = value); } @computed get stkInput() { @@ -1151,16 +1151,16 @@ export class PropertiesView extends React.Component { } @computed get description() { - return Field.toString(LinkManager.currentLink?.description as any as Field); + return Field.toString(LinkManager.currentLink?.link_description as any as Field); } @computed get relationship() { - return StrCast(LinkManager.currentLink?.linkRelationship); + return StrCast(LinkManager.currentLink?.link_relationship); } @observable private relationshipButtonColor: string = ''; // @action - // handleDescriptionChange = (e: React.ChangeEvent) => { this.description = e.target.value; } - // handleRelationshipChange = (e: React.ChangeEvent) => { this.relationship = e.target.value; } + // handleDescriptionChange = (e: React.ChangeEvent) => { this.link_description = e.target.value; } + // handleRelationshipChange = (e: React.ChangeEvent) => { this.link_relationship = e.target.value; } @undoBatch handleDescriptionChange = action((value: string) => { @@ -1171,9 +1171,9 @@ export class PropertiesView extends React.Component { }); @undoBatch - handleLinkRelationshipChange = action((value: string) => { + handlelinkRelationshipChange = action((value: string) => { if (LinkManager.currentLink && this.selectedDoc) { - this.setLinkRelationshipValue(value); + this.setlinkRelationshipValue(value); return true; } }); @@ -1181,19 +1181,19 @@ export class PropertiesView extends React.Component { @undoBatch setDescripValue = action((value: string) => { if (LinkManager.currentLink) { - Doc.GetProto(LinkManager.currentLink).description = value; + Doc.GetProto(LinkManager.currentLink).link_description = value; return true; } }); @undoBatch - setLinkRelationshipValue = action((value: string) => { + setlinkRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { - const prevRelationship = LinkManager.currentLink.linkRelationship as string; - LinkManager.currentLink.linkRelationship = value; - Doc.GetProto(LinkManager.currentLink).linkRelationship = value; - const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); - const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes); + const prevRelationship = StrCast(LinkManager.currentLink.link_relationship); + LinkManager.currentLink.link_relationship = value; + Doc.GetProto(LinkManager.currentLink).link_relationship = value; + const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList); + const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color @@ -1264,13 +1264,13 @@ export class PropertiesView extends React.Component { }; onSelectOutRelationship = () => { - this.setLinkRelationshipValue(this.relationship); + this.setlinkRelationshipValue(this.relationship); document.getElementById('link_relationship_input')?.blur(); }; onRelationshipKey = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - this.setLinkRelationshipValue(this.relationship); + this.setlinkRelationshipValue(this.relationship); document.getElementById('link_relationship_input')?.blur(); } }; @@ -1283,7 +1283,7 @@ export class PropertiesView extends React.Component { const ldoc = LinkManager.currentLink; const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor; if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch; - return ldoc ? DocCast(ldoc.anchor2) : ldoc; + return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc; } @computed get sourceAnchor() { @@ -1314,10 +1314,10 @@ export class PropertiesView extends React.Component {

Show link

} placement="top"> + @@ -657,7 +657,7 @@ export class CollectionViewBaseChrome extends React.Component {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
- {this.aliasButton} + {this.embedButton} {/* {this.pinButton} */} {this.toggleOverlayButton}
@@ -691,7 +691,7 @@ export class CollectionFreeFormViewChrome extends React.Component key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_')); + const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_')); return keys.filter(key => key.toLowerCase().indexOf(val) > -1); - } else { - const keys = new Set(); - docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); - const noviceKeys = Array.from(keys).filter( - key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_') - ); - return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } + const keys = new Set(); + docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); + const noviceKeys = Array.from(keys).filter( + key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_') + ); + return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } if (docs instanceof Doc) { @@ -1125,13 +1124,13 @@ export class CollectionNoteTakingViewChrome extends React.Component key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_')); + const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_')); return keys.filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); const noviceKeys = Array.from(keys).filter( - key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_') + key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_') ); return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } @@ -1235,9 +1234,9 @@ export class CollectionSchemaViewChrome extends React.Component 0; return ( diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 99d4d0bee..a65e23911 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -49,12 +49,12 @@ export class CollectionNoteTakingView extends CollectionSubView() { return BoolCast(this.layoutDoc.chromeHidden); } // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns - @computed get columnHeaders() { - const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null); - const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); - if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) { + @computed get colHeaderData() { + const colHeaderData = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null); + const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !colHeaderData?.find(sh => sh.heading === 'unset')); + if (needsUnsetCategory || colHeaderData === undefined || colHeaderData.length === 0) { setTimeout(() => { - const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null) ?? []); + const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []); const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); if (needsUnsetCategory || columnHeaders.length === 0) { columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)); @@ -62,7 +62,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } }); } - return columnHeaders ?? ([] as SchemaHeaderField[]); + return colHeaderData ?? ([] as SchemaHeaderField[]); } @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); @@ -78,7 +78,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } // numGroupColumns returns the number of columns @computed get numGroupColumns() { - return this.columnHeaders.length; + return this.colHeaderData.length; } // PanelWidth returns the size of the total available space the view occupies @computed get PanelWidth() { @@ -117,7 +117,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { // (2) documentView gets unmounted as you remove it from the list @computed get Sections() { TraceMobx(); - const columnHeaders = this.columnHeaders; + const columnHeaders = this.colHeaderData; // filter out the currently dragged docs from the child docs, since we will insert them later const docs = this.childDocs.filter(d => !DragManager.docsBeingDragged.includes(d)); const sections = new Map(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); @@ -149,9 +149,10 @@ export class CollectionNoteTakingView extends CollectionSubView() { componentDidMount() { super.componentDidMount?.(); document.addEventListener('pointerup', this.removeDocDragHighlight, true); - this._disposers.autoHeight = reaction( - () => this.layoutDoc._autoHeight, - autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))))) + this._disposers.layout_autoHeight = reaction( + () => this.layoutDoc._layout_autoHeight, + layout_autoHeight => + layout_autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))))) ); } @@ -230,7 +231,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { PanelHeight={height} styleProvider={this.styleProvider} docViewPath={this.props.docViewPath} - fitWidth={this.props.childFitWidth} + layout_fitWidth={this.props.childLayoutFitWidth} isContentActive={emptyFunction} onKey={this.onKeyDown} //TODO: change this from a prop to a parameter passed into a function @@ -238,12 +239,12 @@ export class CollectionNoteTakingView extends CollectionSubView() { isDocumentActive={this.isContentActive} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} - NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox - NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined} + NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox + NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined} dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined} dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} rootSelected={this.rootSelected} - showTitle={this.props.childShowTitle} + layout_showTitle={this.props.childlayout_showTitle} dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} @@ -280,10 +281,10 @@ export class CollectionNoteTakingView extends CollectionSubView() { // if a note doc. Otherwise, returns the normal width (for graphs, images, etc...) getDocWidth(d: Doc) { const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field); - const existingHeader = this.columnHeaders.find(sh => sh.heading === heading); + const existingHeader = this.colHeaderData.find(sh => sh.heading === heading); const existingWidth = existingHeader?.width ? existingHeader.width : 0; const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth; - const width = d.fitWidth ? maxWidth : d[WidthSym](); + const width = d.layout_fitWidth ? maxWidth : d[WidthSym](); return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth); } @@ -293,14 +294,14 @@ export class CollectionNoteTakingView extends CollectionSubView() { const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc; const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); - const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); - const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); + const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0); + const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0); if (nw && nh) { const docWid = this.getDocWidth(d); return Math.min(maxHeight, (docWid * nh) / nw); } const childHeight = NumCast(childLayoutDoc._height); - const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin; + const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin; return Math.min(childHeight, maxHeight, panelHeight); } @@ -318,7 +319,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { const n = headers.length; const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0); const scaleFactor = 1 / curWidths; - this.dataDoc.columnHeaders = new List( + this.dataDoc[this.fieldKey + '_columnHeaders'] = new List( headers.map(h => { h.setWidth(Math.abs(h.width) * scaleFactor); return h; @@ -352,7 +353,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { }); // we alter the pivot fields of the docs in case they are moved to a new column. const colIndex = this.getColumnFromXCoord(xCoord); - const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading); + const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading); DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader)); // used to notify sections to re-render this.docsDraggedRowCol.length = 0; @@ -365,12 +366,12 @@ export class CollectionNoteTakingView extends CollectionSubView() { // This function is used to know which document a column SHOULD be in while it is being dragged. getColumnFromXCoord = (xCoord: number): number | undefined => { let colIndex: number | undefined = undefined; - const numColumns = this.columnHeaders.length; + const numColumns = this.colHeaderData.length; const coords = []; let colStartXCoord = 0; for (let i = 0; i < numColumns; i++) { coords.push(colStartXCoord); - colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth; + colStartXCoord += this.colHeaderData[i].width * this.availableWidth + this.DividerWidth; } coords.push(this.PanelWidth); for (let i = 0; i < numColumns; i++) { @@ -386,7 +387,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { getDocsFromXCoord = (xCoord: number): Doc[] => { const docsMatchingHeader: Doc[] = []; const colIndex = this.getColumnFromXCoord(xCoord); - const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading); + const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading); this.childDocs?.map(d => { if (d instanceof Promise) return; const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset'; @@ -401,7 +402,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { @action onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { const docView = fieldProps.DocumentView?.(); - if (docView && (e.ctrlKey || docView.rootDoc._singleLine) && ['Enter'].includes(e.key)) { + if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) { e.stopPropagation?.(); const newDoc = Doc.MakeCopy(docView.rootDoc, true); Doc.GetProto(newDoc).text = undefined; @@ -436,10 +437,10 @@ export class CollectionNoteTakingView extends CollectionSubView() { } } } - } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { - const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); + } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { + const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed e.stopPropagation(); } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); return false; @@ -468,7 +469,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { this.onPointerMove(true, e.clientX, e.clientY); docus?.map((doc: Doc) => this.addDocument(doc)); const newDoc = this.childDocs.lastElement(); - const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading); + const colHeader = colInd === undefined ? 'unset' : StrCast(this.colHeaderData[colInd].heading); newDoc[this.notetakingCategoryField] = colHeader; const docs = this.childDocList; if (docs && targInd !== -1) { @@ -502,7 +503,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { this.refList.push(ref); this.observer = new _global.ResizeObserver( action((entries: any) => { - if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { + if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))); if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) { this.props.setHeight?.(height); @@ -516,7 +517,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { select={this.props.select} addDocument={this.addDocument} chromeHidden={this.chromeHidden} - columnHeaders={this.columnHeaders} + colHeaderData={this.colHeaderData} Document={this.props.Document} DataDoc={this.props.DataDoc} resizeColumns={this.resizeColumns} @@ -524,6 +525,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { numGroupColumns={this.numGroupColumns} gridGap={this.gridGap} pivotField={this.notetakingCategoryField} + fieldKey={this.fieldKey} dividerWidth={this.DividerWidth} maxColWidth={this.maxColWidth} availableWidth={this.availableWidth} @@ -546,15 +548,15 @@ export class CollectionNoteTakingView extends CollectionSubView() { @undoBatch @action addGroup = (value: string) => { - if (this.columnHeaders) { - for (const header of this.columnHeaders) { + if (this.colHeaderData) { + for (const header of this.colHeaderData) { if (header.heading === value) { alert('You cannot use an existing column name. Please try a new column name'); return value; } } } - const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null)); + const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null)); const newColWidth = 1 / (this.numGroupColumns + 1); columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)); value && this.resizeColumns(columnHeaders); @@ -566,7 +568,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { if (!e.isPropagationStopped()) { const subItems: ContextMenuProps[] = []; subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); - subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' }); subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' }); ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' }); } @@ -576,8 +578,8 @@ export class CollectionNoteTakingView extends CollectionSubView() { @action setColumnStartXCoords = (movementXScreen: number, colIndex: number) => { const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0]; - const leftHeader = this.columnHeaders[colIndex]; - const rightHeader = this.columnHeaders[colIndex + 1]; + const leftHeader = this.colHeaderData[colIndex]; + const rightHeader = this.colHeaderData[colIndex + 1]; leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth); rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth); }; diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index 28bdd0cb9..63becac1e 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -29,8 +29,9 @@ interface CSVFieldColumnProps { docList: Doc[]; heading: string; pivotField: string; + fieldKey: string | undefined; chromeHidden?: boolean; - columnHeaders: SchemaHeaderField[] | undefined; + colHeaderData: SchemaHeaderField[] | undefined; headingObject: SchemaHeaderField | undefined; yMargin: number; numGroupColumns: number; @@ -61,9 +62,9 @@ export class CollectionNoteTakingViewColumn extends React.Component { const castedValue = this.getValue(value); if (castedValue) { - if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { + if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { return false; } this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue)); @@ -127,7 +128,7 @@ export class CollectionNoteTakingViewColumn extends React.Component { if (!value && !forceEmptyNote) return false; const key = this.props.pivotField; - const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true }); + const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true }); const colValue = this.getValue(this.props.heading); newDoc[key] = colValue; FormattedTextBox.SelectOnLoad = newDoc[Id]; @@ -141,11 +142,11 @@ export class CollectionNoteTakingViewColumn extends React.Component { - const columnHeaders = Array.from(Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null)); + const colHdrData = Array.from(Cast(this.props.Document[this.props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null)); if (this.props.headingObject) { this.props.docList.forEach(d => (d[this.props.pivotField] = undefined)); - columnHeaders.splice(columnHeaders.indexOf(this.props.headingObject), 1); - this.props.resizeColumns(columnHeaders); + colHdrData.splice(colHdrData.indexOf(this.props.headingObject), 1); + this.props.resizeColumns(colHdrData); } }; @@ -211,7 +212,7 @@ export class CollectionNoteTakingViewColumn extends React.Component { Doc.GetProto(this.props.Document)[name] = ''; - const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true }); + const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true }); if (created) { if (this.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.Document); @@ -243,7 +244,7 @@ export class CollectionNoteTakingViewColumn extends React.Component evContents} isEditingCallback={isEditing => isEditing && this.props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
- {(this.props.columnHeaders?.length ?? 0) > 1 && ( + {(this.props.colHeaderData?.length ?? 0) > 1 && ( diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 5b96a8682..ea0fbbc54 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -82,8 +82,8 @@ export class CollectionPileView extends CollectionSubView() { this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize); DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false); - this.layoutDoc._panX = 0; - this.layoutDoc._panY = -10; + this.layoutDoc._freeform_panX = 0; + this.layoutDoc._freeform_panY = -10; this.props.Document._pileLayoutEngine = computePassLayout.name; } else { const defaultSize = NumCast(this.rootDoc._starburstDiameter, 500); @@ -91,7 +91,7 @@ export class CollectionPileView extends CollectionSubView() { this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym](); - this.layoutDoc._panX = this.layoutDoc._panY = 0; + this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0; this.layoutDoc._width = this.layoutDoc._height = defaultSize; this.props.Document._pileLayoutEngine = computeStarburstLayout.name; } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 22a575989..6b4c8a3e9 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -24,7 +24,7 @@ import { undoBatch, UndoManager } from '../../util/UndoManager'; import { AudioWaveform } from '../AudioWaveform'; import { CollectionSubView } from '../collections/CollectionSubView'; import { LightboxView } from '../LightboxView'; -import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; import { VideoBox } from '../nodes/VideoBox'; import './CollectionStackedTimeline.scss'; @@ -41,7 +41,9 @@ export type CollectionStackedTimelineProps = { mediaPath: string; dictationKey: string; rawDuration: number; + dataFieldKey: string; fieldKey: string; + thumbnails?: () => string[]; }; // trimming state: shows full clip, current trim bounds, or not trimming @@ -80,6 +82,9 @@ export class CollectionStackedTimeline extends CollectionSubView 0 ? new ImageField(thumbnails[nearest]) : undefined; + const imgField = this.thumbnails.length > 0 ? new ImageField(this.thumbnails[nearest]) : undefined; this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined; } } @@ -401,11 +405,9 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._currentTimecode)) { + if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) { if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) { this.props.Pause(); } else { @@ -455,7 +457,7 @@ export class CollectionStackedTimeline extends CollectionSubView NumCast(this.layoutDoc._currentTimecode) - 1e-4) { + if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) { if (this.props.playing()) this.props.Pause(); else if (this.layoutDoc.autoPlayAnchors) this.props.Play(); else if (!this.layoutDoc.autoPlayAnchors) { @@ -596,6 +598,7 @@ export class CollectionStackedTimeline extends CollectionSubView void; + addDocTab: (doc: Doc, where: OpenWhere) => boolean; rangeClickScript: () => ScriptField; rangePlayScript: () => ScriptField; left: number; @@ -709,7 +714,7 @@ class StackedTimelineAnchor extends React.Component () => this.props.currentTimecode(), time => { const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null); - const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); + const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc); if ( !LightboxView.LightboxDoc && // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. @@ -808,11 +813,9 @@ class StackedTimelineAnchor extends React.Component isDocumentActive={this.props.isDocumentActive} PanelWidth={width} PanelHeight={height} - fitWidth={returnTrue} + layout_fitWidth={returnTrue} ScreenToLocalTransform={screenXf} - addDocTab={returnFalse} pinToPres={emptyFunction} - whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} isContentActive={returnFalse} searchFilterDocs={returnEmptyDoclist} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index eedf639aa..404ca5d65 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -49,7 +49,7 @@ export class CollectionStackingView extends CollectionSubView number; width: () => number; stackedDocTransform: () => Transform }[] = []; // Doesn't look like this field is being used anywhere. Obsolete? @@ -65,8 +65,8 @@ export class CollectionStackingView extends CollectionSubView(); + this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List(); } } @@ -154,14 +154,14 @@ export class CollectionStackingView extends CollectionSubView(); + if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map(); - if (this.columnHeaders === undefined) { - setTimeout(() => (this.layoutDoc._columnHeaders = new List()), 0); + if (this.colHeaderData === undefined) { + setTimeout(() => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List()), 0); return new Map(); } - const columnHeaders = Array.from(this.columnHeaders); - const fields = new Map(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); + const colHeaderData = Array.from(this.colHeaderData); + const fields = new Map(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []])); let changed = false; this.filteredChildren.map(d => { const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object; @@ -170,13 +170,13 @@ export class CollectionStackingView extends CollectionSubView sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`)); + const existingHeader = colHeaderData.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`)); if (existingHeader) { fields.get(existingHeader)!.push(d); } else { const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`); fields.set(newSchemaHeader, [d]); - columnHeaders.push(newSchemaHeader); + colHeaderData.push(newSchemaHeader); changed = true; } }); @@ -187,13 +187,13 @@ export class CollectionStackingView extends CollectionSubView !fields.get(key)!.length) .map(header => { fields.delete(header); - columnHeaders.splice(columnHeaders.indexOf(header), 1); + colHeaderData.splice(colHeaderData.indexOf(header), 1); changed = true; }); } changed && setTimeout( - action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), + action(() => this.colHeaderData?.splice(0, this.colHeaderData.length, ...colHeaderData)), 0 ); return fields; @@ -206,12 +206,12 @@ export class CollectionStackingView extends CollectionSubView this.pivotField, - () => (this.layoutDoc._columnHeaders = new List()) + () => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List()) ); - this._autoHeightDisposer = reaction( - () => this.layoutDoc._autoHeight, - autoHeight => - autoHeight && + this._layout_autoHeightDisposer = reaction( + () => this.layoutDoc._layout_autoHeight, + layout_autoHeight => + layout_autoHeight && this.props.setHeight?.( Math.min( NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), @@ -224,7 +224,7 @@ export class CollectionStackingView extends CollectionSubView this.props.isAnyChildContentActive(); @@ -282,12 +282,12 @@ export class CollectionStackingView extends CollectionSubView([]) : undefined; - if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) { - newDoc[layoutKey] = docView.rootDoc[layoutKey]; + if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) { + newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SelectOnLoad = newDoc[Id]; @@ -327,19 +327,19 @@ export class CollectionStackingView extends CollectionSubView (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); - const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); - const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); + const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[WidthSym]() : 0); + const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[HeightSym]() : 0); if (nw && nh) { const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid); return Math.min(maxHeight, (docWid * nh) / nw); } const childHeight = NumCast(childLayoutDoc._height); - const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin; + const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin; return Math.min(childHeight, maxHeight, panelHeight); } @@ -460,10 +460,10 @@ export class CollectionStackingView extends CollectionSubView { - if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { + if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))); if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) { this.props.setHeight?.(height); @@ -539,7 +539,7 @@ export class CollectionStackingView extends CollectionSubView { - if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { + if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0); this.props.setHeight?.(this.headerMargin + height); } @@ -608,9 +608,9 @@ export class CollectionStackingView extends CollectionSubView { - if (value && this.columnHeaders) { + if (value && this.colHeaderData) { const schemaHdrField = new SchemaHeaderField(value); - this.columnHeaders.push(schemaHdrField); + this.colHeaderData.push(schemaHdrField); return true; } return false; @@ -630,7 +630,7 @@ export class CollectionStackingView extends CollectionSubView (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); - optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' }); optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index d62c4dc62..243550c0b 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -34,7 +34,7 @@ interface CSVFieldColumnProps { heading: string; pivotField: string; chromeHidden?: boolean; - columnHeaders: SchemaHeaderField[] | undefined; + colHeaderData: SchemaHeaderField[] | undefined; headingObject: SchemaHeaderField | undefined; yMargin: number; columnWidth: number; @@ -93,7 +93,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) }; - drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false)); + this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false)); }); getValue = (value: string): any => { const parsed = parseInt(value); @@ -107,7 +107,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { const castedValue = this.getValue(value); if (castedValue) { - if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { + if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { return false; } this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue)); @@ -134,7 +134,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { if (!value && !forceEmptyNote) return false; const key = this.props.pivotField; - const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true }); + const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true }); newDoc[key] = this.getValue(this.props.heading); const maxHeading = this.props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0); const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; @@ -147,9 +147,9 @@ export class CollectionStackingViewFieldColumn extends React.Component { this.props.docList.forEach(d => (d[this.props.pivotField] = undefined)); - if (this.props.columnHeaders && this.props.headingObject) { - const index = this.props.columnHeaders.indexOf(this.props.headingObject); - this.props.columnHeaders.splice(index, 1); + if (this.props.colHeaderData && this.props.headingObject) { + const index = this.props.colHeaderData.indexOf(this.props.headingObject); + this.props.colHeaderData.splice(index, 1); } }; @@ -163,15 +163,15 @@ export class CollectionStackingViewFieldColumn extends React.Component { - // is MakeAlias a way to make a copy of a doc without rendering it? - const alias = Doc.MakeAlias(this.props.Document); - alias._width = this.props.columnWidth / (this.props.columnHeaders?.length || 1); - alias._pivotField = undefined; + // is MakeEmbedding a way to make a copy of a doc without rendering it? + const embedding = Doc.MakeEmbedding(this.props.Document); + embedding._width = this.props.columnWidth / (this.props.colHeaderData?.length || 1); + embedding._pivotField = undefined; let value = this.getValue(this._heading); value = typeof value === 'string' ? `"${value}"` : value; - alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name }); - if (alias.viewSpecScript) { - DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY); + embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name }); + if (embedding.viewSpecScript) { + DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY); return true; } return false; @@ -266,7 +266,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { Doc.GetProto(this.props.Document)[name] = ''; - const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true }); + const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true }); if (created) { if (this.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.Document); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5b9453666..008de1944 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -129,7 +129,7 @@ export function CollectionSubView(moreProps?: X) { notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, this.props.Document).length > 0; const fieldKey = Doc.LayoutFieldKey(d); const annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name); - const data = d[annos ? fieldKey + '-annotations' : fieldKey]; + const data = d[annos ? fieldKey + '_annotations' : fieldKey]; if (data !== undefined) { let subDocs = DocListCast(data); if (subDocs.length > 0) { @@ -141,7 +141,7 @@ export function CollectionSubView(moreProps?: X) { const fieldKey = Doc.LayoutFieldKey(t); const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name); notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, d).length)); - DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); + DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc)); }); subDocs = newarray; } @@ -188,9 +188,9 @@ export function CollectionSubView(moreProps?: X) { protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) { if (de.complete.docDragData) { - // if targetDropAction is, say 'alias', but we're just dragging within a collection, we want to ignore the targetAction. + // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction. // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys) - if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.context === this.props.Document && this.childDocs.includes(d))) { + if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) { de.complete.docDragData.dropAction = targetAction; } e.stopPropagation(); @@ -282,7 +282,7 @@ export function CollectionSubView(moreProps?: X) { addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); } } else if (text) { - addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: StrCast(Doc.UserDoc().showTitle), _width: 100, _height: 25 })); + addDocument(Docs.Create.TextDocument(text, { ...options, _layout_showTitle: StrCast(Doc.UserDoc().layout_showTitle), _width: 100, _height: 25 })); } return; } @@ -375,7 +375,7 @@ export function CollectionSubView(moreProps?: X) { _width: 400, _height: 512, _nativeWidth: 850, - useCors: true, + data_useCors: true, }) ); return; @@ -417,7 +417,7 @@ export function CollectionSubView(moreProps?: X) { proto._height = 20; return proto; }), - { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true } + { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _createDocOnCR: true } ) ); }); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 3cdb460a3..6135154c3 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -35,7 +35,7 @@ export class CollectionTimeView extends CollectionSubView() { async componentDidMount() { this.props.setContentView?.(this); //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), ""); - ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; useRightSplit(alias, shiftKey); "; + ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' }); @@ -55,10 +55,10 @@ export class CollectionTimeView extends CollectionSubView() { if (addAsAnnotation) { // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered - 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([anchor]); + this.dataDoc[this.props.fieldKey + '_annotations'] = new List([anchor]); } } return anchor; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f774781ab..095e34c39 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -104,7 +104,7 @@ export class CollectionTreeView extends CollectionSubView this.rootDoc.autoHeight, + () => this.rootDoc.layout_autoHeight, auto => auto && this.computeHeight(), { fireImmediately: true } ); @@ -114,25 +114,25 @@ export class CollectionTreeView extends CollectionSubView p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6; - this.layoutDoc._autoHeightMargins = bodyHeight; + this.layoutDoc._layout_autoHeightMargins = bodyHeight; !this.props.dontRegisterView && this.props.setHeight?.(bodyHeight + titleHeight); } }; unobserveHeight = (ref: any) => { this.refList.delete(ref); - this.rootDoc.autoHeight && this.computeHeight(); + this.rootDoc.layout_autoHeight && this.computeHeight(); }; observeHeight = (ref: any) => { if (ref) { this.refList.add(ref); this.observer = new _global.ResizeObserver( action((entries: any) => { - if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) { + if (this.rootDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) { this.computeHeight(); } }) ); - this.rootDoc.autoHeight && this.computeHeight(); + this.rootDoc.layout_autoHeight && this.computeHeight(); this.observer.observe(ref); } }; @@ -144,7 +144,7 @@ export class CollectionTreeView extends CollectionSubView { const dragData = de.complete.docDragData; if (dragData) { - const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d)); + const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d)); dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction; } }; @@ -176,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => { const res = flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before); - res && (doc.context = this.props.Document); + res && (doc.embedContainer = this.props.Document); return res; }, true); if (this.doc.resolvedDataDoc instanceof Promise) return false; @@ -234,7 +234,7 @@ export class CollectionTreeView extends CollectionSubView NumCast(this.doc._yMargin); marginBot = () => NumCast(this.doc._yMargin); documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth()); - documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins); + documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.layout_autoHeightMargins); truncateTitleWidth = () => this.treeViewtruncateTitleWidth; onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1)); - addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}-annotations`) || false; - remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false; + addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}_annotations`) || false; + remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}_annotations`) || false; moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => - this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false; + this.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}_annotations`) || false; @observable _headerHeight = 0; @computed get content() { @@ -430,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? ( @@ -443,7 +443,7 @@ export class CollectionTreeView extends CollectionSubView boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childContentsActive?: () => boolean | undefined; - childFitWidth?: (child: Doc) => boolean; - childShowTitle?: () => string; + childLayoutFitWidth?: (child: Doc) => boolean; + childlayout_showTitle?: () => string; childOpacity?: () => number; childContextMenuItems?: () => { script: ScriptField; label: string }[]; childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children @@ -134,8 +134,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent func(CollectionViewType.Freeform), icon: 'signature' }, { description: 'Schema', event: () => func(CollectionViewType.Schema), icon: 'th-list' }, { description: 'Tree', event: () => func(CollectionViewType.Tree), icon: 'tree' }, - { description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' }, - { description: 'Notetaking', event: () => (func(CollectionViewType.NoteTaking)._autoHeight = true), icon: 'ellipsis-v' }, + { description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._layout_autoHeight = true), icon: 'ellipsis-v' }, + { description: 'Notetaking', event: () => (func(CollectionViewType.NoteTaking)._layout_autoHeight = true), icon: 'ellipsis-v' }, { description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' }, { description: 'Multirow', event: () => func(CollectionViewType.Multirow), icon: 'columns' }, { description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' }, @@ -159,7 +159,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { - const newRendition = Doc.MakeAlias(this.rootDoc); + const newRendition = Doc.MakeEmbedding(this.rootDoc); newRendition._viewType = vtype; this.props.addDocTab(newRendition, OpenWhere.addRight); return newRendition; @@ -190,9 +190,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent { - const alias = Doc.MakeAlias(this.rootDoc); - DocUtils.makeCustomViewClicked(alias, undefined, func.key); - this.props.addDocTab(alias, OpenWhere.addRight); + const embedding = Doc.MakeEmbedding(this.rootDoc); + DocUtils.makeCustomViewClicked(embedding, undefined, func.key); + this.props.addDocTab(embedding, OpenWhere.addRight); }, }) ); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 45604c1bf..69963736b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -252,11 +252,11 @@ export class TabDocView extends React.Component { const pinDoc = Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc); pinDoc.presentationTargetDoc = anchorDoc ?? doc; pinDoc.title = doc.title + ' - Slide'; - pinDoc.data = new List(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data + pinDoc.data = new List(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; pinDoc.presDuration = pinDoc.presDuration ?? 1000; pinDoc.groupWithUp = false; - pinDoc.context = curPres; + pinDoc.embedContainer = curPres; // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. @@ -264,7 +264,7 @@ export class TabDocView extends React.Component { pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header - const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); + const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc); if (!pinProps?.audioRange && duration !== undefined) { @@ -283,7 +283,7 @@ export class TabDocView extends React.Component { pinDoc.title = doc.title + ' (move)'; pinDoc.presMovement = PresMovement.Pan; } - if (pinDoc.isInkMask) { + if (pinDoc.stroke_isInkMask) { pinDoc.presHideAfter = true; pinDoc.presHideBefore = true; pinDoc.presMovement = PresMovement.None; @@ -357,7 +357,7 @@ export class TabDocView extends React.Component { switch (whereFields[0]) { case undefined: case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { - const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; + const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined; const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)]; if (lightboxView && (!data || data instanceof List)) { lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List([doc]); @@ -409,7 +409,7 @@ export class TabDocView extends React.Component { miniMapColor = () => this.tabColor; tabView = () => this._view; disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform; - hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap); + hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.layout_hideMinimap); @computed get docView() { return !this._activated || !this._document ? null : ( @@ -449,19 +449,19 @@ export class TabDocView extends React.Component { pinToPres={TabDocView.PinDoc} /> - {this._document.hideMinimap ? 'Open minimap' : 'Close minimap'}
}> + {this._document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}
}>
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); - this._document!.hideMinimap = !this._document!.hideMinimap; + this._document!.layout_hideMinimap = !this._document!.layout_hideMinimap; })}>
@@ -559,8 +559,8 @@ export class TabMinimapView extends React.Component { this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - doc._panX = clamp(NumCast(doc._panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim); - doc._panY = clamp(NumCast(doc._panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim); + doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim); + doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim); return false; }), emptyFunction, @@ -569,10 +569,10 @@ export class TabMinimapView extends React.Component { }; render() { if (!this.renderBounds) return null; - const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100; - const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100; - const miniLeft = 50 + ((NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2; - const miniTop = 50 + ((NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2; + const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100; + const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100; + const miniLeft = 50 + ((NumCast(this.props.document._freeform_) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2; + const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2; const miniSize = this.returnMiniSize(); return this.props.hideMinimap() ? null : (
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 4adf86683..92b70cb5a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -102,7 +102,7 @@ export class TreeView extends React.Component { private _treedropDisposer?: DragManager.DragDropDisposer; get treeViewOpenIsTransient() { - return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); + return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsDocDataProto(this.doc); } set treeViewOpen(c: boolean) { if (this.treeViewOpenIsTransient) this._transientOpenState = c; @@ -125,7 +125,7 @@ export class TreeView extends React.Component { : this.props.treeView.fileSysMode ? this.doc.isFolder ? this.fieldKey - : 'aliases' // for displaying + : 'embeddings' // for displaying : this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode @@ -160,11 +160,11 @@ export class TreeView extends React.Component { @computed get childLinks() { return this.childDocList('links'); } - @computed get childAliases() { - return this.childDocList('aliases'); + @computed get childEmbeddings() { + return this.childDocList('proto_embeddings'); } @computed get childAnnos() { - return this.childDocList(this.fieldKey + '-annotations'); + return this.childDocList(this.fieldKey + '_annotations'); } @computed get selected() { return SelectionManager.IsSelected(this._docRef); @@ -219,11 +219,13 @@ export class TreeView extends React.Component { if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) { this.treeViewOpen = !this.treeViewOpen; } else { - // choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias - const bestAlias = - docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail); - const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail); - this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), OpenWhere.lightbox); + // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding + const bestEmbedding = + docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDocDataProto(docView.props.Document) + ? docView.props.Document + : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail); + const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail); + this.props.addDocTab(bestEmbedding ?? nextBestEmbedding ?? Doc.MakeEmbedding(this.props.document), OpenWhere.lightbox); } }; @@ -311,16 +313,16 @@ export class TreeView extends React.Component { treeViewExpandedViewLock: true, treeViewExpandedView: 'data', _viewType: CollectionViewType.Tree, - hideLinkButton: true, - _showSidebar: true, - _fitWidth: true, + layout_hideLinkButton: true, + _layout_showSidebar: true, + _layout_fitWidth: true, treeViewType: TreeViewType.outline, x: 0, y: 0, _xMargin: 0, _yMargin: 0, - _autoHeight: true, - _singleLine: true, + _layout_autoHeight: true, + _createDocOnCR: true, _width: 1000, _height: 10, }); @@ -359,7 +361,7 @@ export class TreeView extends React.Component { if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor(); const destDoc = this.doc; - DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' }); + DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' }); e.stopPropagation(); } const docDragData = de.complete.docDragData; @@ -378,7 +380,7 @@ export class TreeView extends React.Component { const innerAdd = (doc: Doc) => { const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); - dataIsComputed && (doc.context = this.doc.context); + dataIsComputed && (doc.embedContainer = this.doc.embedContainer); return added; }; return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); @@ -413,7 +415,7 @@ export class TreeView extends React.Component { (() => { const aspect = Doc.NativeAspect(layoutDoc); if (aspect) return this.embeddedPanelWidth() / (aspect || 1); - return layoutDoc._fitWidth + return layoutDoc._layout_fitWidth ? !Doc.NativeHeight(layoutDoc) ? NumCast(layoutDoc._height) : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height))) @@ -441,7 +443,7 @@ export class TreeView extends React.Component { const innerAdd = (doc: Doc) => { const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - dataIsComputed && (doc.context = this.doc.context); + dataIsComputed && (doc.embedContainer = this.doc.embedContainer); return added; }; return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); @@ -527,7 +529,7 @@ export class TreeView extends React.Component { TraceMobx(); const expandKey = this.treeViewExpandedView; const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }) ?? {}; - if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) { + if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) { const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None); const sortKeys = Object.keys(sortings); const curSortIndex = Math.max( @@ -549,12 +551,12 @@ export class TreeView extends React.Component { } const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - !dataIsComputed && added && (doc.context = this.doc.context); + !dataIsComputed && added && (doc.embedContainer = this.doc.embedContainer); return added; }; const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); - const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs; + const docs = expandKey === 'embeddings' ? this.childEmbeddings : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs; let downX = 0, downY = 0; if (docs?.length && this._renderCount < docs?.length) { @@ -706,13 +708,13 @@ export class TreeView extends React.Component { } @computed get validExpandViewTypes() { - const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : ''); + const annos = () => (DocListCast(this.doc[this.fieldKey + '_annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : ''); const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : ''); const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ''); - const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases'); + const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings'); const fields = () => (Doc.noviceMode ? '' : 'fields'); const layout = Doc.noviceMode || this.doc.viewType === CollectionViewType.Docking ? [] : ['layout']; - return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); + return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m); } @action expandNextviewType = () => { @@ -759,7 +761,7 @@ export class TreeView extends React.Component { const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' }; const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' }; const folderOp = this.childDocs?.length ? [makeFolder] : []; - const openAlias = { script: ScriptField.MakeFunction(`openDoc(getAlias(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Alias' }; + const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), ${OpenWhere.addRight})`)!, icon: 'copy', label: 'Open Embedding' }; const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' }; return [ ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)), @@ -768,10 +770,10 @@ export class TreeView extends React.Component { : Doc.IsSystem(this.doc) ? [] : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) - ? [openAlias, makeFolder] + ? [openEmbedding, makeFolder] : this.doc.viewType === CollectionViewType.Docking ? [] - : [deleteItem, openAlias, focusDoc]), + : [deleteItem, openEmbedding, focusDoc]), ]; }; childContextMenuItems = () => { @@ -896,7 +898,7 @@ export class TreeView extends React.Component { } })} Document={this.doc} - fitWidth={returnTrue} + layout_fitWidth={returnTrue} DataDoc={undefined} scriptContext={this} hideDecorationTitle={this.props.treeView.outlineMode} @@ -985,7 +987,7 @@ export class TreeView extends React.Component { ref={action((r: DocumentView | null) => (this._dref = r))} Document={this.doc} DataDoc={undefined} - fitWidth={this.fitWidthFilter} + layout_fitWidth={this.fitWidthFilter} PanelWidth={this.embeddedPanelWidth} PanelHeight={this.embeddedPanelHeight} LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined} @@ -1179,7 +1181,7 @@ export class TreeView extends React.Component { TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined; Doc.AddDocToList(newParent, fieldKey, child, addAfter, false); newParent.treeViewOpen = true; - child.context = treeView.Document; + child.embedContainer = treeView.Document; } }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 0dfd119d7..68ba3d368 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -34,11 +34,11 @@ export class CollectionFreeFormLinkView extends React.Component [ this.props.A.props.ScreenToLocalTransform(), - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.[CssSym], + 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)?.[CssSym], this.props.B.props.ScreenToLocalTransform(), - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.[CssSym], + 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)?.[CssSym], ], action(() => { this._start = Date.now(); @@ -58,7 +58,7 @@ export class CollectionFreeFormLinkView extends React.Component (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), + action(() => (!LinkDocs.length || !linkDoc.layout_linkDisplay) && (this._opacity = 0.05)), 750 ); // this will unhighlight the link line. const a = A.ContentDiv.getBoundingClientRect(); @@ -72,36 +72,36 @@ export class CollectionFreeFormLinkView extends React.Component= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100; - if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100; + 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; else linkDoc.opacity = 1; } if (!targetBhyperlink) { - if (linkDoc.linkAutoMove) { - linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100; - linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100; + if (linkDoc.layout_autoMoveAnchors) { + linkDoc.link_anchor_2_x = ((bpt.point.x - bleft) / bwidth) * 100; + linkDoc.link_anchor_2_y = ((bpt.point.y - btop) / bheight) * 100; } } 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(); - if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100; - if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100; + 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; else linkDoc.opacity = 1; } @@ -112,8 +112,8 @@ export class CollectionFreeFormLinkView extends React.Component { - this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0]; - this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + 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, @@ -205,8 +205,8 @@ export class CollectionFreeFormLinkView extends React.Component; + const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship + const linkRelationshipList = Doc.UserDoc().link_relationshipList as List; const linkColorList = Doc.UserDoc().linkColorList as List; - const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List; + const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List; const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship); - const linkDescription = Field.toString(link.description as any as Field); + const linkDescription = Field.toString(link.link_description as any as Field); const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex]; @@ -261,11 +261,11 @@ export class CollectionFreeFormLinkView extends React.Component @@ -294,7 +294,7 @@ export class CollectionFreeFormLinkView extends React.Component {textX === undefined || !linkDescription ? null : ( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 08e3174d1..4d18ebeea 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -100,13 +100,13 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.props.DocumentView?.().nativeWidth) return; const vals = this.fitToContentVals; - this.layoutDoc._panX = vals.bounds.cx; - this.layoutDoc._panY = vals.bounds.cy; - this.layoutDoc._viewScale = vals.scale; + 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); reverseNativeScaling = () => (this.fitContentsToBox ? true : false); - // panx, pany, zoomscale 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. + // 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)?.panX, 1)); - panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); + 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)); contentTransform = () => this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; @@ -297,7 +297,7 @@ export class CollectionFreeFormView extends CollectionSubView= -1e-4 && curTime <= endTime); } @@ -348,7 +348,7 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout) .slice() .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1)); + zsorted.forEach((doc, index) => (doc.zIndex = doc.stroke_isInkMask ? 5000 : index + 1)); const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000)); const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; @@ -366,7 +366,7 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout) .reduce((cluster, cd) => { - const grouping = this.props.Document._useClusters ? NumCast(cd.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; @@ -463,10 +463,10 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster); + 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 ? 'alias' : undefined); + const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined); de.moveDocument = this.props.moveDocument; de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top); DragManager.StartDocumentDrag( @@ -485,8 +485,8 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).map(c => this.updateCluster(c)); } @@ -494,38 +494,43 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout); - if (this.props.Document._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.cluster); - docs.map(doc => (doc.cluster = -1)); + const preferredInd = NumCast(docFirst.layout_cluster); + docs.map(doc => (doc.layout_cluster = -1)); docs.map(doc => this._clusterSets.map((set, i) => set.map(member => { - if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) { - docFirst.cluster = i; + if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) { + docFirst.layout_cluster = i; } }) ) ); - if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) { - docFirst.cluster = preferredInd; + if ( + docFirst.layout_cluster === -1 && + preferredInd !== -1 && + this._clusterSets.length > preferredInd && + (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) + ) { + docFirst.layout_cluster = preferredInd; } this._clusterSets.map((set, i) => { - if (docFirst.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) { - docFirst.cluster = i; + if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) { + docFirst.layout_cluster = i; } }); - if (docFirst.cluster === -1) { + if (docFirst.layout_cluster === -1) { docs.map(doc => { - doc.cluster = this._clusterSets.length; + doc.layout_cluster = this._clusterSets.length; this._clusterSets.push([doc]); }); } else if (this._clusterSets.length) { - for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]); - docs.map(doc => this._clusterSets[(doc.cluster = NumCast(docFirst.cluster))].push(doc)); + for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]); + docs.map(doc => this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc)); } - childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child)); + childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child)); } } @@ -533,31 +538,31 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout); - if (this.props.Document._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.cluster); - doc.cluster = -1; + const preferredInd = NumCast(doc.layout_cluster); + doc.layout_cluster = -1; this._clusterSets.forEach((set, i) => set.forEach(member => { - if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) { - doc.cluster = i; + if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) { + doc.layout_cluster = i; } }) ); - if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) { - doc.cluster = preferredInd; + if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) { + doc.layout_cluster = preferredInd; } this._clusterSets.forEach((set, i) => { - if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) { - doc.cluster = i; + if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) { + doc.layout_cluster = i; } }); - if (doc.cluster === -1) { - doc.cluster = this._clusterSets.length; + if (doc.layout_cluster === -1) { + doc.layout_cluster = this._clusterSets.length; this._clusterSets.push([doc]); } else if (this._clusterSets.length) { - for (let i = this._clusterSets.length; i <= doc.cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]); - this._clusterSets[doc.cluster ?? 0].push(doc); + for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]); + this._clusterSets[doc.layout_cluster ?? 0].push(doc); } } } @@ -566,8 +571,8 @@ export class CollectionFreeFormView extends CollectionSubView doc && this.updateCluster(doc)); } else { @@ -591,7 +596,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (this._hitCluster !== -1) { !addToSel && SelectionManager.DeselectAll(); - const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.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; } @@ -678,7 +683,6 @@ export class CollectionFreeFormView extends CollectionSubView { if (!this._deleteList.includes(intersect.inkView)) { this._deleteList.push(intersect.inkView); - SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1'); + SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1'); SetActiveInkColor(StrCast(intersect.inkView.rootDoc.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) { @@ -1021,19 +1025,19 @@ export class CollectionFreeFormView extends CollectionSubView 20) { deltaScale = 20 / invTransform.Scale; } - if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) { + if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) { this.setPan(0, 0); return; } - if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) { - deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale; + if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._freeform_scaleMin, 1) && this.isAnnotationOverlay) { + deltaScale = NumCast(this.rootDoc._freeform_scaleMin, 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.scrollTop) * safeScale) || -localTransform.TranslateY / safeScale); + this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale); } }; @@ -1113,26 +1117,26 @@ export class CollectionFreeFormView extends CollectionSubView (this.rootDoc.scrollTop = relTop * maxScrollTop), 10); + setTimeout(() => (this.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10); newPanY = minPanY; } !this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX); @@ -1143,7 +1147,7 @@ export class CollectionFreeFormView extends CollectionSubView { const collectionDoc = this.props.docViewPath().lastElement().rootDoc; - if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._panX !== undefined) { + if (collectionDoc?._viewType !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) { 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(), @@ -1162,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); let zfirst = docs.length ? NumCast(docs[0].zIndex) : 0; doc.zIndex = zfirst - 1; - } else if (doc.isInkMask) { + } else if (doc.stroke_isInkMask) { doc.zIndex = 5000; } else { const docs = this.childLayoutPairs.map(pair => pair.layout).slice(); @@ -1222,8 +1226,8 @@ export class CollectionFreeFormView extends CollectionSubView { const docView = fieldProps.DocumentView?.(); - if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ['Tab', 'Enter'].includes(e.key)) { + if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { e.stopPropagation?.(); const below = !e.altKey && e.key !== 'Tab'; - const layoutKey = StrCast(docView.LayoutFieldKey); + const layout_fieldKey = StrCast(docView.LayoutFieldKey); const newDoc = Doc.MakeCopy(docView.rootDoc, true); const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)]; newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : 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 (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) { - newDoc[layoutKey] = docView.rootDoc[layoutKey]; + if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) { + newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SelectOnLoad = newDoc[Id]; @@ -1315,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -1521,7 +1525,7 @@ export class CollectionFreeFormView extends CollectionSubView([anchor]); + this.dataDoc[this.props.fieldKey + '_annotations'] = new List([anchor]); } } return anchor; @@ -1644,8 +1648,8 @@ export class CollectionFreeFormView extends CollectionSubView { this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc['icon-nativeWidth'] = nativeWidth; - this.dataDoc['icon-nativeHeight'] = nativeHeight; + this.dataDoc['icon_nativeWidth'] = nativeWidth; + this.dataDoc['icon_nativeHeight'] = nativeHeight; } ); @@ -1762,7 +1766,7 @@ export class CollectionFreeFormView extends CollectionSubView (Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards), icon: 'eye' }); appearanceItems.push({ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`, - event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox), + event: () => (this.Document._freeform_fitContentsToBox = !this.fitContentsToBox), icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt', }); appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' }); @@ -1781,7 +1785,7 @@ export class CollectionFreeFormView extends CollectionSubView SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' }) : null; - !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._useClusters), icon: 'braille' }) : null; + !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' }); const options = ContextMenu.Instance.findByDescription('Options...'); @@ -1897,7 +1901,7 @@ export class CollectionFreeFormView extends CollectionSubView - {this.layoutDoc._backgroundGridShow ? ( + {this.layoutDoc._freeform_backgroundGrid ? (
this.nativeDimScaling; @@ -2310,7 +2314,7 @@ ScriptingGlobals.add(function sendToBack(doc: Doc) { }); ScriptingGlobals.add(function resetView() { SelectionManager.Docs().forEach(doc => { - doc._panX = doc._panY = 0; - doc._viewScale = 1; + doc._freeform_panX = doc._freeform_panY = 0; + doc._freeform_scale = 1; }); }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 11d466b0f..fdff13b54 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -52,8 +52,8 @@ export interface MarqueeViewBounds { @observer export class MarqueeView extends React.Component { public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) { - const ps = NumCast(pinDoc._viewScale, 1); - return { left: NumCast(pinDoc._panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps }; + 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 }; } private _commandExecuted = false; @@ -110,7 +110,7 @@ export class MarqueeView extends React.Component this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', 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(); @@ -196,7 +196,7 @@ export class MarqueeView extends React.Component 1) { @@ -204,7 +204,7 @@ export class MarqueeView extends React.Component (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined)); if (groupAttr) { docDataProto._group = groupAttr; @@ -379,10 +379,10 @@ export class MarqueeView extends React.Component(selected); Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform'; !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc)); - doc._panX = doc._panY = 0; + doc._freeform_panX = doc._freeform_panY = 0; return doc; })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); - newCollection.system = undefined; + newCollection.isSystem = undefined; newCollection._width = this.Bounds.width; newCollection._height = this.Bounds.height; newCollection._isGroup = makeGroup; @@ -390,8 +390,8 @@ export class MarqueeView extends React.Component (d.context = newCollection)); + newCollection.layout_fitWidth = true; + selected.forEach(d => (d.embedContainer = newCollection)); this.hideMarquee(); return newCollection; }); @@ -516,9 +516,19 @@ export class MarqueeView extends React.Component Doc.AreProtosEqual(DocCast(doc.context), this.rootDoc)); + return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc)); } @computed get documentKeys() { @@ -87,7 +87,7 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get previewWidth() { - return NumCast(this.layoutDoc.schemaPreviewWidth); + return NumCast(this.layoutDoc.schema_previewWidth); } @computed get tableWidth() { @@ -95,12 +95,12 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get columnKeys() { - return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys); + return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys); } @computed get storedColumnWidths() { const widths = NumListCast( - this.layoutDoc.columnWidths, + this.layoutDoc.schema_columnWidths, this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length) ); @@ -243,7 +243,7 @@ export class CollectionSchemaView extends CollectionSubView() { let currKeys = [...this.columnKeys]; currKeys[index] = newKey; - this.layoutDoc.columnKeys = new List(currKeys); + this.layoutDoc.schema_columnKeys = new List(currKeys); }; @undoBatch @@ -257,11 +257,11 @@ export class CollectionSchemaView extends CollectionSubView() { const currWidths = this.storedColumnWidths.slice(); currWidths.splice(0, 0, newColWidth); const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); - this.layoutDoc.columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); + this.layoutDoc.schema_columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); let currKeys = this.columnKeys.slice(); currKeys.splice(0, 0, key); - this.layoutDoc.columnKeys = new List(currKeys); + this.layoutDoc.schema_columnKeys = new List(currKeys); }; @action @@ -274,11 +274,11 @@ export class CollectionSchemaView extends CollectionSubView() { const currWidths = this.storedColumnWidths.slice(); currWidths.splice(index, 1); const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); - this.layoutDoc.columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); + this.layoutDoc.schema_columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); let currKeys = this.columnKeys.slice(); currKeys.splice(index, 1); - this.layoutDoc.columnKeys = new List(currKeys); + this.layoutDoc.schema_columnKeys = new List(currKeys); }; @action @@ -317,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView() { @action finishResize = () => { - this.layoutDoc.columnWidths = new List(this._displayColumnWidths); + this.layoutDoc.schema_columnWidths = new List(this._displayColumnWidths); this._displayColumnWidths = undefined; }; @@ -326,11 +326,11 @@ export class CollectionSchemaView extends CollectionSubView() { moveColumn = (fromIndex: number, toIndex: number) => { let currKeys = this.columnKeys.slice(); currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]); - this.layoutDoc.columnKeys = new List(currKeys); + this.layoutDoc.schema_columnKeys = new List(currKeys); let currWidths = this.storedColumnWidths.slice(); currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]); - this.layoutDoc.columnWidths = new List(currWidths); + this.layoutDoc.schema_columnWidths = new List(currWidths); }; @action @@ -480,14 +480,14 @@ export class CollectionSchemaView extends CollectionSubView() { const maxWidth = 1000; const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; - this.layoutDoc.schemaPreviewWidth = width; + this.layoutDoc.schema_previewWidth = width; return false; }; @action addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => { if (!value && !forceEmptyNote) return false; - const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true }); + const newDoc = Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true }); FormattedTextBox.SelectOnLoad = newDoc[Id]; FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this.addRow(newDoc) || false; @@ -500,7 +500,7 @@ export class CollectionSchemaView extends CollectionSubView() { ContextMenu.Instance.setDefaultItem('::', (name: string): void => { Doc.GetProto(this.props.Document)[name] = ''; - this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true })); + this.addRow(Docs.Create.TextDocument('', { title: name, _layout_autoHeight: true })); }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @@ -837,7 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() { }); return { docs }; } - rowHeightFunc = () => (BoolCast(this.layoutDoc._singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight); + rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight); sortedDocsFunc = () => this.sortedDocs; isContentActive = () => this.props.isSelected() || this.props.isContentActive(); screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0); @@ -968,7 +968,7 @@ class CollectionSchemaViewDocs extends React.Component diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 34631e0b7..45bfe4f77 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -59,7 +59,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { onPointerMove = (e: any) => { if (!SnappingManager.GetIsDragging()) return; - const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.context === this.schemaDoc); // this.schemaView?._selectedDocs.has(doc) ?? false; + const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); // this.schemaView?._selectedDocs.has(doc) ?? false; if (this._ref && dragIsRow) { const rect = this._ref.getBoundingClientRect(); diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 9f659752a..42bf32475 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -73,7 +73,7 @@ export class SchemaTableCell extends React.Component { isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'alias' as dropActionType, + dropAction: 'embed', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, @@ -83,7 +83,7 @@ export class SchemaTableCell extends React.Component { addDocTab: SchemaTableCell.addFieldDoc, pinToPres: returnZero, Document, - fieldKey, + fieldKey: fieldKey, PanelWidth: columnWidth, PanelHeight: props.rowHeight, }; diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index d02a1c4eb..b9d56541a 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -24,12 +24,12 @@ export class LinkMenuGroup extends React.Component { private _menuRef = React.createRef(); getBackgroundColor = (): string => { - const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + const link_relationshipList = StrListCast(Doc.UserDoc().link_relationshipList); const linkColorList = StrListCast(Doc.UserDoc().linkColorList); let color = 'white'; // if this link's relationship property is not default "link", set its color - if (linkRelationshipList) { - const relationshipIndex = linkRelationshipList.indexOf(this.props.groupType); + if (link_relationshipList) { + const relationshipIndex = link_relationshipList.indexOf(this.props.groupType); const RGBcolor: string = linkColorList[relationshipIndex]; if (RGBcolor) { //set opacity to 0.25 by modifiying the rgb string @@ -47,17 +47,18 @@ export class LinkMenuGroup extends React.Component { const sourceDoc = this.props.docView.anchorViewDoc ?? (this.props.docView.rootDoc.type === DocumentType.LINK // - ? this.props.docView.props.LayoutTemplateString?.includes('anchor1') - ? DocCast(linkDoc.anchor1) - : DocCast(linkDoc.anchor2) + ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1') + ? DocCast(linkDoc.link_anchor_1) + : DocCast(linkDoc.link_anchor_2) : this.props.sourceDoc); const destDoc = !sourceDoc ? undefined : this.props.docView.rootDoc.type === DocumentType.LINK - ? this.props.docView.props.LayoutTemplateString?.includes('anchor1') - ? DocCast(linkDoc.anchor2) - : DocCast(linkDoc.anchor1) - : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null)); + ? this.props.docView.props.LayoutTemplateString?.includes('link_anchor_1') + ? DocCast(linkDoc.link_anchor_2) + : DocCast(linkDoc.link_anchor_1) + : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || + LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null).annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null)); return !destDoc || !sourceDoc ? null : ( void; } -// drag links and drop link targets (aliasing them if needed) +// drag links and drop link targets (embedding them if needed) export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { const draggedDocs = (specificLinks ? specificLinks : LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; @@ -45,7 +43,7 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume const dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); dragData.canEmbed = true; - dragData.dropAction = 'alias'; + dragData.dropAction = 'embed'; DragManager.StartDocumentDrag([dragEle], dragData, downX, downY, undefined); } @@ -65,9 +63,9 @@ export class LinkMenuItem extends React.Component { @computed get sourceAnchor() { const ldoc = this.props.linkDoc; - if (this.props.sourceDoc !== ldoc.anchor1 && this.props.sourceDoc !== ldoc.anchor2) { - if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor1); - if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.anchor2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.anchor2); + if (this.props.sourceDoc !== ldoc.link_anchor_1 && this.props.sourceDoc !== ldoc.link_anchor_2) { + if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_1).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_1); + if (Doc.AreProtosEqual(DocCast(DocCast(ldoc.link_anchor_2).annotationOn), this.props.sourceDoc)) return DocCast(ldoc.link_anchor_2); } return this.props.sourceDoc; } @@ -79,7 +77,7 @@ export class LinkMenuItem extends React.Component { this, e, e => { - const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'alias'); + const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed'); dragData.removeDropProperties = ['hidden']; DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); return true; @@ -89,7 +87,7 @@ export class LinkMenuItem extends React.Component { const trail = DocCast(this.props.docView.rootDoc.presTrail); if (trail) { Doc.ActivePresentation = trail; - MainView.addDocTabFunc(trail, OpenWhere.replaceRight); + DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight); } else { SelectionManager.SelectView(this.props.docView, false); if ((SettingsManager.propertiesWidth ?? 0) < 100) { @@ -118,10 +116,10 @@ export class LinkMenuItem extends React.Component { this.props.itemHandler?.(this.props.linkDoc); } else { const focusDoc = - Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc - ? Cast(this.props.linkDoc.anchor1, Doc, null) - : Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc - ? Cast(this.props.linkDoc.anchor12, Doc, null) + Cast(this.props.linkDoc.link_anchor_1, Doc, null)?.annotationOn === this.props.sourceDoc + ? Cast(this.props.linkDoc.link_anchor_1, Doc, null) + : Cast(this.props.linkDoc.link_anchor_2, Doc, null)?.annotationOn === this.props.sourceDoc + ? Cast(this.props.linkDoc.link_anchor_12, Doc, null) : undefined; if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true }); @@ -149,7 +147,7 @@ export class LinkMenuItem extends React.Component { return (
-
+
{ {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}

- {!this.props.linkDoc.description ? null :

{StrCast(this.props.linkDoc.description)}

} + {!this.props.linkDoc.link_description ? null :

{StrCast(this.props.linkDoc.link_description)}

}
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx index 53da880e4..9662b2fea 100644 --- a/src/client/views/linking/LinkRelationshipSearch.tsx +++ b/src/client/views/linking/LinkRelationshipSearch.tsx @@ -1,8 +1,8 @@ -import { observer } from "mobx-react"; +import { observer } from 'mobx-react'; import './LinkEditor.scss'; -import React = require("react"); +import React = require('react'); -interface LinkRelationshipSearchProps { +interface link_relationshipSearchProps { results: string[] | undefined; display: string; //callback fn to set rel + hide dropdown upon setting @@ -10,48 +10,48 @@ interface LinkRelationshipSearchProps { toggleSearch: () => void; } @observer -export class LinkRelationshipSearch extends React.Component { - +export class link_relationshipSearch extends React.Component { handleResultClick = (e: React.MouseEvent) => { const relationship = (e.target as HTMLParagraphElement).textContent; if (relationship) { this.props.handleRelationshipSearchChange(relationship); } - } + }; handleMouseEnter = () => { this.props.toggleSearch(); - } + }; handleMouseLeave = () => { this.props.toggleSearch(); - } + }; /** - * Render an empty div to increase the height of LinkEditor to accommodate 2+ results - */ + * Render an empty div to increase the height of LinkEditor to accommodate 2+ results + */ emptyDiv = () => { - if (this.props.results && this.props.results.length > 2 && this.props.display === "block") { - return
; + if (this.props.results && this.props.results.length > 2 && this.props.display === 'block') { + return
; } - } + }; render() { return (
-
- { // return a dropdown of relationship results if there exist results - this.props.results - ? this.props.results.map(result => { - return

- {result} -

; +
+ { + // return a dropdown of relationship results if there exist results + this.props.results ? ( + this.props.results.map(result => { + return ( +

+ {result} +

+ ); }) - :

No matching relationships

+ ) : ( +

No matching relationships

+ ) }
@@ -60,4 +60,4 @@ export class LinkRelationshipSearch extends React.Component ); } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 68fb19208..8e83cf121 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -76,7 +76,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this.getLinkData(l)) .forEach(({ la1, la2, linkTime }) => { - if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < this._ele!.currentTime) { + if (linkTime > NumCast(this.layoutDoc._layout_currentTimecode) && linkTime < this._ele!.currentTime) { Doc.linkFollowHighlight(la1); } }); - this.layoutDoc._currentTimecode = this._ele.currentTime; - this.timeline?.scrollToTime(NumCast(this.layoutDoc._currentTimecode)); + this.layoutDoc._layout_currentTimecode = this._ele.currentTime; + this.timeline?.scrollToTime(NumCast(this.layoutDoc._layout_currentTimecode)); } }; @@ -221,7 +221,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent l.anchor1 === link || l.anchor2 === link) + .filter(l => l.link_anchor_1 === link || l.link_anchor_2 === link) .forEach(l => { const { la1, la2 } = this.getLinkData(l); const startTime = this.timeline?.anchorStart(la1) || this.timeline?.anchorStart(la2); @@ -435,7 +435,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this.props.ScreenToLocalTransform().translate(0, -AudioBox.topControlsHeight); - setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._currentTimecode = time); + setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._layout_currentTimecode = time); playing = () => this.mediaState === media_state.Playing; @@ -547,7 +547,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent
-
{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
+
{formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode)))}
) : (
@@ -623,7 +623,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent -
{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
+
{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._layout_currentTimecode) - NumCast(this.timeline.clipStart)))}
{this.miniPlayer ? (
/
) : ( @@ -656,6 +656,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent (this._stackedTimeline = r))} {...this.props} CollectionFreeFormDocumentView={undefined} + dataFieldKey={this.fieldKey} fieldKey={this.annotationKey} dictationKey={this.fieldKey + '-dictation'} mediaPath={this.path} @@ -689,7 +690,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '-duration'] = this._ele.duration))}> + onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '_duration'] = this._ele.duration))}> Not supported. diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 862d0da8c..6710cee63 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -36,13 +36,13 @@ export class CollectionFreeFormDocumentView extends DocComponent (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames @@ -154,8 +154,8 @@ export class CollectionFreeFormDocumentView extends DocComponent (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode))); const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so - //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) - targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); + //doc !== targetDoc && (targetDoc.embedContainer = doc.embedContainer); // the computed fields don't see the layout doc -- need to copy the embedContainer to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) + targetDoc.activeFrame = ComputedField.MakeFunction('self.embedContainer?._currentFrame||0'); targetDoc.dataTransition = 'inherit'; }); } @@ -202,7 +202,7 @@ export class CollectionFreeFormDocumentView extends DocComponent() { } render() { - const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]()); + const scaling = Math.min(this.layoutDoc.layout_fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]()); return (
() { SetActiveInkWidth(e.target.value); SelectionManager.Views() .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK) - .map(i => (i.rootDoc.strokeWidth = Number(e.target.value))); + .map(i => (i.rootDoc.stroke_width = Number(e.target.value))); }} />
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index ace388c57..3be2d9a77 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; @@ -26,6 +26,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { // on click, animate slider movement to the targetWidth this._animating = 'all 200ms'; - this.layoutDoc._clipWidth = (targetWidth * 100) / this.props.PanelWidth(); + this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth(); setTimeout( action(() => (this._animating = '')), 200 @@ -71,9 +78,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { - const width = movementX * this.props.ScreenToLocalTransform().Scale + (NumCast(this.layoutDoc._clipWidth) / 100) * this.props.PanelWidth(); + const width = movementX * this.props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this.props.PanelWidth(); if (width && width > 5 && width < this.props.PanelWidth()) { - this.layoutDoc._clipWidth = (width * 100) / this.props.PanelWidth(); + this.layoutDoc[this.clipWidthKey] = (width * 100) / this.props.PanelWidth(); } return false; }; @@ -101,7 +108,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { return (
{ return ( -
this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}> +
this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}> {displayDoc(which)}
); @@ -151,16 +157,16 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent - {displayBox(this.fieldKey === 'data' ? 'compareBox-after' : `${this.fieldKey}2`, 1, this.props.PanelWidth() - 3)} -
- {displayBox(this.fieldKey === 'data' ? 'compareBox-before' : `${this.fieldKey}1`, 0, 0)} + {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)} +
+ {displayBox(`${this.fieldKey}_1`, 0, 0)}
(this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined, + left: `calc(${this.clipWidth + '%'} - 0.5px)`, + cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined, }} onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */ > diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index eb25d3264..5876efb01 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; -import { Cast, NumCast, StrCast } from '../../../../fields/Types'; +import { Cast, CsvCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; import { Docs } from '../../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -29,7 +29,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // @observable private pairs: { [key: string]: FieldResult }[] = []; static pairSet = new ObservableMap(); @computed.struct get pairs() { - return DataVizBox.pairSet.get(StrCast(this.rootDoc.fileUpload)); + return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); } private _chartRenderer: LineChart | undefined; // // another way would be store a schema that defines the type of data we are expecting from an imported doc @@ -61,7 +61,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { @action restoreView = (data: Doc) => { const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView); - const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._dataVizAxes = new List(StrListCast(data.presDataVizAxes))); + const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List(StrListCast(data.presDataVizAxes))); const func = () => this._chartRenderer?.restoreView(data); if (changedView || changedAxes) { setTimeout(func, 100); @@ -88,14 +88,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { }; @computed.struct get axes() { - return StrListCast(this.layoutDoc.dataVizAxes); + return StrListCast(this.layoutDoc.data_vizAxes); } - selectAxes = (axes: string[]) => (this.layoutDoc.dataVizAxes = new List(axes)); + selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List(axes)); @computed get selectView() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; - const margin = { top: 10, right: 25, bottom: 50, left:25}; + const margin = { top: 10, right: 25, bottom: 50, left: 25 }; if (!this.pairs) return 'no data'; // prettier-ignore switch (this.dataVizView) { @@ -113,10 +113,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } fetchData() { - if (DataVizBox.pairSet.has(StrCast(this.rootDoc.fileUpload))) return; - DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), []); + if (DataVizBox.pairSet.has(CsvCast(this.rootDoc[this.fieldKey]).url.href)) return; + DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, []); fetch('/csvData?uri=' + this.dataUrl?.url.href) // - .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), res)))); + .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); } // handle changing the view using a button diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 777bf2f66..11f62a61f 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -56,8 +56,8 @@ export class LineChart extends React.Component { } @computed get incomingLinks() { return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.anchor1 !== this.props.rootDoc) // get links where this chart doc is the target of the link - .map(link => DocCast(link.anchor1)); // then return the source of the link + .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link + .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get incomingSelected() { return this.incomingLinks // all links that are pointing to this node @@ -87,7 +87,7 @@ export class LineChart extends React.Component { { fireImmediately: true } ); this._disposers.annos = reaction( - () => DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), + () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), annotations => { // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way // could be blue colored to make it look like anchor @@ -123,7 +123,7 @@ export class LineChart extends React.Component { element.classList.remove('selected'); } }; - // gets called whenever the "data-annotations" fields gets updated + // gets called whenever the "data_annotations" fields gets updated drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => { // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY @@ -228,15 +228,15 @@ export class LineChart extends React.Component { // creating the x and y scales const xScale = scaleCreatorNumerical(xMin, xMax, 0, width); - const yScale = scaleCreatorNumerical(0, yMax,height, 0); + const yScale = scaleCreatorNumerical(0, yMax, height, 0); // adding svg const margin = this.props.margin; const svg = (this._lineChartSvg = d3 .select(this._lineChartRef.current) .append('svg') - .attr('width', `${width +margin.left + margin.right}`) - .attr('height', `${height + margin.top + margin.bottom }`) + .attr('width', `${width + margin.left + margin.right}`) + .attr('height', `${height + margin.top + margin.bottom}`) .append('g') .attr('transform', `translate(${margin.left}, ${margin.top})`)); diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 0d69ac890..3816bddfa 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -47,17 +47,17 @@ export class TableBox extends React.Component { e => { const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; const targetCreator = (annotationOn: Doc | undefined) => { - const alias = Doc.MakeAlias(this.props.docView?.()!.rootDoc!); - alias._dataVizView = DataVizView.LINECHART; - alias._dataVizAxes = new List([col, col]); - alias.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; - return alias; + const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); + embedding._dataVizView = DataVizView.LINECHART; + embedding._data_vizAxes = new List([col, col]); + embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; + return embedding; }; if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { dragComplete: e => { if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.linkDocument.linkDisplay = true; + e.linkDocument.layout_linkDisplay = true; // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; // e.annoDragData.linkSourceDoc.followLinkZoom = false; } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 76a5ce7b3..348ef910a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -120,15 +120,15 @@ export class DocumentContentsView extends React.Component< select: (ctrl: boolean) => void; NativeDimScaling?: () => number; setHeight?: (height: number) => void; - layoutKey: string; + layout_fieldKey: string; } > { @computed get layout(): string { TraceMobx(); if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString; if (!this.layoutDoc) return '

awaiting layout

'; - if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString()); - const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string'); + if (this.props.layout_fieldKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString()); + const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layout_fieldKey ? this.props.layout_fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string'); if (layout === undefined) return this.props.Document.data ? "" : KeyValueBox.LayoutString(); if (typeof layout === 'string') return layout; return '

Loading layout

'; @@ -140,12 +140,12 @@ export class DocumentContentsView extends React.Component< } get layoutDoc() { // bcz: replaced this with below : is it correct? change was made to accommodate passing fieldKey's from a layout script - // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined); + // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined); const template: Doc = this.props.LayoutTemplate?.() || (this.props.LayoutTemplateString && this.props.Document) || - (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) || - Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined); + (this.props.layout_fieldKey && StrCast(this.props.Document[this.props.layout_fieldKey]) && this.props.Document) || + Doc.Layout(this.props.Document, this.props.layout_fieldKey ? Cast(this.props.Document[this.props.layout_fieldKey], Doc, null) : undefined); return Doc.expandTemplateLayout(template, this.props.Document); } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 47705d53d..d5ca30957 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -52,7 +52,7 @@ export class DocumentLinksButton extends React.Component { if (this.props.View && dropEv.linkDocument) { // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop - !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = 'hyperlink'); + !dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink'); } linkDrag?.end(); }, @@ -142,7 +142,7 @@ export class DocumentLinksButton extends React.Component(this.props.View.allLinks)).forEach(link => { - if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.anchor2 as Doc], filters, []).length || DocUtils.FilterDocs([link.anchor1 as Doc], filters, []).length) { + if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) { results.push(link); } }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ffbde18ca..d8494e58e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -131,7 +131,7 @@ export interface DocComponentView { setData?: (data: Field | Promise) => boolean; componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; incrementalRendering?: () => void; - fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox) + layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox) overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document fieldKey?: string; annotationKey?: string; @@ -150,7 +150,7 @@ export interface DocumentViewSharedProps { Document: Doc; DataDoc?: Doc; contentBounds?: () => undefined | { x: number; y: number; r: number; b: number }; - fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document + fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document suppressSetHeight?: boolean; thumbShown?: () => boolean; setContentView?: (view: DocComponentView) => any; @@ -164,11 +164,11 @@ export interface DocumentViewSharedProps { styleProvider: Opt; setTitleFocus?: () => void; focus: DocFocusFunc; - fitWidth?: (doc: Doc) => boolean | undefined; + layout_fitWidth?: (doc: Doc) => boolean | undefined; docFilters: () => string[]; docRangeFilters: () => string[]; searchFilterDocs: () => Doc[]; - showTitle?: () => string; + layout_showTitle?: () => string; whenChildContentsActiveChanged: (isActive: boolean) => void; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: OpenWhere) => boolean; @@ -275,8 +275,8 @@ export class DocumentViewInternal extends DocComponent; + @computed get layout_showTitle() { + return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.layout_showTitle) as Opt; } @computed get NativeDimScaling() { return this.props.NativeDimScaling?.() || 1; @@ -305,8 +305,8 @@ export class DocumentViewInternal extends DocComponent { - if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title'; + if (!StrCast(this.layoutDoc._layout_showTitle)) this.layoutDoc._layout_showTitle = 'title'; setTimeout(() => this._titleRef.current?.setIsFocused(true)); // use timeout in case title wasn't shown to allow re-render so that titleref will be defined }; @@ -539,7 +539,7 @@ export class DocumentViewInternal extends DocComponent this.props.removeDocument?.(this.props.Document); @undoBatch setToggleDetail = () => (this.Document.onClick = ScriptField.MakeScript( - `toggleDetail(documentView, "${StrCast(this.Document.layoutKey) + `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey) .replace('layout_', '') .replace(/^layout$/, 'detail')}")`, { documentView: 'any' } @@ -611,7 +611,7 @@ export class DocumentViewInternal extends DocComponent { - const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from'); + const portalLink = this.allLinks.find(d => d.link_anchor_1 === this.props.Document && d.link_relationship === 'portal to:portal from'); if (!portalLink) { DocUtils.MakeLink( this.props.Document, - Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }), - { linkRelationship: 'portal to:portal from' } + Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _layout_fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }), + { link_relationship: 'portal to:portal from' } ); } this.Document.followLinkLocation = OpenWhere.lightbox; @@ -699,7 +699,7 @@ export class DocumentViewInternal extends DocComponent item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp })); if (!this.props.Document.isFolder) { - const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); + const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null); const appearance = cm.findByDescription('UI Controls...'); const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : []; !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); @@ -742,7 +742,7 @@ export class DocumentViewInternal extends DocComponent this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) }); + funcs.push({ description: 'Drag an Embedding', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getEmbedding(this.dragFactory)')) }); funcs.push({ description: 'Drag a Copy', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) }); funcs.push({ description: 'Drag Document', icon: 'edit', event: () => (this.layoutDoc.onDragStart = undefined) }); cm.addItem({ description: 'OnDrag...', noexpand: true, subitems: funcs, icon: 'asterisk' }); @@ -910,7 +910,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
@@ -922,7 +922,7 @@ export class DocumentViewInternal extends DocComponent, props: Opt, property: string): any => { // prettier-ignore switch (property.split(':')[0]) { - case StyleProp.ShowTitle: return ''; + case StyleProp.layout_showTitle: return ''; case StyleProp.PointerEvents: return 'none'; case StyleProp.Highlighting: return undefined; } @@ -938,22 +938,22 @@ export class DocumentViewInternal extends DocComponent - Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) || - Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) || - ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) || - ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc)) + Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc) || + Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc) || + ((link.link_anchor_1 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.rootDoc)) || + ((link.link_anchor_2 as Doc)?.unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.rootDoc)) ); } @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); } - hideLink = computedFn((link: Doc) => () => (link.linkDisplay = false)); + hideLink = computedFn((link: Doc) => () => (link.layout_linkDisplay = false)); @computed get allLinkEndpoints() { // the small blue dots that mark the endpoints of links TraceMobx(); - if (this.props.hideLinkAnchors || this.layoutDoc.hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null; - const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.linkDisplay); + if (this.props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this.props.dontRegisterView || this.layoutDoc.unrendered) return null; + const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => d.layout_linkDisplay); return filtered.map(link => (
)); @@ -1046,9 +1046,9 @@ export class DocumentViewInternal extends DocComponent this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1; - const showTitle = this.ShowTitle?.split(':')[0]; - const showTitleHover = this.ShowTitle?.includes(':hover'); - const captionView = !this.showCaption ? null : ( + const layout_showTitle = this.layout_showTitle?.split(':')[0]; + const layout_showTitleHover = this.layout_showTitle?.includes(':hover'); + const captionView = !this.layout_showCaption ? null : (
); - const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; + const targetDoc = layout_showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; const background = StrCast( SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, - Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' + Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' ); - const sidebarWidthPercent = +StrCast(this.layoutDoc.sidebarWidthPercent).replace('%', ''); - const titleView = !showTitle ? null : ( + const layout_sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); + const titleView = !layout_showTitle ? null : (
field.trim()) .map(field => targetDoc[field]?.toString()) @@ -1100,27 +1100,27 @@ export class DocumentViewInternal extends DocComponent { this.props.select(false); - return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle; + return layout_showTitle.split(';').length === 1 ? layout_showTitle + '=' + Field.toString(targetDoc[layout_showTitle.split(';')[0]] as any as Field) : '#' + layout_showTitle; }} SetValue={undoBatch((input: string) => { if (input?.startsWith('#')) { - if (this.props.showTitle) { - this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined; + if (this.props.layout_showTitle) { + this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined; } else { - Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate'; + Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'creationDate'; } } else { - var value = input.replace(new RegExp(showTitle + '='), '') as string | number; - if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value); - if (showTitle.includes('Date') || showTitle === 'author') return true; - Doc.SetInPlace(targetDoc, showTitle, value, true); + var value = input.replace(new RegExp(layout_showTitle + '='), '') as string | number; + if (layout_showTitle !== 'title' && Number(value).toString() === value) value = Number(value); + if (layout_showTitle.includes('Date') || layout_showTitle === 'author') return true; + Doc.SetInPlace(targetDoc, layout_showTitle, value, true); } return true; })} />
); - return this.props.hideTitle || (!showTitle && !this.showCaption) ? ( + return this.props.hideTitle || (!layout_showTitle && !this.layout_showCaption) ? ( this.contents ) : (
@@ -1319,11 +1319,11 @@ export class DocumentView extends React.Component { get LayoutFieldKey() { return this.docView?.LayoutFieldKey || 'layout'; } - @computed get fitWidth() { - return this.docView?._componentView?.fitWidth?.() ?? this.props.fitWidth?.(this.rootDoc) ?? this.layoutDoc?.fitWidth; + @computed get layout_fitWidth() { + return this.docView?._componentView?.layout_fitWidth?.() ?? this.props.layout_fitWidth?.(this.rootDoc) ?? this.layoutDoc?.layout_fitWidth; } @computed get anchorViewDoc() { - return this.props.LayoutTemplateString?.includes('anchor2') ? DocCast(this.rootDoc['anchor2']) : this.props.LayoutTemplateString?.includes('anchor1') ? DocCast(this.rootDoc['anchor1']) : undefined; + return this.props.LayoutTemplateString?.includes('link_anchor_2') ? DocCast(this.rootDoc['link_anchor_2']) : this.props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(this.rootDoc['link_anchor_1']) : undefined; } @computed get hideLinkButton() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.isSelected() ? ':selected' : '')); @@ -1339,13 +1339,13 @@ export class DocumentView extends React.Component { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { - return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth)); + return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); } @computed get nativeHeight() { - return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth)); + return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth)); } @computed get shouldNotScale() { - return (this.fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any); + return (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._viewType as any); } @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width); @@ -1356,8 +1356,8 @@ export class DocumentView extends React.Component { @computed get nativeScaling() { if (this.shouldNotScale) return 1; const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0; - if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) { - return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth + if (this.layout_fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) { + return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or layout_fitWidth } return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled } @@ -1365,7 +1365,7 @@ export class DocumentView extends React.Component { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } @computed get panelHeight() { - if (this.effectiveNativeHeight && (!this.fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) { + if (this.effectiveNativeHeight && (!this.layout_fitWidth || !this.layoutDoc.nativeHeightUnfrozen)) { return Math.min(this.props.PanelHeight(), this.effectiveNativeHeight * this.nativeScaling); } return this.props.PanelHeight(); @@ -1374,7 +1374,10 @@ export class DocumentView extends React.Component { return this.effectiveNativeWidth ? Math.max(0, (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2) : 0; } @computed get Yshift() { - return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && (!this.layoutDoc.nativeHeightUnfrozen || (!this.fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight())) + return this.effectiveNativeWidth && + this.effectiveNativeHeight && + Math.abs(this.Xshift) < 0.001 && + (!this.layoutDoc.nativeHeightUnfrozen || (!this.layout_fitWidth && this.effectiveNativeHeight * this.nativeScaling <= this.props.PanelHeight())) ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; } @@ -1410,10 +1413,10 @@ export class DocumentView extends React.Component { finished?.(); this.docView && (this.docView._animateScaleTime = animTime); }); - const layoutKey = Cast(this.Document.layoutKey, 'string', null); - if (layoutKey !== 'layout_icon') { + const layout_fieldKey = Cast(this.Document.layout_fieldKey, 'string', null); + if (layout_fieldKey !== 'layout_icon') { this.switchViews(true, 'icon', finalFinished); - if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', ''); + if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') this.Document.deiconifyLayout = layout_fieldKey.replace('layout_', ''); } else { const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null); this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finalFinished); @@ -1433,7 +1436,7 @@ export class DocumentView extends React.Component { setTimeout( action(() => { if (useExistingLayout && custom && this.rootDoc['layout_' + view]) { - this.rootDoc.layoutKey = 'layout_' + view; + this.rootDoc.layout_fieldKey = 'layout_' + view; } else { this.setCustomView(custom, view); } @@ -1520,7 +1523,7 @@ export class DocumentView extends React.Component { width: xshift ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`, height: this.props.forceAutoHeight ? undefined - : yshift ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`), + : yshift ?? (this.layout_fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`), }}> Doc.AreProtosEqual(d, otherdoc))) { - const alias = Doc.MakeAlias(otherdoc); - alias.x = wid; - alias.y = 0; - alias._lockedPosition = false; + const embedding = Doc.MakeEmbedding(otherdoc); + embedding.x = wid; + embedding.y = 0; + embedding._lockedPosition = false; wid += otherdoc[WidthSym](); - Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', alias); + Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', embedding); } }); return links; diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index 163c5a9ed..f17ab06e7 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -90,7 +90,7 @@ export class EquationBox extends ViewBoxBaseComponent() { }; render() { TraceMobx(); - const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); + const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); return (
this.updateSize()} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 7eae4e938..85dd779fc 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -50,7 +50,7 @@ export class FieldView extends React.Component { @computed get field(): FieldResult { - const { Document, fieldKey } = this.props; + const { Document, fieldKey: fieldKey } = this.props; return Document[fieldKey]; } render() { diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index b43e359ff..db72209ab 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -90,7 +90,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc); } - // if (this.autoHeight) this.tryUpdateScrollHeight(); + // if (this.layout_autoHeight) this.tryUpdateScrollHeight(); }; @computed get theGraph() { return
r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index c9be10d3a..7d1d50cc7 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -79,9 +79,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent ({ forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes, - scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1), + scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._freeform_scale, 1), selected: this.props.isSelected(), }), ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'), @@ -116,7 +116,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.layoutDoc._scrollTop, + () => this.layoutDoc.layout_scrollTop, s_top => { this._forcedScroll = true; !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top)); @@ -144,7 +144,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop); - this.rootDoc[this.fieldKey + '-usePath'] = 'alternate:hover'; + this.rootDoc[this.fieldKey + '_usePath'] = 'alternate:hover'; e.stopPropagation(); }) ); @@ -167,28 +167,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1); + const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._freeform_scale, 1); const nscale = NumCast(this.props.PanelWidth()) / scaling; - const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']); - const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']); - this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh; - this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh; - this.rootDoc._panX = nh * NumCast(this.rootDoc._panX); - this.rootDoc._panY = nh * NumCast(this.rootDoc._panY); - this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined; - this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined; - this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined; - this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined; + const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']); + const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); + this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nh; + this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nh; + this.rootDoc._freeform_panX = nh * NumCast(this.rootDoc._freeform_panX); + this.rootDoc._freeform_panY = nh * NumCast(this.rootDoc._freeform_panY); + this.dataDoc._freeform_panXMax = this.dataDoc._freeform_panXMax ? nh * NumCast(this.dataDoc._freeform_panXMax) : undefined; + this.dataDoc._freeform_panXMin = this.dataDoc._freeform_panXMin ? nh * NumCast(this.dataDoc._freeform_panXMin) : undefined; + this.dataDoc._freeform_panYMax = this.dataDoc._freeform_panYMax ? nw * NumCast(this.dataDoc._freeform_panYMax) : undefined; + this.dataDoc._freeform_panYMin = this.dataDoc._freeform_panYMin ? nw * NumCast(this.dataDoc._freeform_panYMin) : undefined; }); @undoBatch rotate = action(() => { - const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']); - const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']); + const nw = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); + const nh = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']); const w = this.layoutDoc._width; const h = this.layoutDoc._height; this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360; - this.dataDoc[this.fieldKey + '-nativeWidth'] = nh; - this.dataDoc[this.fieldKey + '-nativeHeight'] = nw; + this.dataDoc[this.fieldKey + '_nativeWidth'] = nh; + this.dataDoc[this.fieldKey + '_nativeHeight'] = nw; this.layoutDoc._width = h; this.layoutDoc._height = w; }); @@ -204,7 +204,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent; }; - getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined); + getScrollHeight = () => (this.props.layout_fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._freeform_scale, 1) === NumCast(this.rootDoc._freeform_scaleMin, 1) ? this.nativeSize.nativeHeight : undefined); @computed private get considerDownloadIcon() { @@ -358,13 +358,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}-usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} + onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))} style={{ display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none', width: 'min(10%, 25px)', @@ -431,7 +431,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}> @@ -459,9 +459,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent; } - screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale); + screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop) * this.props.ScreenToLocalTransform().Scale); marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._freeform_scale, 1) <= NumCast(this.rootDoc.freeform_scaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, @@ -497,9 +497,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent { if (!this._forcedScroll) { - if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) { + if (this.layoutDoc._layout_scrollTop || this._mainCont.current?.scrollTop) { this._ignoreScroll = true; - this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop; + this.layoutDoc._layout_scrollTop = this._mainCont.current?.scrollTop; this._ignoreScroll = false; } } @@ -510,7 +510,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { } reverseNativeScaling = returnTrue; able = returnAlways; - fitWidth = returnTrue; + layout_fitWidth = returnTrue; overridePointerEvents = returnAll; onClickScriptDisable = returnAlways; @observable private rows: KeyValuePair[] = []; + @observable _splitPercentage = 50; - @computed get splitPercentage() { - return NumCast(this.props.Document.schemaSplitPercentage, 50); - } get fieldDocToLayout() { return this.props.fieldKey ? DocCast(this.props.Document[this.props.fieldKey], DocCast(this.props.Document)) : this.props.Document; } @@ -147,7 +145,9 @@ export class KeyValueBox extends React.Component { const rows: JSX.Element[] = []; let i = 0; const self = this; - for (const key of Object.keys(ids).slice().sort()) { + const keys = Object.keys(ids).slice(); + //for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) { + for (const key of keys.sort()) { rows.push( { if (el) self.rows.push(el); }; })()} - keyWidth={100 - this.splitPercentage} + keyWidth={100 - this._splitPercentage} rowStyle={'keyValueBox-' + (i++ % 2 ? 'oddRow' : 'evenRow')} key={key} keyName={key} @@ -180,7 +180,7 @@ export class KeyValueBox extends React.Component { this._keyInput.current!.select(); e.stopPropagation(); }} - style={{ width: `${100 - this.splitPercentage}%` }}> + style={{ width: `${100 - this._splitPercentage}%` }}> { this._valInput.current!.select(); e.stopPropagation(); }} - style={{ width: `${this.splitPercentage}%` }}> + style={{ width: `${this._splitPercentage}%` }}> @@ -199,7 +199,7 @@ export class KeyValueBox extends React.Component { @action onDividerMove = (e: PointerEvent): void => { const nativeWidth = this._mainCont.current!.getBoundingClientRect(); - this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100)); + this._splitPercentage = Math.max(0, 100 - Math.round(((e.clientX - nativeWidth.left) / nativeWidth.width) * 100)); }; @action onDividerUp = (e: PointerEvent): void => { @@ -216,7 +216,7 @@ export class KeyValueBox extends React.Component { getFieldView = async () => { const rows = this.rows.filter(row => row.isChecked); if (rows.length > 1) { - const parent = Docs.Create.StackingDocument([], { _autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true }); + const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true }); for (const row of rows) { const field = this.createFieldView(DocCast(this.props.Document.data), row); field && Doc.AddDocToList(parent, 'data', field); @@ -229,9 +229,9 @@ export class KeyValueBox extends React.Component { createFieldView = (templateDoc: Doc, row: KeyValuePair) => { const metaKey = row.props.keyName; - const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeAlias(templateDoc); + const fieldTemplate = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc); fieldTemplate.title = metaKey; - fieldTemplate.fitWidth = true; + fieldTemplate.layout_fitWidth = true; fieldTemplate._xMargin = 10; fieldTemplate._yMargin = 10; fieldTemplate._width = 100; @@ -284,8 +284,8 @@ export class KeyValueBox extends React.Component { render() { const dividerDragger = - this.splitPercentage === 0 ? null : ( -
+ this._splitPercentage === 0 ? null : ( +
); @@ -295,10 +295,10 @@ export class KeyValueBox extends React.Component { - - diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index 57d36932e..2b4867321 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -19,6 +19,7 @@ position: relative; margin: 0; } + .keyValuePair-keyFieldMod, .keyValuePair-keyField { width: 100%; margin-left: 20px; @@ -27,6 +28,9 @@ overflow: auto; display: inline; } + .keyValuePair-keyFieldMod { + margin-left: 35px; + } } } .keyValuePair-td-value { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 85641124f..509bb667e 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -66,7 +66,7 @@ export class KeyValuePair extends React.Component { isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, - dropAction: 'alias', + dropAction: 'embed', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, @@ -108,8 +108,8 @@ export class KeyValuePair extends React.Component { })}> X - -
+ +
{'('.repeat(parenCount)} {props.fieldKey} {')'.repeat(parenCount)} diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 916458dfd..32026ea9c 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -37,7 +37,7 @@ export class LabelBox extends ViewBoxBaseComponent { @@ -82,7 +82,7 @@ export class LabelBox extends ViewBoxBaseComponent { + fitTextToBox = (r: any) => { const singleLine = BoolCast(this.rootDoc._singleLine, true); const params = { rotateText: null, @@ -141,9 +141,9 @@ export class LabelBox extends ViewBoxBaseComponent - this.fitTextToBox(r))}> + this.fitTextToBox(r))}> {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')}
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 31f1775e5..f38ef634c 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -46,14 +46,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); if (separation > 100) { const dragData = new DragManager.DocumentDragData([this.rootDoc]); - dragData.dropAction = 'alias'; - dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'onClick']; + dragData.dropAction = 'embed'; + dragData.removeDropProperties = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); return true; } else { this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100; this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100; - this.rootDoc.linkAutoMove = false; + this.rootDoc.layout_autoMoveAnchors = false; } } return false; @@ -67,10 +67,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100); const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100); const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor'); - const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1'; - const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25; + const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1'; + const anchorScale = !this.dataDoc[this.fieldKey + '_useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25; const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title); - const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor1') ? 'anchor1' : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('anchor2') ? 'anchor2' : ''; + const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1') + ? 'link_anchor_1' + : SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_2') + ? 'link_anchor_2' + : ''; return (
() { - public static LayoutString(fieldKey: string) { + public static LayoutString(fieldKey: string = 'link') { return FieldView.LayoutString(LinkBox, fieldKey); } @@ -23,7 +23,7 @@ export class LinkBox extends ViewBoxBaseComponent() {
{ onDismiss = (add: boolean) => { LinkDescriptionPopup.descriptionPopup = false; if (add) { - LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).description = this.description); + LinkManager.currentLink && (Doc.GetProto(LinkManager.currentLink).link_description = this.description); } }; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index c58b5dd8c..c6172ee01 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -58,13 +58,13 @@ export class LinkDocPreview extends React.Component { var linkTarget = this.props.linkDoc; this._linkSrc = this.props.linkSrc; this._linkDoc = this.props.linkDoc; - const anchor1 = this._linkDoc?.anchor1 as Doc; - const anchor2 = this._linkDoc?.anchor2 as Doc; - if (anchor1 && anchor2) { - linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1; + const link_anchor_1 = this._linkDoc?.link_anchor_1 as Doc; + const link_anchor_2 = this._linkDoc?.link_anchor_2 as Doc; + if (link_anchor_1 && link_anchor_2) { + linkTarget = Doc.AreProtosEqual(link_anchor_1, this._linkSrc) || Doc.AreProtosEqual(link_anchor_1?.annotationOn as Doc, this._linkSrc) ? link_anchor_2 : link_anchor_1; } if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { - // want to show annotation context document if annotation is not text + // want to show annotation embedContainer document if annotation is not text linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._markerTargetDoc = this._targetDoc = anno))); } else { this._markerTargetDoc = this._targetDoc = linkTarget; @@ -111,7 +111,7 @@ export class LinkDocPreview extends React.Component { action(anchor => { if (anchor instanceof Doc && LinkManager.Links(anchor).length) { this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; - const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; + const automaticLink = this._linkDoc.link_relationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source const linkTarget = anchor; @@ -175,7 +175,7 @@ export class LinkDocPreview extends React.Component { } else if (this.props.hrefs?.length) { const webDoc = Array.from(SearchBox.staticSearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ?? - Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }); + Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true }); this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox); } }; @@ -208,7 +208,7 @@ export class LinkDocPreview extends React.Component {
{StrCast(this._markerTargetDoc.title).length > 16 ? StrCast(this._markerTargetDoc.title).substr(0, 16) + '...' : StrCast(this._markerTargetDoc.title)} -

{StrCast(this._linkDoc.description)}

+

{StrCast(this._linkDoc.link_description)}

Next Link
} placement="top"> @@ -263,7 +263,7 @@ export class LinkDocPreview extends React.Component { isDocumentActive={returnFalse} isContentActive={returnFalse} addDocument={returnFalse} - showTitle={returnEmptyString} + layout_showTitle={returnEmptyString} removeDocument={returnFalse} addDocTab={returnFalse} pinToPres={returnFalse} @@ -278,7 +278,7 @@ export class LinkDocPreview extends React.Component { pointerEvents={returnNone} focus={emptyFunction} whenChildContentsActiveChanged={returnFalse} - ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size. + ignoreAutoHeight={true} // need to ignore layout_autoHeight otherwise layout_autoHeight text boxes will expand beyond the preview panel size. bringToFront={returnFalse} NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined} NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined} diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 36be7d257..9b0fddce4 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -98,7 +98,7 @@ export class MapBox extends ViewBoxAnnotatableComponent void); @computed get inlineTextAnnotations() { @@ -126,7 +126,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } }, 250); // listener to addmarker event @@ -282,7 +282,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } this.dataDoc.mapLat = this._map.getCenter()?.lat(); this.dataDoc.mapLng = this._map.getCenter()?.lng(); @@ -292,7 +292,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } this.dataDoc.mapZoom = this._map.getZoom(); }; @@ -327,7 +327,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { console.log('print all sidebar Docs'); - if (!this.layoutDoc._showSidebar) this.toggleSidebar(); + if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; docs.forEach(doc => { if (doc.lat !== undefined && doc.lng !== undefined) { @@ -351,7 +351,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (this.layoutDoc._showSidebar) this.toggleSidebar(); + if (this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; return this.removeDocument(doc, sidebarKey); }; @@ -375,11 +375,11 @@ export class MapBox extends ViewBoxAnnotatableComponent 0) { this._showSidebar = true; this.layoutDoc._width = fullWidth + localDelta[0]; - this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; } else { this._showSidebar = false; this.layoutDoc._width = mapWidth; - this.layoutDoc._sidebarWidthPercent = '0%'; + this.layoutDoc._layout_sidebarWidthPercent = '0%'; } return false; }), @@ -388,12 +388,12 @@ export class MapBox extends ViewBoxAnnotatableComponent (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); - @computed get sidebarWidthPercent() { - return StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); + sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + @computed get layout_sidebarWidthPercent() { + return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } @computed get sidebarColor() { - return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4')); + return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4')); } /** @@ -453,7 +453,7 @@ export class MapBox extends ViewBoxAnnotatableComponent @@ -467,8 +467,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { //1.2 * w * ? = .2 * w .2/1.2 const prevWidth = this.sidebarWidth(); - this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; - this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; + this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; sidebarDown = (e: React.PointerEvent) => { @@ -476,8 +476,8 @@ export class MapBox extends ViewBoxAnnotatableComponent { const bounds = this._ref.current!.getBoundingClientRect(); - this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; - this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%'; + this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; + this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%'; e.preventDefault(); return false; }; @@ -548,7 +548,7 @@ export class MapBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); + scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; infoWidth = () => this.props.PanelWidth() / 5; @@ -616,7 +616,7 @@ export class MapBox extends ViewBoxAnnotatableComponent + style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} @@ -667,7 +667,7 @@ export class MapBox extends ViewBoxAnnotatableComponent {/* */} -
+
void); @computed get inlineTextAnnotations() { @@ -123,7 +123,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } }, 250); // listener to addmarker event @@ -279,7 +279,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } this.dataDoc.mapLat = this._map.getCenter()?.lat(); this.dataDoc.mapLng = this._map.getCenter()?.lng(); @@ -289,7 +289,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { if (this._loadPending && this._map.getBounds()) { this._loadPending = false; - this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } this.dataDoc.mapZoom = this._map.getZoom(); }; @@ -324,7 +324,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { console.log('print all sidebar Docs'); - if (!this.layoutDoc._showSidebar) this.toggleSidebar(); + if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; docs.forEach(doc => { if (doc.lat !== undefined && doc.lng !== undefined) { @@ -348,7 +348,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { - if (this.layoutDoc._showSidebar) this.toggleSidebar(); + if (this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; return this.removeDocument(doc, sidebarKey); }; @@ -372,11 +372,11 @@ export class MapBox2 extends ViewBoxAnnotatableComponent 0) { this._showSidebar = true; this.layoutDoc._width = fullWidth + localDelta[0]; - this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; } else { this._showSidebar = false; this.layoutDoc._width = mapWidth; - this.layoutDoc._sidebarWidthPercent = '0%'; + this.layoutDoc._layout_sidebarWidthPercent = '0%'; } return false; }), @@ -385,12 +385,12 @@ export class MapBox2 extends ViewBoxAnnotatableComponent (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); - @computed get sidebarWidthPercent() { - return StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); + sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + @computed get layout_sidebarWidthPercent() { + return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } @computed get sidebarColor() { - return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4')); + return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4')); } /** @@ -450,7 +450,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent @@ -464,8 +464,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { //1.2 * w * ? = .2 * w .2/1.2 const prevWidth = this.sidebarWidth(); - this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; - this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; + this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; sidebarDown = (e: React.PointerEvent) => { @@ -473,8 +473,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { const bounds = this._ref.current!.getBoundingClientRect(); - this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; - this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%'; + this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; + this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%'; e.preventDefault(); return false; }; @@ -545,7 +545,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); + scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; infoWidth = () => this.props.PanelWidth() / 5; @@ -566,7 +566,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent { e.button === 0 && !e.ctrlKey && e.stopPropagation(); }} - style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> + style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{renderAnnotations(this.transparentFilter)}
{renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} @@ -616,7 +616,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent {/* */} -
+
{ setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => { - const newBox = Docs.Create.TextDocument('Note', { _autoHeight: true }); + const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true }); 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 Doc.AddDocToList(this.props.place, 'data', newBox); this._stack?.scrollToBottom(); @@ -42,7 +42,7 @@ export class MapBoxInfoWindow extends React.Component doc.type === DocumentType.RTF; + childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF; addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean); removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean); render() { @@ -69,7 +69,7 @@ export class MapBoxInfoWindow extends React.Component (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href))); else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf))); @@ -122,15 +122,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent { @@ -173,15 +173,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent { setTimeout(() => { this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc['icon-nativeWidth'] = nativeWidth; - this.dataDoc['icon-nativeHeight'] = nativeHeight; + this.dataDoc['icon_nativeWidth'] = nativeWidth; + this.dataDoc['icon_nativeHeight'] = nativeHeight; }, 500); } ); @@ -201,13 +201,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.rootDoc.scrollTop, + () => this.rootDoc.layout_scrollTop, () => { - if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '-panY'])) instanceof ComputedField)) { - this.props.Document[this.SidebarKey + '-panY'] = ComputedField.MakeFunction('this.scrollTop'); + if (!(ComputedField.WithoutComputed(() => FieldValue(this.props.Document[this.SidebarKey + '_panY'])) instanceof ComputedField)) { + this.props.Document[this.SidebarKey + '_panY'] = ComputedField.MakeFunction('this.layout_scrollTop'); } - this.props.Document[this.SidebarKey + '-viewScale'] = 1; - this.props.Document[this.SidebarKey + '-panX'] = 0; + this.props.Document[this.SidebarKey + '_freeform_scale'] = 1; + this.props.Document[this.SidebarKey + '_freeform_panX'] = 0; } ); } @@ -215,7 +215,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.brushView(view); sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { - if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + if (DocListCast(this.props.Document[this.props.fieldKey + '_sidebar']).includes(doc) && !this.SidebarShown) { this.toggleSidebar(false); return true; } @@ -239,7 +239,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { const anchor = Docs.Create.TextanchorDocument({ - title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)), + title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)), unrendered: true, annotationOn: this.rootDoc, }); @@ -258,11 +258,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - this.dataDoc[this.props.fieldKey + '-numPages'] = np; + this.dataDoc[this.props.fieldKey + '_numPages'] = np; Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72)); Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72); this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1); - !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); + !this.Document._layout_fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); }; public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { @@ -279,14 +279,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.prevAnnotation(); public nextAnnotation = () => this._pdfViewer?.nextAnnotation(); public backPage = () => { - this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1); + this.Document._layout_curPage = Math.max(1, (NumCast(this.Document._layout_curPage) || 1) - 1); return true; }; public forwardPage = () => { - this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '-numPages']), (NumCast(this.Document._curPage) || 1) + 1); + this.Document._layout_curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '_numPages']), (NumCast(this.Document._layout_curPage) || 1) + 1); return true; }; - public gotoPage = (p: number) => (this.Document._curPage = p); + public gotoPage = (p: number) => (this.Document._layout_curPage = p); @undoBatch onKeyDown = action((e: KeyboardEvent) => { @@ -316,9 +316,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent) => (this._searchString = e.currentTarget.value); // adding external documents; to sidebar key - // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"-annotation") + // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"_annotation") sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => { - if (!this.layoutDoc._showSidebar) this.toggleSidebar(); + if (!this.layoutDoc._show_sidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); }; sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { @@ -332,13 +332,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]); - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + this.layoutDoc._show_sidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; }, @@ -352,7 +352,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent = undefined; @observable _previewWidth: Opt = undefined; toggleSidebar = action((preview: boolean = false) => { - const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); + const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); @@ -363,7 +363,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent ); const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`; - const curPage = NumCast(this.Document._curPage) || 1; + const curPage = NumCast(this.Document._layout_curPage) || 1; return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
99 ? 4 : 3}ch`, pointerEvents: 'all' }} - onChange={e => (this.Document._curPage = Number(e.currentTarget.value))} + onChange={e => (this.Document._layout_curPage = Number(e.currentTarget.value))} onKeyDown={e => e.stopPropagation()} onClick={action(() => (this._pageControls = !this._pageControls))} /> @@ -470,7 +470,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._sidebarRef.current?.anchorMenuClick; @observable _showSidebar = false; @computed get SidebarShown() { - return this._showSidebar || this.layoutDoc._showSidebar ? true : false; + return this._showSidebar || this.layoutDoc._show_sidebar ? true : false; } @computed get sidebarHandle() { return ( @@ -480,7 +480,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.sidebarBtnDown(e, true)}> @@ -490,10 +490,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined, + height: this.props.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined, }}>
this.sidebarBtnDown(e, false)} />
{ this.result = info; this.dataDoc.type = DocumentType.VID; - this.dataDoc[this.fieldKey + '-duration'] = this.videoDuration; + this.dataDoc[this.fieldKey + '_duration'] = this.videoDuration; this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey); this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.client); diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 75e61bbeb..7bf765042 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -129,7 +129,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { - const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); + const startTime = Cast(this.layoutDoc._layout_currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc; }; @@ -236,13 +236,13 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { if (this.dataDoc[this.fieldKey + '-dictation']) return; const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height)); - dictationText._autoHeight = false; + dictationText._layout_autoHeight = false; const dictationTextProto = Doc.GetProto(dictationText); dictationTextProto.recordingSource = this.dataDoc; dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`); dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState'); this.dataDoc[this.fieldKey + '-dictation'] = dictationText; }; - videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth(); + videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth(); formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight()); render() { TraceMobx(); diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index f09962b22..37fda14fc 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -167,7 +167,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { - this.rootDoc.layoutKey = 'layout'; + this.rootDoc.layout_fieldKey = 'layout'; }; // displays error message @@ -702,7 +702,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent - {!StrCast(this.rootDoc.layoutKey).startsWith('layout_on') ? null : ( + {!StrCast(this.rootDoc.layout_fieldKey).startsWith('layout_on') ? null : (
+ Key + Fields