diff options
author | bobzel <zzzman@gmail.com> | 2023-12-21 14:55:48 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-12-21 14:55:48 -0500 |
commit | 1caba64ee0f32ee8af79263cd4ef2a8bc5d5146e (patch) | |
tree | 0fa0e957d1f342fdc6ed4a4b43f5dddfddb1298a /src/client/views/nodes/CollectionFreeFormDocumentView.tsx | |
parent | 02eb7da95df283606d4275a22d9451cef371c3b5 (diff) | |
parent | 2691b951d96f2ce7652acbea9e340b61737b3b57 (diff) |
Merge branch 'moreUpgrading' into dataViz-annotations
Diffstat (limited to 'src/client/views/nodes/CollectionFreeFormDocumentView.tsx')
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 150 |
1 files changed, 92 insertions, 58 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 421d431b3..548734dab 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,20 +1,21 @@ -import { action, computed, observable, trace } from 'mobx'; +import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, Opt } from '../../../fields/Doc'; +import * as React from 'react'; +import { OmitKeys, numberRange } from '../../../Utils'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { numberRange, OmitKeys } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; +import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; -import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { DocComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; -import React = require('react'); export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps { x: number; @@ -23,6 +24,7 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView width: number; height: number; zIndex?: number; + autoDim?: number; // 1 means use Panel Width/Height, 0 means use width/height rotation?: number; color?: string; backgroundColor?: string; @@ -34,7 +36,11 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView CollectionFreeFormView: CollectionFreeFormView; } @observer -export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps>() implements CollectionFreeFormDocumentViewProps { +export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>() implements CollectionFreeFormDocumentViewProps { + constructor(props: any) { + super(props); + makeObservable(this); + } @observable X = this.props.x; @observable Y = this.props.y; @observable Z = this.props.z; @@ -46,6 +52,7 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti @observable Highlight = this.props.highlight; @observable Width = this.props.width; @observable Height = this.props.height; + @observable AutoDim = this.props.autoDim; @observable Transition = this.props.transition; @observable DataTransition = this.props.dataTransition; CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking @@ -61,28 +68,30 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti w_X = () => this.X; // prettier-ignore w_Y = () => this.Y; // prettier-ignore w_Z = () => this.Z; // prettier-ignore - w_ZIndex = () => this.ZIndex ?? NumCast(this.props.Document.zIndex); // prettier-ignore - w_Rotation = () => this.Rotation ?? NumCast(this.props.Document._rotation); // prettier-ignore + w_ZIndex = () => this.ZIndex ?? NumCast(this.Document.zIndex); // prettier-ignore + w_Rotation = () => this.Rotation ?? NumCast(this.Document._rotation); // prettier-ignore w_Opacity = () => this.Opacity; // prettier-ignore - w_BackgroundColor = () => this.BackgroundColor ?? Cast(this.props.Document._backgroundColor, 'string', null); // prettier-ignore - w_Color = () => this.Color ?? Cast(this.props.Document._color, 'string', null); // prettier-ignore + w_BackgroundColor = () => this.BackgroundColor ?? Cast(this.Document._backgroundColor, 'string', null); // prettier-ignore + w_Color = () => this.Color ?? Cast(this.Document._color, 'string', null); // prettier-ignore w_Highlight = () => this.Highlight; // prettier-ignore w_Width = () => this.Width; // prettier-ignore w_Height = () => this.Height; // prettier-ignore + w_AutoDim = () => this.AutoDim; // prettier-ignore w_Transition = () => this.Transition; // prettier-ignore w_DataTransition = () => this.DataTransition; // prettier-ignore - PanelWidth = () => this.Width || this.props.PanelWidth?.(); // prettier-ignore - PanelHeight = () => this.Height || this.props.PanelHeight?.(); // prettier-ignore - @action - componentDidUpdate() { - this.WrapperKeys.forEach(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower])); + PanelWidth = () => this._props.autoDim ? this._props.PanelWidth?.() : this.Width; // prettier-ignore + PanelHeight = () => this._props.autoDim ? this._props.PanelHeight?.() : this.Height; // prettier-ignore + + componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>>) { + super.componentDidUpdate(prevProps); + this.WrapperKeys.forEach(action(keys => ((this as any)[keys.upper] = (this.props as any)[keys.lower]))); } render() { const layoutProps = this.WrapperKeys.reduce((val, keys) => [(val['w_' + keys.upper] = (this as any)['w_' + keys.upper]), val][1], {} as { [key: string]: Function }); return ( <CollectionFreeFormDocumentView - {...OmitKeys(this.props, this.WrapperKeys.map(keys => keys.lower) ).omit} // prettier-ignore + {...OmitKeys(this._props, this.WrapperKeys.map(keys => keys.lower) ).omit} // prettier-ignore {...layoutProps} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} @@ -112,8 +121,12 @@ export interface CollectionFreeFormDocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps & DocumentViewProps>() { + constructor(props: any) { + super(props); + makeObservable(this); + } get displayName() { // this makes mobx trace() statements more descriptive - return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')'; + return 'CollectionFreeFormDocumentView(' + this.Document.title + ')'; } // prettier-ignore public static animFields: { key: string; val?: number }[] = [ { key: 'x' }, @@ -132,72 +145,90 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames get CollectionFreeFormView() { - return this.props.CollectionFreeFormView; + return this._props.CollectionFreeFormView; } styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => { if (doc === this.layoutDoc) { switch (property) { - case StyleProp.Opacity: return this.props.w_Opacity(); // only change the opacity for this specific document, not its children - case StyleProp.BackgroundColor: return this.props.w_BackgroundColor(); - case StyleProp.Color: return this.props.w_Color(); + case StyleProp.Opacity: return this._props.w_Opacity(); // only change the opacity for this specific document, not its children + case StyleProp.BackgroundColor: return this._props.w_BackgroundColor(); + case StyleProp.Color: return this._props.w_Color(); } // prettier-ignore } - return this.props.styleProvider?.(doc, props, property); + return this._props.styleProvider?.(doc, props, property); }; public static getValues(doc: Doc, time: number, fillIn: boolean = true) { - return CollectionFreeFormDocumentView.animFields.reduce((p, val) => { - p[val.key] = Cast(doc[`${val.key}-indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); - return p; - }, {} as { [val: string]: Opt<number> }); + return CollectionFreeFormDocumentView.animFields.reduce( + (p, val) => { + p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); + return p; + }, + {} as { [val: string]: Opt<number> } + ); } public static getStringValues(doc: Doc, time: number) { - return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => { - p[val] = Cast(doc[`${val}-indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string); - return p; - }, {} as { [val: string]: Opt<string> }); + return CollectionFreeFormDocumentView.animStringFields.reduce( + (p, val) => { + p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string); + return p; + }, + {} as { [val: string]: Opt<string> } + ); } public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { - const findexed = Cast(d[`${val}-indexed`], listSpec('string'), []).slice(); + const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice(); findexed[timecode] = vals[val] as any as string; - d[`${val}-indexed`] = new List<string>(findexed); + d[`${val}_indexed`] = new List<string>(findexed); }); } public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { - const findexed = Cast(d[`${val}-indexed`], listSpec('number'), []).slice(); + const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice(); findexed[timecode] = vals[val] as any as number; - d[`${val}-indexed`] = new List<number>(findexed); + d[`${val}_indexed`] = new List<number>(findexed); }); } + public static gotoKeyFrame(doc: Doc, newFrame: number) { + if (doc) { + const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]); + const currentFrame = Cast(doc._currentFrame, 'number', null); + if (currentFrame === undefined) { + doc._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); + } + CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0); + doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame); + } + } public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) { docs.forEach(doc => { if (doc.appearFrame === undefined) doc.appearFrame = currTimecode; - if (!doc['opacity-indexed']) { + if (!doc['opacity_indexed']) { // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in - doc['opacity-indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); + doc['opacity_indexed'] = new List<number>(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); } CollectionFreeFormDocumentView.animFields.forEach(val => (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[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode))); const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so //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.activeFrame = ComputedField.MakeFunction('this.embedContainer?._currentFrame||0'); targetDoc.dataTransition = 'inherit'; }); } @action public float = () => { - const topDoc = this.rootDoc; - const containerDocView = this.props.docViewPath().lastElement(); + const topDoc = this.Document; + const containerDocView = this._props.docViewPath().lastElement(); const screenXf = containerDocView?.screenToLocalTransform(); if (screenXf) { SelectionManager.DeselectAll(); @@ -206,8 +237,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF topDoc.z = 0; topDoc.x = spt[0]; topDoc.y = spt[1]; - this.props.removeDocument?.(topDoc); - this.props.addDocTab(topDoc, OpenWhere.inParentFromScreen); + this._props.removeDocument?.(topDoc); + this._props.addDocTab(topDoc, OpenWhere.inParentFromScreen); } else { const spt = this.screenToLocalTransform().inverse().transformPoint(0, 0); const fpt = screenXf.transformPoint(spt[0], spt[1]); @@ -220,15 +251,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF }; nudge = (x: number, y: number) => { - const [locX, locY] = this.props.ScreenToLocalTransform().transformDirection(x, y); - this.props.Document.x = this.props.w_X() + locX; - this.props.Document.y = this.props.w_Y() + locY; + const [locX, locY] = this._props.ScreenToLocalTransform().transformDirection(x, y); + this._props.Document.x = this._props.w_X() + locX; + this._props.Document.y = this._props.w_Y() + locY; }; screenToLocalTransform = () => - this.props + this._props .ScreenToLocalTransform() - .translate(-this.props.w_X(), -this.props.w_Y()) - .rotateDeg(-(this.props.w_Rotation?.() || 0)); + .translate(-this._props.w_X(), -this._props.w_Y()) + .rotateDeg(-(this._props.w_Rotation?.() || 0)); returnThis = () => this; /// this indicates whether the doc view is activated because of its relationshop to a group @@ -238,26 +269,26 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF // undefined - this is not activated by a group isGroupActive = () => { if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined; - const isGroup = this.rootDoc._isGroup && (!this.rootDoc.backgroundColor || this.rootDoc.backgroundColor === 'transparent'); - return isGroup ? (this.props.isDocumentActive?.() ? 'group' : this.props.isGroupActive?.() ? 'child' : 'inactive') : this.props.isGroupActive?.() ? 'child' : undefined; + const isGroup = this.dataDoc.isGroup && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent'); + return isGroup ? (this._props.isDocumentActive?.() ? 'group' : this._props.isGroupActive?.() ? 'child' : 'inactive') : this._props.isGroupActive?.() ? 'child' : undefined; }; public static CollectionFreeFormDocViewClassName = 'collectionFreeFormDocumentView-container'; render() { TraceMobx(); - const passOnProps = OmitKeys(this.props, Object.keys(this.props).filter(key => key.startsWith('w_'))).omit; // prettier-ignore + const passOnProps = OmitKeys(this._props, Object.keys(this._props).filter(key => key.startsWith('w_'))).omit; // prettier-ignore return ( <div className={CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName} style={{ - width: this.props.PanelWidth(), - height: this.props.PanelHeight(), - transform: `translate(${this.props.w_X()}px, ${this.props.w_Y()}px) rotate(${NumCast(this.props.w_Rotation?.())}deg)`, - transition: this.props.w_Transition?.() ?? (this.props.w_DataTransition?.() || this.props.w_Transition?.()), - zIndex: this.props.w_ZIndex?.(), - display: this.props.w_Width?.() ? undefined : 'none', + width: this._props.PanelWidth(), + height: this._props.PanelHeight(), + transform: `translate(${this._props.w_X()}px, ${this._props.w_Y()}px) rotate(${NumCast(this._props.w_Rotation?.())}deg)`, + transition: this._props.w_Transition?.() ?? (this._props.w_DataTransition?.() || this._props.w_Transition?.()), + zIndex: this._props.w_ZIndex?.(), + display: this._props.w_Width?.() ? undefined : 'none', }}> - {this.props.RenderCutoffProvider(this.props.Document) ? ( - <div style={{ position: 'absolute', width: this.props.PanelWidth(), height: this.props.PanelHeight(), background: 'lightGreen' }} /> + {this._props.RenderCutoffProvider(this._props.Document) ? ( + <div style={{ position: 'absolute', width: this._props.PanelWidth(), height: this._props.PanelHeight(), background: 'lightGreen' }} /> ) : ( <DocumentView {...passOnProps} CollectionFreeFormDocumentView={this.returnThis} styleProvider={this.styleProvider} ScreenToLocalTransform={this.screenToLocalTransform} isGroupActive={this.isGroupActive} /> )} @@ -265,3 +296,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF ); } } +ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) { + CollectionFreeFormDocumentView.gotoKeyFrame(doc, newFrame); +}); |