diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 11 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/components/TableBox.tsx | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 158 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 5 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 12 |
6 files changed, 117 insertions, 71 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 034a38e9c..ee67dd305 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -20,6 +20,11 @@ import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldViewProps } from './FieldView'; import { OpenWhere } from './OpenWhere'; +export enum GroupActive { // flags for whether a view is activate because of its relationship to a group + group = 'group', // this is a group that is activated because it's on an active canvas, but is not part of some other group + child = 'child', // this is a group child that is activated because its containing group is activated + inactive = 'inactive', // this is a group child but it is not active +} /// Ugh, typescript has no run-time way of iterating through the keys of an interface. so we need /// manaully keep this list of keys in synch wih the fields of the freeFormProps interface const freeFormPropsKeys = ['x', 'y', 'z', 'zIndex', 'rotation', 'opacity', 'backgroundColor', 'color', 'highlight', 'width', 'height', 'autoDim', 'transition']; @@ -274,7 +279,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF if (this._props.isAnyChildContentActive()) return undefined; const backColor = this.BackgroundColor; const isGroup = this.dataDoc.isGroup && (!backColor || backColor === 'transparent'); - return isGroup ? (this._props.isDocumentActive?.() ? 'group' : this._props.isGroupActive?.() ? 'child' : 'inactive') : this._props.isGroupActive?.() ? 'child' : undefined; + return isGroup ? (this._props.isDocumentActive?.() ? GroupActive.group : + this._props.isGroupActive?.() ? GroupActive.child : GroupActive.inactive) : + this._props.isGroupActive?.() ? GroupActive.child : undefined; // prettier-ignore }; localRotation = () => this._props.rotation; render() { @@ -313,6 +320,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF } } // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) { +ScriptingGlobals.add(function gotoFrame(doc: Doc, newFrame: number) { CollectionFreeFormDocumentView.gotoKeyFrame(doc, newFrame); }); diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index a1deb1625..d2e82284e 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -18,6 +18,7 @@ import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import './Chart.scss'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const { DATA_VIZ_TABLE_ROW_HEIGHT } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore interface TableBoxProps { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9ff96c692..f09e0c73d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -43,6 +43,7 @@ import { ObserverJsxParser } from '../ObservableReactComponent'; import { PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { ViewBoxInterface } from '../ViewBoxInterface'; +import { GroupActive } from './CollectionFreeFormDocumentView'; import { DocumentContentsView } from './DocumentContentsView'; import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; @@ -52,7 +53,7 @@ import { OpenWhere, OpenWhereMod } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails/PresEnums'; import SpringAnimation from './trails/SlideEffect'; -import { SpringSettings, SpringType, springMappings } from './trails/SpringUtils'; +import { SpringType, springMappings } from './trails/SpringUtils'; interface Window { MediaRecorder: MediaRecorder; @@ -153,7 +154,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document @computed get disableClickScriptFunc() { const onScriptDisable = this._props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable; - return (DocumentView.LongPress || + return (SnappingManager.LongPress || onScriptDisable === 'always' || (onScriptDisable !== 'never' && (this.rootSelected() || this._componentView?.isAnyChildContentActive?.()))); // prettier-ignore } @@ -297,17 +298,15 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document e.stopPropagation(); }; onClick = action((e: React.MouseEvent | React.PointerEvent) => { - if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return; - const documentView = this._docView; - if (documentView && !this.Document.ignoreClick && this._props.renderDepth >= 0 && ClientUtils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { + if (this._props.isGroupActive?.() === GroupActive.child && !this._props.isDocumentActive?.()) return; + if (this._docView && !this.Document.ignoreClick && this._props.renderDepth >= 0 && ClientUtils.isClick(e.clientX, e.clientY, this._downX, this._downY, this._downTime)) { let stopPropagate = true; let preventDefault = true; - !this.layoutDoc._keepZWhenDragged && this._props.bringToFront?.(this.Document); const scriptProps = { this: this.Document, _readOnly_: false, scriptContext: this._props.scriptContext, - documentView, + documentView: this._docView, clientX: e.clientX, clientY: e.clientY, shiftKey: e.shiftKey, @@ -317,44 +316,39 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document }; if (this._doubleTap) { const defaultDblclick = this._props.defaultDoubleClick?.() || this.Document.defaultDoubleClick; - if (this.onDoubleClickHdlr?.script) { - UndoManager.RunInBatch(() => this.onDoubleClickHdlr.script.run(scriptProps, console.log).result?.select && this._props.select(false), 'on double click: ' + this.Document.title); - } else if (!Doc.IsSystem(this.Document) && defaultDblclick !== 'ignore') { - UndoManager.RunInBatch(() => this._props.addDocTab(this.Document, OpenWhere.lightboxAlways), 'double tap'); - DocumentView.DeselectAll(); - Doc.UnBrushDoc(this.Document); - } else { - this._singleClickFunc?.(); - } + undoable(() => { + if (this.onDoubleClickHdlr?.script) { + this.onDoubleClickHdlr.script.run(scriptProps, console.log).result?.select && this._props.select(false); + } else if (!Doc.IsSystem(this.Document) && defaultDblclick !== 'ignore') { + this._props.addDocTab(this.Document, OpenWhere.lightboxAlways); + DocumentView.DeselectAll(); + Doc.UnBrushDoc(this.Document); + } else this._singleClickFunc?.(); + }, 'on double click: ' + this.Document.title)(); this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout); this._doubleClickTimeout = undefined; this._singleClickFunc = undefined; } else { - let clickFunc: undefined | (() => any); - if (!this.disableClickScriptFunc && this.onClickHdlr?.script) { - clickFunc = undoable(() => { - this.onClickHdlr?.script.run(scriptProps, console.log).result?.select && this._props.select(false); - }, 'click ' + this.Document.title); - } 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 && !(e.ctrlKey || e.button > 0)) { - stopPropagate = false; - } - preventDefault = false; - } - const sendToBack = e.altKey ? () => documentView._props.bringToFront?.(this.Document, true) : undefined; + const sendToBack = e.altKey ? () => this._props.bringToFront?.(this.Document, true) : undefined; const selectFunc = () => { + !this.layoutDoc._keepZWhenDragged && this._props.bringToFront?.(this.Document); // selecting a view that is part of a template proxies the selection back to the root of the template const templateRoot = !(e.ctrlKey || e.button > 0) && this._props.docViewPath?.().reverse().find(dv => !dv._props.TemplateDataDocument); // prettier-ignore (templateRoot || this._docView)?.select(e.ctrlKey || e.shiftKey, e.metaKey); }; - this._singleClickFunc = clickFunc ?? sendToBack ?? selectFunc; - const waitFordblclick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick; - if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') { + const clickFunc = this.onClickFunc?.()?.script ? () => (this.onClickFunc?.()?.script.run(scriptProps, console.log).result as Opt<{ select: boolean }>)?.select && this._props.select(false) : undefined; + if (!clickFunc) { + // 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 && !(e.ctrlKey || e.button > 0)) stopPropagate = false; + preventDefault = false; + } + this._singleClickFunc = undoable(clickFunc ?? sendToBack ?? selectFunc, 'click: ' + this.Document.title); + const waitForDblClick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick; + if ((clickFunc && waitForDblClick !== 'never') || waitForDblClick === 'always') { this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout); this._doubleClickTimeout = setTimeout(this._singleClickFunc, 300); // eslint-disable-next-line no-use-before-define - } else if (!DocumentView.LongPress) { + } else if (!SnappingManager.LongPress) { this._singleClickFunc(); this._singleClickFunc = undefined; } @@ -365,9 +359,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document }); onPointerDown = (e: React.PointerEvent): void => { - if (this._props.isGroupActive?.() === 'child' && !this._props.isDocumentActive?.()) return; + if (this._props.isGroupActive?.() === GroupActive.child && !this._props.isDocumentActive?.()) return; // eslint-disable-next-line no-use-before-define - this._longPressSelector = setTimeout(() => DocumentView.LongPress && this._props.select(false), 1000); + this._longPressSelector = setTimeout(() => SnappingManager.LongPress && this._props.select(false), 1000); if (!DocumentView.DownDocView) DocumentView.DownDocView = this._docView; this._downX = e.clientX; @@ -418,7 +412,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document if (!this.isContentActive()) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected } // eslint-disable-next-line no-use-before-define - if (DocumentView.LongPress) e.preventDefault(); + if (SnappingManager.LongPress) e.preventDefault(); }; toggleFollowLink = undoable((): void => { @@ -975,8 +969,8 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document * @returns a function that will wrap a JSX animation element wrapping any JSX element */ public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Opt<Doc>, root: Doc) { - let dir = (presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) as PresEffectDirection; - const transitionTime = presEffectDoc?.presentation_transition ? NumCast(presEffectDoc?.presentation_transition) : 500; + const dir = ((presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) || PresEffectDirection.Center) as PresEffectDirection; + const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)); const effectProps = { left: dir === PresEffectDirection.Left, right: dir === PresEffectDirection.Right, @@ -984,26 +978,14 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document bottom: dir === PresEffectDirection.Bottom, opposite: true, delay: 0, - duration: Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)), + duration, }; const timing = StrCast(presEffectDoc?.presentation_effectTiming); - let timingConfig: SpringSettings | undefined; - if (timing) { - timingConfig = JSON.parse(timing); - } - - if (!timingConfig) { - timingConfig = { - type: SpringType.GENTLE, - ...springMappings.gentle, - }; - } - - if (!dir) { - dir = PresEffectDirection.Center; - } - + const timingConfig = (timing ? JSON.parse(timing) : undefined) ?? { + type: SpringType.GENTLE, + ...springMappings.gentle, + }; switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) { case PresEffect.Expand: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Expand} springSettings={timingConfig}>{renderDoc}</SpringAnimation> case PresEffect.Flip: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Flip} springSettings={timingConfig}>{renderDoc}</SpringAnimation> @@ -1085,15 +1067,16 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done. ) => Promise<void>; public static linkCommonAncestor: (link: Doc) => DocumentView | undefined; - // pin func + /** + * Pins a Doc to the current presentation trail. (see TabDocView for implementation) + */ public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void; - // gesture - public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to. - // media playing - @observable public static CurrentlyPlaying: DocumentView[] = []; + /** + * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event). Used by GestureOverlay to determine the doc a gesture should apply to. + */ + public static DownDocView: DocumentView | undefined; public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore - public ContentRef = React.createRef<HTMLDivElement>(); private _htmlOverlayEffect: Opt<Doc>; private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewTimer: NodeJS.Timeout | undefined; @@ -1124,7 +1107,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { @observable private _htmlOverlayText: Opt<string> = undefined; @observable private _isHovering = false; @observable private _selected = false; - @observable public static LongPress = false; + @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing @computed private get shouldNotScale() { return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.(); @@ -1471,7 +1454,6 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { {!this.Document || !this._props.PanelWidth() ? null : ( <div className="contentFittingDocumentView-previewDoc" - ref={this.ContentRef} style={{ transform: `translate(${this.centeringX}px, ${this.centeringY}px)`, width: xshift ?? `${this._props.PanelWidth() - this.Xshift * 2}px`, @@ -1568,6 +1550,56 @@ export class DocumentView extends DocComponent<DocumentViewProps>() { } } +export function ActiveFillColor(): string { + const dv = DocumentView.Selected().lastElement() ?.Document._layout_isSvg ? DocumentView.Selected().lastElement() : undefined; + return StrCast(dv?.Document.fillColor, StrCast(ActiveInkPen()?.activeFillColor, "")); +} // prettier-ignore +export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore +export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore +export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore +export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore +export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore +export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore +export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore +export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore +export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore +export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore +export function ActiveEraserWidth(): number { return Number(ActiveInkPen()?.eraserWidth); } // prettier-ignore + +export function SetActiveInkWidth(width: string): void { + !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); +} +export function SetActiveBezierApprox(bezier: string): void { + ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); +} +export function SetActiveInkColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeInkColor = value); +} +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} +export function SetActiveInkHideTextLabels(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); +} +export function SetActiveFillColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeFillColor = value); +} +export function SetActiveArrowStart(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); +} +export function SetActiveArrowEnd(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); +} +export function SetActiveArrowScale(value: number) { + ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); +} +export function SetActiveDash(dash: string): void { + !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); +} +export function SetEraserWidth(width: number): void { + ActiveInkPen() && (ActiveInkPen().eraserWidth = width); +} + // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function DocFocusOrOpen(docIn: Doc, optionsIn?: FocusViewOptions, containingDoc?: Doc) { return DocumentView.FocusOrOpen(docIn, optionsIn, containingDoc); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 1c90fae9e..d940a96b9 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -454,6 +454,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; focus = (anchor: Doc, options: FocusViewOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options)); + renderedPixelDimensions = async () => { + const { nativeWidth: width, nativeHeight: height } = await Networking.PostToServer('/inspectImage', { source: this.paths[0] }); + return { width, height }; + }; + savedAnnotations = () => this._savedAnnotations; render() { TraceMobx(); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8835ea5e7..da947face 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -45,6 +45,7 @@ import { LinkInfo } from './LinkDocPreview'; import { OpenWhere } from './OpenWhere'; import './WebBox.scss'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const { CreateImage } = require('./WebBoxRenderer'); @observer diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9f2a9b8e1..5b435e44a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -87,9 +87,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch when typing a new text note into a collection static _globalHighlightsCache: string = ''; static _globalHighlights = new ObservableSet<string>(['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']); - static _highlightStyleSheet: any = addStyleSheet(); - static _bulletStyleSheet: any = addStyleSheet(); - static _userStyleSheet: any = addStyleSheet(); + static _highlightStyleSheet = addStyleSheet(); + static _bulletStyleSheet = addStyleSheet(); + static _userStyleSheet = addStyleSheet(); static _hadSelection: boolean = false; private _selectionHTML: string | undefined; private _sidebarRef = React.createRef<SidebarAnnos>(); @@ -384,7 +384,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB } } } else { - const jsonstring = Cast(dataDoc[this.fieldKey], RichTextField)?.Data!; + const jsonstring = Cast(dataDoc[this.fieldKey], RichTextField)?.Data; if (jsonstring) { const json = JSON.parse(jsonstring); json.selection = state.toJSON().selection; @@ -1925,11 +1925,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB setHeight={this.setSidebarHeight} /> ) : ( - <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> + <div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.(), false), true)}> <ComponentTag // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} - ref={this._sidebarTagRef as any} + ref={this._sidebarTagRef} setContentView={emptyFunction} NativeWidth={returnZero} NativeHeight={returnZero} |
