diff options
Diffstat (limited to 'src/client/views/collections')
21 files changed, 323 insertions, 213 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a790a0475..07fcd6a7d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -121,7 +121,7 @@ export class CollectionDockingView extends CollectionSubView() { SelectionManager.DeselectAll(); const instance = CollectionDockingView.Instance; if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { - return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + return CurrentUserUtils.openDashboard(doc); } const newItemStackConfig = { type: 'stack', @@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch @action public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { - if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document); + if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document); const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document); if (tab) { @@ -188,10 +188,13 @@ export class CollectionDockingView extends CollectionSubView() { stack.addChild(docContentConfig, undefined); stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]); } else { - const newItemStackConfig = { type: 'stack', content: [docContentConfig] }; - const newContentItem = glayRoot.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + const newContentItem = () => { + const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout); + newItem.callDownwards('_$init'); + return newItem; + } if (glayRoot.contentItems.length === 0) { // if no rows / columns - glayRoot.addChild(newContentItem); + glayRoot.addChild(newContentItem()); } else if (glayRoot.contentItems[0].isStack) { glayRoot.contentItems[0].addChild(docContentConfig); } else if ( @@ -203,32 +206,33 @@ export class CollectionDockingView extends CollectionSubView() { else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row switch (pullSide) { default: - case "right": glayRoot.contentItems[0].addChild(newContentItem); break; - case "left": glayRoot.contentItems[0].addChild(newContentItem, 0); break; + case "right": glayRoot.contentItems[0].addChild(newContentItem()); break; + case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; case "top": case "bottom": // if not going in a row layout, must add already existing content into column const rowlayout = glayRoot.contentItems[0]; const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); + const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(rowlayout.element); rowlayout.parent.replaceChild(rowlayout, newColumn); if (pullSide === "top") { newColumn.addChild(rowlayout, undefined, true); - newColumn.addChild(newContentItem, 0, true); + newColumn.addChild(newItem, 0, true); } else if (pullSide === "bottom") { - newColumn.addChild(newContentItem, undefined, true); + newColumn.addChild(newItem, undefined, true); newColumn.addChild(rowlayout, 0, true); } instance._goldenLayout.restoreScrollTops(rowlayout.element); rowlayout.config.height = 50; - newContentItem.config.height = 50; + newItem.config.height = 50; } } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column switch (pullSide) { - case "top": glayRoot.contentItems[0].addChild(newContentItem, 0); break; - case "bottom": glayRoot.contentItems[0].addChild(newContentItem); break; + case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; + case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break; case "left": case "right": default: @@ -236,23 +240,23 @@ export class CollectionDockingView extends CollectionSubView() { const collayout = glayRoot.contentItems[0]; const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); + const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(collayout.element); collayout.parent.replaceChild(collayout, newRow); if (pullSide === "left") { newRow.addChild(collayout, undefined, true); - newRow.addChild(newContentItem, 0, true); + newRow.addChild(newItem, 0, true); } else { - newRow.addChild(newContentItem, undefined, true); + newRow.addChild(newItem, undefined, true); newRow.addChild(collayout, 0, true); } instance._goldenLayout.restoreScrollTops(collayout.element); collayout.config.width = 50; - newContentItem.config.width = 50; + newItem.config.width = 50; } } instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig()); - newContentItem.callDownwards('_$init'); } return instance.layoutChanged(); @@ -364,7 +368,7 @@ export class CollectionDockingView extends CollectionSubView() { } } if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && - ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { e.stopPropagation(); } } @@ -412,6 +416,10 @@ export class CollectionDockingView extends CollectionSubView() { } tabDestroyed = (tab: any) => { + if(tab.DashDoc?.type !== DocumentType.KVP) { + Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc); + Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); + } const dview = CollectionDockingView.Instance.props.Document; const fieldKey = CollectionDockingView.Instance.props.fieldKey; Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc); @@ -427,11 +435,11 @@ export class CollectionDockingView extends CollectionSubView() { stackCreated = (stack: any) => { stack.header?.element.on('mousedown', (e: any) => { - if (e.target === stack.header?.element[0] && e.button === 2) { - const emptyPane = CurrentUserUtils.EmptyPane; - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + const dashboard = CurrentUserUtils.ActiveDashboard; + if (dashboard && e.target === stack.header?.element[0] && e.button === 2) { + dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`, }); this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); CollectionDockingView.AddSplit(docToAdd, "", stack); @@ -455,13 +463,15 @@ export class CollectionDockingView extends CollectionSubView() { .off('click') //unbind the current click handler .click(action(() => { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size - const emptyPane = CurrentUserUtils.EmptyPane; - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - }); - this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); - CollectionDockingView.AddSplit(docToAdd, "", stack); + const dashboard = CurrentUserUtils.ActiveDashboard; + if (dashboard) { + dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; + const docToAdd = Docs.Create.FreeformDocument([], { + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}` + }); + this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); + CollectionDockingView.AddSplit(docToAdd, "", stack); + } })); } @@ -474,6 +484,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab( "opens up document in a lightbox", "(doc: any)"); ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, "opens up document in tab on right side of the screen", "(doc: any)"); -ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); }, +ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); }, "opens up document in screen overlay layer", "(doc: any)"); ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 01e77f342..668d82387 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -99,7 +99,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{ } @computed get contMenuButtons() { - const selDoc = Doc.UserDoc().contextMenuBtns; + const selDoc = CurrentUserUtils.MyContextMenuBtns; return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} > <CollectionLinearView Document={selDoc} @@ -164,7 +164,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{ // </button> // </Tooltip>; - // OLD BUTTONS + // //OLD BUTTONS // return this.getElement(!this.SelectedCollection ? [/*button*/] : // [<CollectionViewBaseChrome key="chrome" // docView={this.SelectedCollection} @@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu }; _clusterCommand = { params: ["target"], title: "fit content", - script: "self.target._fitToBox = !self.target._fitToBox;", - immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox), + script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;", + immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox), initialize: emptyFunction }; _fitContentCommand = { @@ -268,17 +268,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`, immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), initialize: (button: Doc) => { - button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ? - ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined; - button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined; + const activeDash = CurrentUserUtils.ActiveDashboard; + if (activeDash) { + button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? + ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined; + button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; + } }, }; - @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } - @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } - @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } - @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } - @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; } + @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } + @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } + @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } + @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } + @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; } @computed get _tree_commands() { return undefined; } private get _buttonizableCommands() { switch (this.props.type) { @@ -303,7 +306,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu @undoBatch viewChanged = (e: React.ChangeEvent) => { - const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc; + const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc; //@ts-ignore target._viewType = e.target.selectedOptions[0].value; } @@ -467,7 +470,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu @action startRecording = () => { const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); - //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); CollectionDockingView.AddSplit(doc, "right"); } @@ -691,8 +694,8 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; } @action primCreated() { if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke= - if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); - CurrentUserUtils.SelectedTool = InkTool.None; + if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); + CurrentUserUtils.ActiveTool = InkTool.None; this._selectedPrimitive = this._shapePrims.length; SetActiveArrowStart("none"); SetActiveArrowEnd("none"); @@ -734,11 +737,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView if (this._selectedPrimitive !== i) { this._selectedPrimitive = i; if (this._title[i] === "highlighter") { - CurrentUserUtils.SelectedTool = InkTool.Highlighter; + CurrentUserUtils.ActiveTool = InkTool.Highlighter; GestureOverlay.Instance.SavedColor = ActiveInkColor(); SetActiveInkColor("rgba(245, 230, 95, 0.75)"); } else { - CurrentUserUtils.SelectedTool = InkTool.Pen; + CurrentUserUtils.ActiveTool = InkTool.Pen; } SetActiveArrowStart(this._head[i]); SetActiveArrowEnd(this._end[i]); @@ -747,7 +750,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView GestureOverlay.Instance.InkShape = this._shapePrims[i]; } else { this._selectedPrimitive = this._shapePrims.length; - CurrentUserUtils.SelectedTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; SetActiveArrowStart(""); SetActiveArrowEnd(""); GestureOverlay.Instance.InkShape = ""; @@ -840,7 +843,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView {this.widthPicker} {this.colorPicker} {this.fillPicker} - {Doc.UserDoc().noviceMode || this.props.isDoc ? (null) : + {Doc.noviceMode || this.props.isDoc ? (null) : <> <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom"> <div className="backKeyframe" onClick={this.prevKeyframe}> @@ -879,7 +882,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView const val = value.toLowerCase(); const docs = DocListCast(this.document[this.props.fieldKey]); - if (Doc.UserDoc().noviceMode) { + if (Doc.noviceMode) { if (docs instanceof Doc) { const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index e8b6817b4..bb98e1c99 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -6,8 +6,18 @@ overflow-y: hidden; border: none; background-color: $white; - border: 2px solid $dark-gray; border-width: 0 2px 0 2px; + + &:hover { + .collectionStackedTimeline-hover { + display: block; + } + } +} + +.timeline-container:hover + .timeline-hoverUI { + display: flex; + justify-content: center; } ::-webkit-scrollbar { @@ -19,6 +29,7 @@ background: $off-white; z-index: 1000; height: 100%; + overflow: hidden; .collectionStackedTimeline-trim-shade { position: absolute; @@ -61,15 +72,23 @@ border-width: 1px; } - .collectionStackedTimeline-current { + .collectionStackedTimeline-current, .collectionStackedTimeline-hover { width: 1px; height: 100%; - background-color: $pink; position: absolute; top: 0px; pointer-events: none; } + .collectionStackedTimeline-current { + background-color: $pink; + } + + .collectionStackedTimeline-hover { + display: none; + background-color: $medium-blue; + } + .collectionStackedTimeline-marker-timeline { position: absolute; top: 2.5%; @@ -108,3 +127,19 @@ pointer-events: none; } } + +.timeline-hoverUI { + position: absolute; + z-index: 10000; + transform: translate(-50%, 100%); + height: 100%; + display: none; + + .hoverTime { + position: absolute; + color: $dark-gray; + transform: translate(0, -100%); + + font-weight: bold; + } +}
\ No newline at end of file diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index ebdea9aaf..3e85edac8 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -43,6 +43,8 @@ import { } from "../nodes/DocumentView"; import { LabelBox } from "../nodes/LabelBox"; import "./CollectionStackedTimeline.scss"; +import { VideoBox } from "../nodes/VideoBox"; +import { ImageField } from "../../../fields/URLField"; export type CollectionStackedTimelineProps = { Play: () => void; @@ -86,9 +88,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack @observable _trimEnd: number = 0; // trim controls end pos @observable _zoomFactor: number = 1; - @observable _scroll: number = 0; + @observable _hoverTime: number = 0; + + @observable _thumbnail: string | undefined; + // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore get minTrimLength() { return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5); } @@ -178,7 +183,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack @action keyEvents = (e: KeyboardEvent) => { if ( - !(e.target instanceof HTMLInputElement) && + // need to include range inputs because after dragging video time slider it becomes target element + !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) && this.props.isSelected(true) ) { // if shift pressed scrub 1 second otherwise 1/10th @@ -315,11 +321,28 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack } + @action + onHover = (e: React.MouseEvent): void => { + e.stopPropagation(); + const rect = this._timeline?.getBoundingClientRect(); + const clientX = e.clientX; + if (rect) { + this._hoverTime = this.toTimeline(clientX - rect.x, rect.width); + if (this.dataDoc.thumbnails) { + const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails); + const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []); + const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField(""); + const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : ""; + this._thumbnail = src ? src : undefined; + } + } + } + + // for dragging trim start handle @action trimLeft = (e: React.PointerEvent): void => { const rect = this._timeline?.getBoundingClientRect(); - const clientX = e.movementX; setupMoveUpEvents( this, e, @@ -346,7 +369,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack @action trimRight = (e: React.PointerEvent): void => { const rect = this._timeline?.getBoundingClientRect(); - const clientX = e.movementX; setupMoveUpEvents( this, e, @@ -436,7 +458,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack if (anchorStartTime === undefined) return rootDoc; const anchor = docAnchor ?? Docs.Create.LabelDocument({ title: ComputedField.MakeFunction( - `"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])` + `self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])` ) as any, _minFontSize: 12, _maxFontSize: 24, @@ -556,7 +578,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100; @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; } - @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor - 4; } // subtract size of container border + @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; } // subtract size of container border dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight); @@ -632,6 +654,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack style={{ width: this.props.PanelWidth() }} onWheel={e => e.stopPropagation()} onScroll={this.setScroll} + onMouseMove={(e) => this.isContentActive() && this.onHover(e)} ref={wrapper => this._timelineWrapper = wrapper}> <div className="collectionStackedTimeline" @@ -702,6 +725,13 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack /> {/* {this.renderDictation} */} + { /* check time to prevent weird div overflow */ this._hoverTime < this.clipDuration && <div + className="collectionStackedTimeline-hover" + style={{ + left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`, + }} + />} + <div className="collectionStackedTimeline-current" style={{ @@ -744,6 +774,10 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack )} </div> </div> + <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}> + <div className="hoverTime">{formatTime(this._hoverTime)}</div> + {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />} + </div> </div >); } } @@ -799,7 +833,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> return `#${formatTime(start)}-${formatTime(end)}`; } - componentDidMount() { this._disposer = reaction( () => this.props.currentTimecode(), @@ -890,7 +923,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> // context menu contextMenuItems = () => { - const resetTitle = { script: ScriptField.MakeFunction(`self.title = "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"])`)!, icon: "folder-plus", label: "Reset Title" }; + const resetTitle = { script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!, icon: "folder-plus", label: "Reset Title" }; return [resetTitle]; } diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 2f002736d..73572299a 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -30,11 +30,10 @@ width: 90%; margin: 5px; font-size: 11px; - background-color: $light-blue; color: $medium-blue; padding: 10px; - border-radius: 10px; - border: solid 2px $medium-blue; + border-radius: 5px; + border: solid .5px $medium-blue; } } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index dddae4a34..4e8c14039 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -229,6 +229,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection } } isContentActive = () => this.props.isSelected() || this.props.isContentActive(); + isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)); getDisplayDoc(doc: Doc, width: () => number) { const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); @@ -245,12 +246,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection styleProvider={this.styleProvider} docViewPath={this.props.docViewPath} fitWidth={this.props.childFitWidth} - isContentActive={emptyFunction} + isContentActive={this.isChildContentActive} onKey={this.onKeyDown} isDocumentActive={this.isContentActive} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} - freezeDimensions={this.props.childFreezeDimensions} 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} dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined} @@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection <> {buttonMenu || noviceExplainer ? <div className="documentButtonMenu"> {buttonMenu ? this.buttonMenu : null} - {Doc.UserDoc().noviceMode && noviceExplainer ? + {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation"> {noviceExplainer} </div> diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 7f96217b8..f3a798143 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -224,8 +224,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC } }, icon: "compress-arrows-alt" })); - !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" }); - !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" }); + !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" }); + !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" }); ContextMenu.Instance.setDefaultItem("::", (name: string): void => { Doc.GetProto(this.props.Document)[name] = ""; const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true }); diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 4f6f45d2f..3dd9d2d84 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -59,7 +59,7 @@ export class CollectionTimeView extends CollectionSubView() { //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'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); "; runInAction(() => { - this._childClickedScript = ScriptField.MakeScript("openInLightbox(self, shiftKey)", { this: Doc.name, shiftKey: "boolean" });//, { detailView: detailView! }); + this._childClickedScript = ScriptField.MakeScript("openInLightbox(self)", { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); }); } @@ -135,11 +135,10 @@ export class CollectionTimeView extends CollectionSubView() { onClick={this.contentsDown}> <CollectionFreeFormView {...this.props} engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }} - fitContentsToDoc={returnTrue} + fitContentsToBox={returnTrue} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} - childFreezeDimensions={true} - dontScaleFilter={this.dontScaleFilter} + //dontScaleFilter={this.dontScaleFilter} layoutEngine={this.layoutEngine} /> </div>; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e809bfbce..ba72fb7b9 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -67,7 +67,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; } @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; } @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; } - @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; } + @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; } @observable _explainerHeight = 0; // height of the description of the tree view @@ -76,7 +76,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent @observable _isAnyChildContentActive = false; whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); - isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None || + isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None || (this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction)) ? true : false) @@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { const layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" }); @@ -286,7 +286,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree } @computed get noviceExplainer() { - return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) : + return !Doc.noviceMode || !this.rootDoc.explainer ? (null) : <div className="documentExplanation"> {this.rootDoc.explainer} </div>; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 965f0a352..b432104a1 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -75,7 +75,6 @@ export interface CollectionViewProps extends FieldViewProps { childHideResizeHandles?: () => boolean; childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection childLayoutString?: string; - childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height childIgnoreNativeSize?: boolean; childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; @@ -160,8 +159,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" }); subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" }); subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); + !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); + !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) { @@ -184,16 +183,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab const options = cm.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; + !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; if (this.rootDoc.childLayout instanceof Doc) { optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" }); } if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) { optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } - !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); + !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { optionItems.push({ description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" }); @@ -205,12 +204,12 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab }); } if (this.Document._viewType === CollectionViewType.Docking) { - optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" }); + optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" }); } !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); - if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) { + if (!Doc.noviceMode && !this.rootDoc.annotationOn) { const existingOnClick = cm.findByDescription("OnClick..."); const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; @@ -230,7 +229,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); } - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) }); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 11d5df197..62d07b0e4 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -36,6 +36,7 @@ import { CollectionView, CollectionViewType } from './CollectionView'; import "./TabDocView.scss"; import React = require("react"); import { listSpec } from '../../../fields/Schema'; +import { ScriptField } from '../../../fields/ScriptField'; const _global = (window /* browser */ || global /* node */) as any; interface TabDocViewProps { @@ -114,20 +115,12 @@ export class TabDocView extends React.Component<TabDocViewProps> { } })); }; - - closeWrap.className = "lm_iconWrap"; - closeWrap.id = "lm_closeWrap"; - closeWrap.onclick = (e: MouseEvent) => { - tab.header.parent.contentItem.remove(); - Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc); - Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); - }; + const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />; - const closeIcon = <FontAwesomeIcon icon={"times"} />; + const closeIcon = <FontAwesomeIcon icon={"eye"} />; ReactDOM.render(docIcon, iconWrap); ReactDOM.render(closeIcon, closeWrap); tab.reactComponents = [iconWrap, closeWrap]; - // tab.element[0].append(closeWrap); tab.element[0].prepend(iconWrap); tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), ({ layer, color }) => { @@ -192,15 +185,15 @@ export class TabDocView extends React.Component<TabDocViewProps> { * Adds a document to the presentation view **/ @action - public static PinDoc(doc: Doc, pinProps?: PinProps) { - //add this new doc to props.Document + public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) { + const docList = ((docs instanceof Doc) ? [docs]: docs); + const batch = UndoManager.StartBatch("pinning doc"); // all docs will be added to the ActivePresentation as stored on CurrentUserUtils const curPres = CurrentUserUtils.ActivePresentation; - if (curPres) { + curPres && docList.forEach(doc => { // Edge Case 1: Cannot pin document to itself if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; } - const batch = UndoManager.StartBatch("pinning doc"); const pinDoc = Doc.MakeAlias(doc); pinDoc.presentationTargetDoc = doc; pinDoc.title = doc.title + " - Slide"; @@ -227,7 +220,41 @@ export class TabDocView extends React.Component<TabDocViewProps> { pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2; pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2; pinDoc.presPinViewScale = viewProps.scale; + pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]); + } + if (pinProps?.pinDocView) { + const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking; + const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG); + if (scrollable) { + const scroll = doc._scrollTop; + pinDoc.presPinView = true; + pinDoc.presPinViewScroll = scroll; + } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) { + pinDoc.presPinView = true; + pinDoc.presStartTime = doc._currentTimecode; + pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1; + } else if (pannable) { + pinDoc.presPinView = true; + pinDoc.presPinViewX = pinDoc._panX; + pinDoc.presPinViewY = pinDoc._panY; + pinDoc.presPinViewScale = pinDoc._viewScale; + const pw = NumCast(pinProps.panelWidth); + const ph = NumCast(pinProps.panelHeight); + const ps = NumCast(pinDoc._viewScale); + if (pw && ph && ps) { + pinDoc.contentBounds = new List<number>([ + NumCast(pinDoc.panX)-pw/2/ps, + NumCast(pinDoc.panY)-ph/2/ps, + NumCast(pinDoc.panX)+pw/2/ps, + NumCast(pinDoc.panY)+ph/2/ps]); + } + } else if (doc.type === DocumentType.COMPARISON) { + const width = doc._clipWidth; + pinDoc.presPinClipWidth = width; + pinDoc.presPinView = true; + } } + pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)"); Doc.AddDocToList(curPres, "data", pinDoc, presSelected); if (!pinProps?.audioRange && duration !== undefined) { pinDoc.mediaStart = "manual"; @@ -246,17 +273,16 @@ export class TabDocView extends React.Component<TabDocViewProps> { pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { - const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []); - if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); - CollectionDockingView.AddSplit(curPres, "right"); - setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things - } PresBox.Instance?._selectedArray.clear(); pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array - setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs - return pinDoc; + }); + if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { + const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []); + if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); + CollectionDockingView.AddSplit(curPres, "right"); + setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 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 } componentDidMount() { @@ -303,7 +329,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); const locationParams = locationFields.length > 1 ? locationFields[1] : ""; switch (locationFields[0]) { - case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + case "dashboard": return CurrentUserUtils.openDashboard(doc); case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); @@ -539,7 +565,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { docFilters={CollectionDockingView.Instance.childDocFilters} docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - fitContentsToDoc={returnTrue} + fitContentsToBox={returnTrue} /> <div className="miniOverlay" onPointerDown={this.miniDown} > <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} /> diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 09f05f69a..704b8989a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -114,7 +114,7 @@ export class TreeView extends React.Component<TreeViewProps> { return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : this.props.treeView.dashboardMode ? this.fieldKey : this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying - this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); + this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); } @computed get doc() { return this.props.document; } @@ -560,8 +560,8 @@ export class TreeView extends React.Component<TreeViewProps> { const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : ""; const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ""; const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; - const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - const layout = (Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; + 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); } @action @@ -613,7 +613,7 @@ export class TreeView extends React.Component<TreeViewProps> { return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); } - onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null)); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { @@ -796,7 +796,7 @@ export class TreeView extends React.Component<TreeViewProps> { isDocumentActive={isActive} styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} hideTitle={asText} - fitContentsToDoc={returnTrue} + fitContentsToBox={returnTrue} hideDecorationTitle={this.props.treeView.outlineMode} hideResizeHandles={this.props.treeView.outlineMode} onClick={this.onChildClick} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a661cebb8..1320785a9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel import { List } from "../../../../fields/List"; import { ObjectField } from "../../../../fields/ObjectField"; import { RichTextField } from "../../../../fields/RichTextField"; -import { createSchema, listSpec } from "../../../../fields/Schema"; +import { listSpec } from "../../../../fields/Schema"; import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { ImageField } from "../../../../fields/URLField"; @@ -41,6 +41,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn import { LightboxView } from "../../LightboxView"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; +import { FieldViewProps } from "../../nodes/FieldView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { PresBox } from "../../nodes/trails/PresBox"; import { VideoBox } from "../../nodes/VideoBox"; @@ -50,29 +51,15 @@ import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; import { TreeViewType } from "../CollectionTreeView"; import { CollectionViewType } from "../CollectionView"; +import { TabDocView } from "../TabDocView"; import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { FieldView, FieldViewProps } from "../../nodes/FieldView"; -import { InkTranscription } from "../../InkTranscription"; import e = require("connect-flash"); import { ReplayMovements } from "../../../util/ReplayMovements"; -export const panZoomSchema = createSchema({ - _panX: "number", - _panY: "number", - _currentTimecode: "number", - _timecodeToShow: "number", - _currentFrame: "number", - _useClusters: "boolean", - _viewTransition: "string", - _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set - _yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set - _fitToBox: "boolean", - scrollHeight: "number" // this will be set when the collection is an annotation overlay for a PDF/Webpage -}); export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -140,16 +127,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) }; } - @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; } - @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); } - @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); } - @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); } + @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; } + @computed get contentBounds() { + const cb = Cast(this.rootDoc.contentBounds, listSpec("number")); + return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : + this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); + } + @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); } + @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); } @computed get cachedCenteringShiftX(): number { - const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; + const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling; return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections } @computed get cachedCenteringShiftY(): number { - const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; + const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling; return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections } @computed get cachedGetLocalTransform(): Transform { @@ -174,11 +165,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.layoutDoc._panY = vals.bounds.cy; this.layoutDoc._viewScale = vals.scale; } - freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined; - reverseNativeScaling = () => this.fitToContent ? true : false; - panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX); - panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY); - zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1)); + 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. + // 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)); + 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)`; getTransform = () => this.cachedGetTransform.copy(); getLocalTransform = () => this.cachedGetLocalTransform.copy(); @@ -260,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)]; layoutDoc._width = NumCast(layoutDoc._width, 300); layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300); - (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront + (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront } (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments); @@ -465,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - switch (CurrentUserUtils.SelectedTool) { + switch (CurrentUserUtils.ActiveTool) { case InkTool.Highlighter: break; // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to @@ -504,7 +497,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); - if (CurrentUserUtils.SelectedTool === InkTool.None) { + if (CurrentUserUtils.ActiveTool === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); @@ -526,9 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection case GestureUtils.Gestures.Stroke: const points = ge.points; const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); - const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, + const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() }); - if (CurrentUserUtils.SelectedTool === InkTool.Write) { + if (CurrentUserUtils.ActiveTool === InkTool.Write) { this.unprocessedDocs.push(inkDoc); CollectionFreeFormView.collectionsWithUnprocessedInk.add(this); } @@ -718,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onPointerMove = (e: PointerEvent): void => { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (this.tryDragCluster(e, this._hitCluster)) { @@ -841,7 +834,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (!e.cancelBubble) { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); if (myTouches[0]) { - if (CurrentUserUtils.SelectedTool === InkTool.None) { + if (CurrentUserUtils.ActiveTool === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); @@ -992,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; + if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -1031,7 +1024,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2; } } - if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) { + if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) { this._viewTransition = panTime; const scale = this.getLocalTransform().inverse().Scale; const minScale = NumCast(this.rootDoc._viewScaleMin, 1); @@ -1265,7 +1258,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection styleProvider={this.getClusterColor} dataProvider={this.childDataProvider} sizeProvider={this.childSizeProvider} - freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} bringToFront={this.bringToFront} showTitle={this.props.childShowTitle} @@ -1273,7 +1265,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView} pointerEvents={this.pointerEvents} jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0} - //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this + //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this />; } addDocTab = action((doc: Doc, where: string) => { @@ -1416,9 +1408,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) })); - if (this.props.isAnnotationOverlay) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar - if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, NumCast(this.props.Document[this.scaleFieldKey], 1)); - else this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey])); + if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar + if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling()); + else this.props.Document[this.scaleFieldKey] = Math.max(1,this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey])); } this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true); @@ -1502,15 +1494,22 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } } if (oldDiv instanceof HTMLCanvasElement) { - const canvas = oldDiv; - const img = document.createElement('img'); // create a Image Element - img.src = canvas.toDataURL(); //image source - img.style.width = canvas.style.width; - img.style.height = canvas.style.height; - const newCan = newDiv as HTMLCanvasElement; - const parEle = newCan.parentElement as HTMLElement; - parEle.removeChild(newCan); - parEle.appendChild(img); + if (oldDiv.className === "collectionFreeFormView-grid") { + const newCan = newDiv as HTMLCanvasElement; + const parEle = newCan.parentElement as HTMLElement; + parEle.removeChild(newCan); + parEle.appendChild(document.createElement('div')) + } else { + const canvas = oldDiv; + const img = document.createElement('img'); // create a Image Element + img.src = canvas.toDataURL(); //image source + img.style.width = canvas.style.width; + img.style.height = canvas.style.height; + const newCan = newDiv as HTMLCanvasElement; + const parEle = newCan.parentElement as HTMLElement; + parEle.removeChild(newCan); + parEle.appendChild(img); + } } } @@ -1518,15 +1517,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), this.props.docViewPath().lastElement().ContentDiv!, this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), - this.props.PanelWidth(), this.props.PanelHeight(), 0, + this.props.PanelWidth(), this.props.PanelHeight(), 0, 1, false, "", (iconFile, nativeWidth, nativeHeight) => { - this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc["icon-nativeWidth"] = nativeWidth; - this.dataDoc["icon-nativeHeight"] = nativeHeight; + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; }); - public static UpdateIcon(filename:string, docViewContent:HTMLElement, width: number, height: number, - panelWidth:number, panelHeight: number, scrollTop:number, cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) { + public static UpdateIcon( + filename:string, docViewContent:HTMLElement, + width: number, height: number, + panelWidth:number, panelHeight: number, + scrollTop:number, + realNativeHeight: number, + noSuffix: boolean, + replaceRootFilename: string| undefined, + cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) + { const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = width.toString(); newDiv.style.height = height.toString(); @@ -1534,19 +1541,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const htmlString = new XMLSerializer().serializeToString(newDiv); const nativeWidth = width; const nativeHeight = height; - CreateImage( + return CreateImage( Utils.prepend(""), document.styleSheets, htmlString, nativeWidth, nativeWidth * panelHeight / panelWidth, - scrollTop + scrollTop * panelHeight / realNativeHeight ).then - ((data_url: any) => { - VideoBox.convertDataUri(data_url, filename).then( - returnedfilename => setTimeout(() => { - cb(returnedfilename as string, nativeWidth, nativeHeight); - }, 500)); + (async (data_url: any) => { + const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); + cb(returnedFilename as string, nativeWidth, nativeHeight); }) .catch(function (error: any) { console.error('oops, something went wrong!', error); @@ -1616,9 +1621,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const appearance = ContextMenu.Instance.findByDescription("Appearance..."); const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); - !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); - appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); - appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); + !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); + appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); + appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" }); + //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); this.props.ContainingCollectionView && appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" }); @@ -1626,27 +1632,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" }); - !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; + !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); const viewctrls = ContextMenu.Instance.findByDescription("UI Controls..."); const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : []; - !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; - !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; + !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => 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; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode && + !this.props.isAnnotationOverlay && !Doc.noviceMode && optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" }); this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); } !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); const mores = ContextMenu.Instance.findByDescription("More..."); const moreItems = mores && "subitems" in mores ? mores.subitems : []; - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) }); moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) }); } @@ -1774,7 +1780,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection isAnnotationOverlay={this.isAnnotationOverlay}> <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}> {this.layoutDoc._backgroundGridShow ? - <CollectionFreeFormBackgroundGrid + <div><CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!? PanelWidth={this.props.PanelWidth} PanelHeight={this.props.PanelHeight} panX={this.panX} @@ -1784,7 +1790,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection isAnnotationOverlay={this.isAnnotationOverlay} cachedCenteringShiftX={this.cachedCenteringShiftX} cachedCenteringShiftY={this.cachedCenteringShiftY} - /> : (null)} + /></div> : (null)} <CollectionFreeFormViewPannableContents isAnnotationOverlay={this.isAnnotationOverlay} isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 77ac855e6..081a1a924 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { RichTextField } from "../../../../fields/RichTextField"; import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; +import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; import { ImageField } from "../../../../fields/URLField"; import { GetEffectiveAcl } from "../../../../fields/util"; import { intersectRect, returnFalse, Utils } from "../../../../Utils"; @@ -156,7 +156,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque })); } else if (e.key === "s" && e.ctrlKey) { e.preventDefault(); - const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!; + const slide = Doc.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!; slide.x = x; slide.y = y; FormattedTextBox.SelectOnLoad = slide[Id]; @@ -330,7 +330,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - if (CurrentUserUtils.SelectedTool === InkTool.None) { + if (CurrentUserUtils.ActiveTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { (e.nativeEvent as any).marqueeHit = true; if (!this.props.trySelectCluster(e.shiftKey)) { @@ -367,7 +367,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => { Doc.GetProto(doc).data = new List<Doc>(selected); Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform"; - !this.props.isAnnotationOverlay && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(doc)); + !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc)); doc._panX = doc._panY = 0; return doc; })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); @@ -517,7 +517,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque d.y = NumCast(d.y) - this.Bounds.top; return d; }); - const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" }); + const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" }); const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" }); DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", ""); @@ -643,7 +643,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque return <div className="marqueeView" style={{ overflow: StrCast(this.props.Document._overflow), - cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer" + cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer" }} onDragOver={e => e.preventDefault()} diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index da102fe18..4e4c33446 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -162,7 +162,6 @@ export class CollectionGridView extends CollectionSubView() { DataDoc={layout.resolvedDataDoc as Doc} PanelWidth={width} PanelHeight={height} - freezeDimensions={true} ScreenToLocalTransform={dxf} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index c0a33a5e0..c31267e87 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -153,6 +153,7 @@ export class CollectionLinearView extends CollectionSubView() { PanelWidth={nested ? doc[WidthSym] : this.dimension} PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension} renderDepth={this.props.renderDepth + 1} + dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)} focus={emptyFunction} styleProvider={this.props.styleProvider} docViewPath={returnEmptyDoclist} @@ -206,7 +207,7 @@ export class CollectionLinearView extends CollectionSubView() { }}> {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} </div> - {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null : + {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : <span className="bottomPopup-background" style={{ pointerEvents: "all" }} onPointerDown={e => e.stopPropagation()} > <span className="bottomPopup-text" > @@ -225,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() { </span> </Tooltip> </span>} - {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) : + {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) : <span className="bottomPopup-background"> <span className="bottomPopup-text"> Currently playing: diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index e2dfb25e2..777ef464f 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -6,6 +6,7 @@ import { List } from '../../../../fields/List'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -242,7 +243,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { return this.props.addDocTab(doc, where); } isContentActive = () => this.props.isSelected() || this.props.isContentActive(); - isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; + isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return <DocumentView Document={layout} @@ -251,13 +252,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { docViewPath={this.props.docViewPath} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} - freezeDimensions={this.props.childFreezeDimensions} renderDepth={this.props.renderDepth + 1} - isContentActive={this.isChildContentActive} - isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive} - hideResizeHandles={this.props.childHideResizeHandles?.()} - hideDecorationTitle={this.props.childHideDecorationTitle?.()} - fitContentsToDoc={this.props.fitContentsToDoc} PanelWidth={width} PanelHeight={height} rootSelected={this.rootSelected} @@ -266,12 +261,18 @@ export class CollectionMulticolumnView extends CollectionSubView() { onDoubleClick={this.onChildDoubleClickHandler} suppressSetHeight={true} ScreenToLocalTransform={dxf} + isContentActive={this.isChildContentActive} + isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive} + hideResizeHandles={this.props.childHideResizeHandles?.()} + hideDecorationTitle={this.props.childHideDecorationTitle?.()} + fitContentsToBox={this.props.fitContentsToBox} focus={this.props.focus} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} + dontRegisterView={this.props.dontRegisterView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 3010e36aa..08385bcb5 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -6,6 +6,7 @@ import { List } from '../../../../fields/List'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -242,7 +243,7 @@ export class CollectionMultirowView extends CollectionSubView() { return this.props.addDocTab(doc, where); } isContentActive = () => this.props.isSelected() || this.props.isContentActive(); - isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; + isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return <DocumentView Document={layout} @@ -251,7 +252,6 @@ export class CollectionMultirowView extends CollectionSubView() { docViewPath={this.props.docViewPath} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} - freezeDimensions={this.props.childFreezeDimensions} renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} @@ -261,16 +261,17 @@ export class CollectionMultirowView extends CollectionSubView() { onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={dxf} isContentActive={this.isChildContentActive} - isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive} + isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive} hideResizeHandles={this.props.childHideResizeHandles?.()} hideDecorationTitle={this.props.childHideDecorationTitle?.()} - fitContentsToDoc={this.props.fitContentsToDoc} + fitContentsToBox={this.props.fitContentsToBox} focus={this.props.focus} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} + dontRegisterView={this.props.dontRegisterView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index 0875c80b3..9653f2808 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -321,7 +321,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"]; const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const showKeys = new Set<string>(); - [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode || + [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode || whitelistKeys.includes(key) || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null); return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm)); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index b731479a5..9eba788a9 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -402,8 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() { <DocumentView Document={this.previewDocument} DataDoc={undefined} - fitContentsToDoc={returnTrue} - freezeDimensions={true} + fitContentsToBox={returnTrue} dontCenter={"y"} focus={DocUtils.DefaultFocus} renderDepth={this.props.renderDepth} diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx index bea5b3be6..43266a571 100644 --- a/src/client/views/collections/collectionSchema/SchemaTable.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx @@ -574,7 +574,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { DataDoc={this._showDataDoc} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} - freezeDimensions={true} focus={DocUtils.DefaultFocus} renderDepth={this.props.renderDepth} rootSelected={returnFalse} |