diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DocumentManager.ts | 2 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/linking/LinkEditor.tsx | 74 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.scss | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 86 | ||||
-rw-r--r-- | src/fields/Doc.ts | 7 |
9 files changed, 136 insertions, 49 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a60c1ed6b..00f6bc40a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -233,7 +233,7 @@ export class DocumentManager { } } if (focusView) { - !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox + !noSelect && Doc.linkFollowHighlight(focusView.rootDoc, undefined, targetDoc); //TODO:glr make this a setting in PresBox if (originatingDoc?.followLinkAudio) DocumentManager.playAudioAnno(focusView.rootDoc); const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 987bfc23d..392b4eeeb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -484,6 +484,7 @@ export class MainView extends React.Component { } globalPointerDown = action((e: PointerEvent) => { + runInAction(() => (Doc.HighlightBrush.linkFollowEffect = undefined)); AudioBox.Enabled = true; const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 92319d080..434466505 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -452,7 +452,7 @@ export class CollectionDockingView extends CollectionSubView() { .map(id => DocServer.GetCachedRefField(id)) .filter(f => f) .map(f => f as Doc); - const changesMade = this.props.Document.dockcingConfig !== json; + const changesMade = this.props.Document.dockingConfig !== json; if (changesMade && !this._flush) { UndoManager.RunInBatch(() => { this.props.Document.dockingConfig = json; @@ -507,7 +507,12 @@ export class CollectionDockingView extends CollectionSubView() { action(() => { //if (confirm('really close this?')) { if ((!stack.parent.isRoot && !stack.parent.parent.isRoot) || stack.parent.contentItems.length > 1) { + const batch = UndoManager.StartBatch('close stack'); stack.remove(); + setTimeout(() => { + this.stateChanged(); + batch.end(); + }); } else { alert('cant delete the last stack'); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7bc273d7d..b66dc0aa2 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -111,7 +111,7 @@ export function CollectionSubView<X>(moreProps?: X) { rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : []; } - const docs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate).map(d => d as Doc); + const docs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && !d.unrendered).map(d => d as Doc); const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 3e8867c50..d90a91ab7 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -3,13 +3,14 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, NumListCast, StrListCast, Field } from '../../../fields/Doc'; -import { DateCast, StrCast, Cast, BoolCast } from '../../../fields/Types'; +import { DateCast, StrCast, Cast, BoolCast, DocCast, NumCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import './LinkEditor.scss'; import { LinkRelationshipSearch } from './LinkRelationshipSearch'; import React = require('react'); import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; +import { PresBox, PresEffect } from '../nodes/trails'; interface LinkEditorProps { sourceDoc: Doc; @@ -24,6 +25,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> { @observable zoomFollow = BoolCast(this.props.sourceDoc.followLinkZoom); @observable audioFollow = BoolCast(this.props.sourceDoc.followLinkAudio); @observable openDropdown: boolean = false; + @observable openEffectDropdown: boolean = false; @observable private buttonColor: string = ''; @observable private relationshipButtonColor: string = ''; @observable private relationshipSearchVisibility: string = 'none'; @@ -295,6 +297,61 @@ export class LinkEditor extends React.Component<LinkEditorProps> { ); } + @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); + } + return this.props.sourceDoc; + } + @action + changeEffectDropdown = () => { + this.openEffectDropdown = !this.openEffectDropdown; + }; + + @undoBatch + changeEffect = action((follow: string) => { + this.openEffectDropdown = false; + this.sourceAnchor.presEffect = follow; + }); + + @computed + get effectDropdown() { + return ( + <div className="linkEditor-followingDropdown"> + <div className="linkEditor-followingDropdown-label">Transition Effect:</div> + <div className="linkEditor-followingDropdown-dropdown"> + <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeEffectDropdown}> + {StrCast(this.sourceAnchor.presEffect, 'default')} + <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openEffectDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} /> + </div> + <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openEffectDropdown ? '' : 'none' }}> + {[ + PresEffect.None, + PresEffect.Zoom, + PresEffect.Lightspeed, + PresEffect.Fade, + PresEffect.Flip, + PresEffect.Rotate, + PresEffect.Bounce, + PresEffect.Roll, + PresEffect.Left, + PresEffect.Right, + PresEffect.Center, + PresEffect.Top, + PresEffect.Bottom, + ].map(effect => ( + <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeEffect(effect.toString())}> + {effect.toString()} + </div> + ))} + </div> + </div> + </div> + ); + } + autoMove = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove)))); }; @@ -348,7 +405,6 @@ export class LinkEditor extends React.Component<LinkEditorProps> { </> ) : null} </div> - {this.editDescription} {this.editRelationship} {this.editZoomFollow} @@ -390,6 +446,20 @@ export class LinkEditor extends React.Component<LinkEditorProps> { </Tooltip> </div> {this.followingDropdown} + {this.effectDropdown} + {PresBox.inputter('0.1', '0.1', '10', NumCast(this.sourceAnchor.presTransition) / 1000, true, (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => (this.sourceAnchor.presTransition = timeInMS)))} + <div + className={'slider-headers'} + style={{ + display: 'grid', + justifyContent: 'space-between', + width: '100%', + gridTemplateColumns: 'auto auto auto', + }}> + <div className="slider-text">Fast</div> + <div className="slider-text">Medium</div> + <div className="slider-text">Slow</div> + </div>{' '} </div> ); } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 01188d3fa..abf6e37ab 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -28,7 +28,7 @@ transition: outline 0.3s linear; // background: $white; //overflow: hidden; - transform-origin: left top; + transform-origin: center; &.minimized { width: 30px; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index dc468cf89..fb07f1033 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.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 { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; -import { observer } from 'mobx-react'; +import { observer, renderReporter } from 'mobx-react'; import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; @@ -1394,7 +1394,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined, }); - const animRenderDoc = PresBox.Instance?.isActiveItemTarget(this.layoutDoc) ? PresBox.AnimationEffect(renderDoc, PresBox.Instance.activeItem) : renderDoc; + const animRenderDoc = Doc.IsHighlighted(this.rootDoc) || PresBox.Instance?.isActiveItemTarget(this.layoutDoc) ? PresBox.AnimationEffect(renderDoc, PresBox.Instance?.activeItem ?? Doc.HighlightBrush.linkFollowEffect, this.rootDoc) : renderDoc; return ( <div className={`${DocumentView.ROOT_DIV} docView-hack`} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 10f2dc016..7235481e0 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -34,6 +34,7 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresMovement, PresStatus } from './PresEnums'; +import { map } from 'bluebird'; const { Howl } = require('howler'); export interface PinProps { @@ -67,17 +68,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * @param presEffectDoc presentation effects document that specifies the animation effect parameters * @returns a function that will wrap a JSX animation element wrapping any JSX element */ - public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc) { + public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc, root: Doc) { const effectProps = { - left: presEffectDoc.presEffectDirection === PresEffect.Left, - right: presEffectDoc.presEffectDirection === PresEffect.Right, - top: presEffectDoc.presEffectDirection === PresEffect.Top, - bottom: presEffectDoc.presEffectDirection === PresEffect.Bottom, + left: presEffectDoc?.presEffectDirection === PresEffect.Left, + right: presEffectDoc?.presEffectDirection === PresEffect.Right, + top: presEffectDoc?.presEffectDirection === PresEffect.Top, + bottom: presEffectDoc?.presEffectDirection === PresEffect.Bottom, opposite: true, - delay: NumCast(presEffectDoc.presTransition), + delay: 0, + duration: Cast(presEffectDoc?.presTransition, 'number', null), }; //prettier-ignore - switch (StrCast(presEffectDoc.presEffect)) { + switch (StrCast(presEffectDoc?.presEffect)) { default: case PresEffect.None: return renderDoc; case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>; @@ -386,7 +388,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } if (pinDataTypes.dataannos) { const fkey = Doc.LayoutFieldKey(bestTarget); - Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>(DocListCast(activeItem.presAnnotations)); + Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]); } if (pinDataTypes.dataview && activeItem.presData !== undefined) { const fkey = Doc.LayoutFieldKey(bestTarget); @@ -465,7 +467,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } if (pinProps.pinData.dataannos) { const fkey = Doc.LayoutFieldKey(targetDoc); - pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations'])); + pinDoc.presAnnotations = new List<Doc>(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.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop; @@ -1095,12 +1097,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection // Converts seconds to ms and updates presTransition - setTransitionTime = (number: String, change?: number) => { + public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 10000) timeInMS = 10000; - this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)); + setter(timeInMS); + }; + setTransitionTime = (number: String, change?: number) => { + PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change); }; // Converts seconds to ms and updates presTransition @@ -1165,6 +1170,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { _batch: UndoManager.Batch | undefined = undefined; + public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => { + let batch: any; + return ( + <input + type="range" + step={step} + min={min} + max={max} + value={value} + className={`toolbar-slider ${active ? '' : 'none'}`} + onPointerDown={e => { + batch = UndoManager.StartBatch('pres slider'); + e.stopPropagation(); + }} + onPointerUp={() => batch?.end()} + onChange={e => { + e.stopPropagation(); + change(e.target.value); + }} + /> + ); + }; @computed get transitionDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; @@ -1180,39 +1207,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {movement} </div> ); - const presDirection = (diretion: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => { - const color = this.activeItem.presEffectDirection === diretion || (diretion === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black'; + const presDirection = (direction: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => { + const color = this.activeItem.presEffectDirection === direction || (direction === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black'; return ( - <Tooltip title={<div className="dash-tooltip">{diretion}</div>}> + <Tooltip title={<div className="dash-tooltip">{direction}</div>}> <div - style={{ ...opts, border: diretion === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }} - onClick={() => this.updateEffectDirection(diretion)}> + style={{ ...opts, border: direction === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }} + onClick={() => this.updateEffectDirection(direction)}> {icon ? <FontAwesomeIcon icon={icon as any} /> : null} </div> </Tooltip> ); }; - const inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => { - return ( - <input - type="range" - step={step} - min={min} - max={max} - value={value} - className={`toolbar-slider ${active ? '' : 'none'}`} - onPointerDown={e => { - this._batch = UndoManager.StartBatch('pres slider'); - e.stopPropagation(); - }} - onPointerUp={() => this._batch?.end()} - onChange={e => { - e.stopPropagation(); - change(e.target.value); - }} - /> - ); - }; if (activeItem && targetDoc) { const type = targetDoc.type; const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5; @@ -1263,7 +1269,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)} + {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)} <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}> <div className="presBox-subheading">Transition Speed</div> <div className="ribbon-property"> @@ -1278,7 +1284,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0.1', '0.1', '10', transitionSpeed, true, this.setTransitionTime)} + {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.setTransitionTime)} <div className={'slider-headers'}> <div className="slider-text">Fast</div> <div className="slider-text">Medium</div> @@ -1331,7 +1337,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)} + {PresBox.inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)} <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}> <div className="slider-text">Short</div> <div className="slider-text">Medium</div> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 70cb10970..75801b68c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1263,11 +1263,15 @@ export namespace Doc { export function linkFollowUnhighlight() { Doc.UnhighlightAll(); document.removeEventListener('pointerdown', linkFollowUnhighlight); + runInAction(() => (HighlightBrush.linkFollowEffect = undefined)); } let _lastDate = 0; - export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true) { + export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true, presEffect?: Doc) { linkFollowUnhighlight(); + // runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = undefined)); + // setTimeout(() => runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = presEffect))); + runInAction(() => presEffect && (HighlightBrush.linkFollowEffect = presEffect)); (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs)); document.removeEventListener('pointerdown', linkFollowUnhighlight); document.addEventListener('pointerdown', linkFollowUnhighlight); @@ -1277,6 +1281,7 @@ export namespace Doc { export class HighlightBrush { @observable HighlightedDoc: Map<Doc, boolean> = new Map(); + @observable static linkFollowEffect: Doc | undefined; } const highlightManager = new HighlightBrush(); export function IsHighlighted(doc: Doc) { |