diff options
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b72864567..ef96e64be 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc'; -import { AclPrivate, Animation, DocData, Width } from '../../../fields/DocSymbols'; +import { AclPrivate, Animation, AudioPlay, DocData, Width } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -28,6 +28,7 @@ import { FollowLinkScript } 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'; @@ -51,7 +52,6 @@ import { LinkAnchorBox } from './LinkAnchorBox'; import { PresEffect, PresEffectDirection } from './trails'; import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); -import { SettingsManager } from '../../util/SettingsManager'; const { Howl } = require('howler'); interface Window { @@ -100,6 +100,7 @@ export interface DocFocusOptions { effect?: Doc; // animation effect for focus noSelect?: boolean; // whether target should be selected after focusing playAudio?: boolean; // whether to play audio annotation on focus + playMedia?: boolean; // whether to play start target videos openLocation?: OpenWhere; // 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 @@ -120,6 +121,7 @@ export interface DocComponentView { 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 select?: (ctrlKey: boolean, shiftKey: boolean) => void; + focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>; menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. isAnyChildContentActive?: () => boolean; // is any child content of the document active onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected @@ -428,7 +430,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps 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; let preventDefault = true; - (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); + !this.rootDoc._keepZWhenDragged && this.props.bringToFront(this.rootDoc); if (this._doubleTap) { const defaultDblclick = this.props.defaultDoubleClick?.() || this.Document.defaultDoubleClick; if (this.onDoubleClickHandler?.script) { @@ -629,7 +631,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData; if (linkdrag) { linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor(); - if (linkdrag.linkSourceDoc) { + if (linkdrag.linkSourceDoc && linkdrag.linkSourceDoc !== this.rootDoc) { if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) { de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined); } @@ -700,11 +702,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } const cm = ContextMenu.Instance; - if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return; + if (!cm || (e as any)?.nativeEvent?.SchemaHandled || DocumentView.ExploreMode) return; if (e && !(e.nativeEvent as any).dash) { const onDisplay = () => { - DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. + if (this.rootDoc.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY)); }; if (navigator.userAgent.includes('Macintosh')) { @@ -744,8 +746,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'arrow-up' }); zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'arrow-down' }); zorderItems.push({ - description: this.rootDoc._raiseWhenDragged !== false ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', - event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))), + description: !this.rootDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', + event: undoBatch(action(() => (this.rootDoc._keepZWhenDragged = !this.rootDoc._keepZWhenDragged))), icon: 'hand-point-up', }); !zorders && cm.addItem({ description: 'Z Order...', addDivider: true, noexpand: true, subitems: zorderItems, icon: 'layer-group' }); @@ -761,10 +763,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); 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' }); + !Doc.noviceMode && 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...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); } else if (LinkManager.Links(this.Document).length) { - onClicks.push({ description: 'Select on Click', event: () => this.noOnClick(), icon: 'link' }); + onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' }); onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' }); !existingOnClick && cm.addItem({ description: 'OnClick...', subitems: onClicks, icon: 'mouse-pointer' }); } @@ -798,7 +800,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' }); } const constantItems: ContextMenuProps[] = []; - if (!Doc.IsSystem(this.rootDoc)) { + if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking) { constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); (this.rootDoc._type_collection !== 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) { @@ -965,6 +967,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps case StyleProp.ShowTitle: return ''; case StyleProp.PointerEvents: return 'none'; case StyleProp.Highlighting: return undefined; + case StyleProp.Opacity: { + const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines); + return filtered.some(link => link._link_displayArrow) ? 0 : undefined; + } } return this.props.styleProvider?.(doc, props, property); }; @@ -1052,7 +1058,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } } }; - runInAction(() => (dataDoc.audioAnnoState = 'recording')); + //runInAction(() => (dataDoc.audioAnnoState = 'recording')); recorder.start(); const stopFunc = () => { recorder.stop(); @@ -1069,16 +1075,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps 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'; + if (anno instanceof AudioField) { + switch (audioAnnoState) { + case 'stopped': + this.dataDoc[AudioPlay] = 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'; + break; + case 'playing': + this.dataDoc[AudioPlay]?.stop(); + this.dataDoc.audioAnnoState = 'stopped'; + break; + } } }; @@ -1114,7 +1128,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps </div> ); const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; - const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.Instance.userVariantColor)); + const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor)); const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); const titleView = !showTitle ? null : ( <div @@ -1124,7 +1138,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps position: this.headerMargin ? 'relative' : 'absolute', height: this.titleHeight, width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button - color: lightOrDark(background), + color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background), background, pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, }}> @@ -1138,14 +1152,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps display={'block'} fontSize={10} GetValue={() => { - this.props.select(false); return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle; }} SetValue={undoBatch((input: string) => { if (input?.startsWith('#')) { - if (this.props.layout_showTitle) { + if (this.rootDoc.layout_showTitle) { this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined; - } else { + } else if (!this.props.layout_showTitle) { Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'author_date'; } } else { @@ -1276,7 +1289,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @observer export class DocumentView extends React.Component<DocumentViewProps> { public static ROOT_DIV = 'documentView-effectsWrapper'; + @observable public static Interacting = false; @observable public static LongPress = false; + @observable public static ExploreMode = false; + @observable public static LastPressedSidebarBtn: Opt<Doc>; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach + @computed public static get exploreMode() { + return () => (DocumentView.ExploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined); + } @observable public docView: DocumentViewInternal | undefined | null; @observable public textHtmlOverlay: Opt<string>; @observable private _isHovering = false; |