diff options
author | bobzel <zzzman@gmail.com> | 2022-08-17 12:31:26 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2022-08-17 12:31:26 -0400 |
commit | 0178de4ab9ffd11630b700f9c02468b74beabd14 (patch) | |
tree | 52aff11f9cb82d3545ac640de40005721c253160 /src | |
parent | 0b43c12bf9394f8561de57299c733734082f9f0a (diff) |
fixed dragging docs on web and pdf to work better by temporarily adding transparent docs to the opaque layer so they can get drop events. cleaned eraser and pen interaction code and made erasing strokes work faster and avoid hanging by not intersecting strokes that are already partially deleted.
Diffstat (limited to 'src')
-rw-r--r-- | src/Utils.ts | 3 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 26 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 12 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 140 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 11 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.scss | 17 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 110 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 75 |
10 files changed, 153 insertions, 252 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index 528a429d0..9e002ebd4 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -111,6 +111,7 @@ export namespace Utils { const isTransparentFunctionHack = 'isTransparent(__value__)'; export const noRecursionHack = '__noRecursion'; + export const noDragsDocFilter = 'noDragDocs:any:check'; export function IsRecursiveFilter(val: string) { return !val.includes(noRecursionHack); } @@ -125,7 +126,7 @@ export namespace Utils { // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field } - export function PropUnsetFilter(prop: string) { + export function IsPropUnsetFilter(prop: string) { return `${prop}:any,${noRecursionHack}:unset`; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index cf9ed43e1..e579bfd8a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1143,30 +1143,6 @@ export namespace Docs { } export namespace DocUtils { - export function Excluded(d: Doc, docFilters: string[]) { - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - docFilters.forEach(filter => { - const fields = filter.split(':'); - const key = fields[0]; - const value = fields[1]; - const modifiers = fields[2]; - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - }); - - if (d.z) return false; - for (const facetKey of Object.keys(filterFacets)) { - const facet = filterFacets[facetKey]; - const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value)); - if (failsNotEqualFacets) { - return true; - } - } - return false; - } /** * @param docs * @param docFilters @@ -1200,7 +1176,7 @@ export namespace DocUtils { return false; } - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) { + for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(':')[0])) { const facet = filterFacets[facetKey]; // facets that match some value in the field of the document (e.g. some text field) diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5479929bd..e33bb77de 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -88,11 +88,11 @@ export function CollectionSubView<X>(moreProps?: X) { } collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters); collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []); + // child filters apply to the descendants of the documents in this collection childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()]; + // unrecursive filters apply to the documents in the collection, but no their children. See Utils.noRecursionHack unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])]; childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; - IsFiltered = () => - this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined; searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); @@ -122,13 +122,11 @@ export function CollectionSubView<X>(moreProps?: X) { return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one } - // console.log(Doc.ActiveDashboard._docFilters); - // if (!this.props.Document._docFilters && this.props.Document.currentFilter) { - // (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean; - // } const docsforFilter: Doc[] = []; childDocs.forEach(d => { - // if (DocUtils.Excluded(d, docFilters)) return; + // dragging facets + const dragged = this.props.docFilters?.().some(f => f.includes(Utils.noDragsDocFilter)); + if (dragged && DragManager.docsBeingDragged.includes(d)) return false; let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0; if (notFiltered) { notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f3074543b..45a5e30ff 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -109,8 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @observable _hLines: number[] | undefined; @observable _vLines: number[] | undefined; @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. - @observable _pullCoords: number[] = [0, 0]; - @observable _pullDirection: string = ''; @observable _showAnimTimeline = false; @observable _clusterSets: Doc[][] = []; @observable _deleteList: DocumentView[] = []; @@ -465,20 +463,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => { let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 - if (property !== StyleProp.BackgroundColor) return styleProp; - const cluster = NumCast(doc?.cluster); - if (this.Document._useClusters) { - if (this._clusterSets.length <= cluster) { - setTimeout(() => doc && this.updateCluster(doc)); - } else { - // choose a cluster color from a palette - const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; - styleProp = colors[cluster % colors.length]; - const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor); - // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document - set?.map(s => (styleProp = StrCast(s.backgroundColor))); - } - } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray"; + switch (property) { + case StyleProp.BackgroundColor: + const cluster = NumCast(doc?.cluster); + if (this.Document._useClusters) { + if (this._clusterSets.length <= cluster) { + setTimeout(() => doc && this.updateCluster(doc)); + } else { + // choose a cluster color from a palette + const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; + styleProp = colors[cluster % colors.length]; + const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor); + // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document + set?.map(s => (styleProp = StrCast(s.backgroundColor))); + } + } + break; + } return styleProp; }; @@ -523,17 +524,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views case InkTool.Eraser: - document.addEventListener('pointermove', this.onEraserMove); - document.addEventListener('pointerup', this.onEraserUp); this._batch = UndoManager.StartBatch('collectionErase'); - e.stopPropagation(); - e.preventDefault(); + setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction); break; case InkTool.None: if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) { this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); - document.addEventListener('pointermove', this.onPointerMove); - document.addEventListener('pointerup', this.onPointerUp); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false); } break; } @@ -698,23 +695,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; @action onEraserUp = (e: PointerEvent): void => { - if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - document.removeEventListener('pointermove', this.onEraserMove); - document.removeEventListener('pointerup', this.onEraserUp); - this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc)); - this._deleteList = []; - this._batch?.end(); - } - }; - - @action - onPointerUp = (e: PointerEvent): void => { - if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); - this.removeMoveListeners(); - this.removeEndListeners(); - } + this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc)); + this._deleteList = []; + this._batch?.end(); }; onClick = (e: React.MouseEvent) => { @@ -752,46 +735,42 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection * However, if Shift is held, then no segmentation is done -- instead any intersected stroke is deleted in its entirety. */ @action - onEraserMove = (e: PointerEvent) => { + onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => { const currPoint = { X: e.clientX, Y: e.clientY }; - this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => { + this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => { if (!this._deleteList.includes(intersect.inkView)) { this._deleteList.push(intersect.inkView); SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1'); SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black'); // create a new curve by appending all curves of the current segment together in order to render a single new stroke. - !e.shiftKey && + if (!e.shiftKey) { this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment => GestureOverlay.Instance.dispatchGesture( GestureUtils.Gestures.Stroke, segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]) ) ); + } // Lower ink opacity to give the user a visual indicator of deletion. intersect.inkView.layoutDoc.opacity = 0.5; + intersect.inkView.layoutDoc.dontIntersect = true; } }); - this._lastX = currPoint.X; - this._lastY = currPoint.Y; - - 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(); + return false; }; @action - onPointerMove = (e: PointerEvent): void => { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; + onPointerMove = (e: PointerEvent): boolean => { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { Doc.ActiveTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (this.tryDragCluster(e, this._hitCluster)) { - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); + return true; } else this.pan(e); - 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(); } + return false; }; /** @@ -801,6 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => { const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) }; const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) }; + return this.childDocs .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)) .filter(inkView => inkView?.ComponentView instanceof InkingStroke) @@ -888,7 +868,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const tVals: number[] = []; // Iterating through all ink strokes in the current freeform collection. this.childDocs - .filter(doc => doc.type === DocumentType.INK) + .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect) .forEach(doc => { const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke; const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; @@ -919,7 +899,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection 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(); document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); return; } // TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected' @@ -967,13 +946,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; // const transformed = this.getTransform().inverse().transformPoint(centerX, centerY); - if (!this._pullDirection) { - // if we are not bezel movement - this.pan({ clientX: centerX, clientY: centerY }); - } else { - this._pullCoords = [centerX, centerY]; - } - this._lastX = centerX; this._lastY = centerY; } @@ -998,24 +970,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; this._lastX = centerX; this._lastY = centerY; - const screenBox = this._mainCont?.getBoundingClientRect(); - - // determine if we are using a bezel movement - if (screenBox) { - if (screenBox.right - centerX < 100) { - this._pullCoords = [centerX, centerY]; - this._pullDirection = 'right'; - } else if (centerX - screenBox.left < 100) { - this._pullCoords = [centerX, centerY]; - this._pullDirection = 'left'; - } else if (screenBox.bottom - centerY < 100) { - this._pullCoords = [centerX, centerY]; - this._pullDirection = 'bottom'; - } else if (centerY - screenBox.top < 100) { - this._pullCoords = [centerX, centerY]; - this._pullDirection = 'top'; - } - } this.removeMoveListeners(); this.addMoveListeners(); @@ -1027,19 +981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; cleanUpInteractions = () => { - switch (this._pullDirection) { - case 'left': - case 'right': - case 'top': - case 'bottom': - CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection); - } - - this._pullDirection = ''; - this._pullCoords = [0, 0]; - - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); this.removeMoveListeners(); this.removeEndListeners(); }; @@ -1380,7 +1321,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const { z, color, zIndex } = params.pair.layout; const { x, y, opacity } = this.Document._currentFrame === undefined - ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } + ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.childOpacity ? this.props.childOpacity() : this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame)); return { x: NumCast(x), @@ -1915,7 +1856,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> - <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}> + <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}> {this.layoutDoc._backgroundGridShow ? ( <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!!? @@ -1996,15 +1937,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection {this._firstRender ? this.placeholder : this.marqueeView} {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />} - <div - className={'pullpane-indicator'} - style={{ - display: this._pullDirection ? 'block' : 'none', - top: clientRect ? (this._pullDirection === 'bottom' ? this._pullCoords[1] - clientRect.y : 0) : 'auto', - left: clientRect ? (this._pullDirection === 'right' ? this._pullCoords[0] - clientRect.x : 0) : 'auto', - width: clientRect ? (this._pullDirection === 'left' ? this._pullCoords[0] - clientRect.left : this._pullDirection === 'right' ? clientRect.right - this._pullCoords[0] : clientRect.width) : 0, - height: clientRect ? (this._pullDirection === 'top' ? this._pullCoords[1] - clientRect.top : this._pullDirection === 'bottom' ? clientRect.bottom - this._pullCoords[1] : clientRect.height) : 0, - }}></div> { // uncomment to show snap lines <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}> diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 78f351f4f..e19e2d525 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable, trace } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { List } from '../../../fields/List'; @@ -6,13 +6,12 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DashColor, numberRange, OmitKeys } from '../../../Utils'; +import { numberRange } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { DocComponent } from '../DocComponent'; -import { InkingStroke } from '../InkingStroke'; import { StyleProp } from '../StyleProvider'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bcc55eab4..172adcafe 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -970,7 +970,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps collectionFilters = () => StrListCast(this.props.Document._docFilters); collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters); @computed get showFilterIcon() { - return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined; + return this.collectionFilters().length || this.collectionRangeDocFilters().length + ? 'hasFilter' + : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || this.props.docRangeFilters().length + ? 'inheritsFilter' + : undefined; } rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; panelHeight = () => this.props.PanelHeight() - this.headerMargin; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index a3d501153..0ff15f93b 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -831,16 +831,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // stretches vertically or horizontally depending on video orientation so video fits full screen fullScreenSize() { if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) { - return { height: '100%' }; - } else { - return { width: '100%' }; + //prettier-ignore + return ({ height: '100%' }); } + //prettier-ignore + return ({ width: '100%' }); } // for zoom slider, sets timeline waveform zoom - zoom = (zoom: number) => { - this.timeline?.setZoom(zoom); - }; + zoom = (zoom: number) => this.timeline?.setZoom(zoom); // plays link playLink = (doc: Doc) => { diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index d8dd074a5..85986ff27 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -1,9 +1,11 @@ -@import "../global/globalCssVariables.scss"; - +@import '../global/globalCssVariables.scss'; .webBox { height: 100%; - position: relative; + width: 100%; + top: 0; + left: 0; + position: absolute; display: flex; .webBox-sideResizer { @@ -84,7 +86,6 @@ background: none; } - .webBox-overlayCont { position: absolute; width: calc(100% - 40px); @@ -95,7 +96,7 @@ justify-content: center; align-items: center; overflow: hidden; - transition: left .5s; + transition: left 0.5s; pointer-events: all; .webBox-searchBar { @@ -158,7 +159,7 @@ left: 0; cursor: text; padding: 15px; - height: 100% + height: 100%; } .webBox-cont { @@ -235,7 +236,7 @@ height: 25px; align-items: center; - >svg { + > svg { margin: auto; } } @@ -257,4 +258,4 @@ } } } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index ca9f363c1..6c2e42f86 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as WebRequest from 'web-request'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; @@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; @@ -35,6 +35,7 @@ import { LinkDocPreview } from './LinkDocPreview'; import { VideoBox } from './VideoBox'; import './WebBox.scss'; import React = require('react'); +import { DragManager } from '../../util/DragManager'; const { CreateImage } = require('./WebBoxRenderer'); const _global = (window /* browser */ || global) /* node */ as any; const htmlToText = require('html-to-text'); @@ -818,6 +819,56 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } + @computed get webpage() { + const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); + const scale = previewScale * (this.props.NativeDimScaling?.() || 1); + const renderAnnotations = (docFilters?: () => string[]) => ( + <CollectionFreeFormView + {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} + renderDepth={this.props.renderDepth + 1} + isAnnotationOverlay={true} + fieldKey={this.annotationKey} + CollectionView={undefined} + setPreviewCursor={this.setPreviewCursor} + PanelWidth={this.panelWidth} + PanelHeight={this.panelHeight} + ScreenToLocalTransform={this.scrollXf} + NativeDimScaling={returnOne} + dropAction={'alias'} + docFilters={docFilters} + select={emptyFunction} + bringToFront={emptyFunction} + styleProvider={this.childStyleProvider} + whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + childPointerEvents={this.props.isContentActive() ? 'all' : undefined} + pointerEvents={this.annotationPointerEvents} + /> + ); + return ( + <div + className={'webBox-outerContent'} + ref={this._outerRef} + style={{ + height: `${100 / scale}%`, + pointerEvents, + }} + onWheel={StopEvent} // block wheel events from propagating since they're handled by the iframe + onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} + onPointerDown={this.onMarqueeDown}> + <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}> + {this.content} + {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>} + {renderAnnotations(this.opaqueFilter)} + {this.annotationLayer} + </div> + </div> + ); + } + @computed get searchUI() { return ( <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}> @@ -859,9 +910,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; - basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')]; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (doc.textInlineAnnotations) return 'none'; @@ -871,37 +921,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none'); render() { - const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const scale = previewScale * (this.props.NativeDimScaling?.() || 1); - const renderAnnotations = (docFilters?: () => string[]) => ( - <CollectionFreeFormView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} - renderDepth={this.props.renderDepth + 1} - isAnnotationOverlay={true} - fieldKey={this.annotationKey} - CollectionView={undefined} - setPreviewCursor={this.setPreviewCursor} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} - ScreenToLocalTransform={this.scrollXf} - NativeDimScaling={returnOne} - dropAction={'alias'} - docFilters={docFilters || this.basicFilter} - dontRenderDocuments={docFilters ? false : true} - select={emptyFunction} - bringToFront={emptyFunction} - whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocument} - styleProvider={this.childStyleProvider} - childPointerEvents={this.props.isContentActive() ? 'all' : undefined} - pointerEvents={this.annotationPointerEvents} - /> - ); return ( - <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? 'none' : undefined }}> + <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined }}> <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} /> <div className="webBox-container" @@ -911,27 +935,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps pointerEvents, }} onContextMenu={this.specificContextMenu}> - <div - className={'webBox-outerContent'} - ref={this._outerRef} - style={{ - height: `${100 / scale}%`, - pointerEvents, - }} - onWheel={e => { - e.stopPropagation(); - e.preventDefault(); - }} // block wheel events from propagating since they're handled by the iframe - onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} - onPointerDown={this.onMarqueeDown}> - <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}> - {this.content} - <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div> - {renderAnnotations(this.opaqueFilter)} - {SnappingManager.GetIsDragging() ? null : renderAnnotations()} - {this.annotationLayer} - </div> - </div> + {this.webpage} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}> <MarqueeAnnotator diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 2c83082b7..a45edfbca 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,6 +9,7 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; +import { DragManager } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -487,9 +488,8 @@ export class PDFViewer extends React.Component<IViewerProps> { overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1)); panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); - basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')]; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (doc.textInlineAnnotations) return 'none'; @@ -498,56 +498,43 @@ export class PDFViewer extends React.Component<IViewerProps> { return this.props.styleProvider?.(doc, props, property); }; - renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => ( - <CollectionFreeFormView - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} - isAnnotationOverlay={true} - fieldKey={this.props.fieldKey + '-annotations'} - setPreviewCursor={this.setPreviewCursor} - PanelHeight={this.panelHeight} - PanelWidth={this.panelWidth} - dropAction={'alias'} - select={emptyFunction} - bringToFront={emptyFunction} - docFilters={docFilters || this.basicFilter} - styleProvider={this.childStyleProvider} - dontRenderDocuments={dontRender} - CollectionView={undefined} - ScreenToLocalTransform={this.overlayTransform} - renderDepth={this.props.renderDepth + 1} - /> + renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => ( + <div + className="pdfViewerDash-overlay" + style={{ + mixBlendMode: mixBlendMode, + display: display, + transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, + }}> + <CollectionFreeFormView + {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} + renderDepth={this.props.renderDepth + 1} + isAnnotationOverlay={true} + fieldKey={this.props.fieldKey + '-annotations'} + CollectionView={undefined} + setPreviewCursor={this.setPreviewCursor} + PanelHeight={this.panelHeight} + PanelWidth={this.panelWidth} + ScreenToLocalTransform={this.overlayTransform} + dropAction={'alias'} + docFilters={docFilters} + select={emptyFunction} + bringToFront={emptyFunction} + styleProvider={this.childStyleProvider} + /> + </div> ); @computed get overlayTransparentAnnotations() { - return this.renderAnnotations(this.transparentFilter, false); + return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined); } @computed get overlayOpaqueAnnotations() { - return this.renderAnnotations(this.opaqueFilter, false); - } - @computed get overlayClickableAnnotations() { - return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>{this.renderAnnotations(undefined, true)}</div>; + return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined); } @computed get overlayLayer() { return ( <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}> - <div - className="pdfViewerDash-overlay" - style={{ - pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none', - mixBlendMode: 'multiply', - transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, - }}> - {this.overlayTransparentAnnotations} - </div> - <div - className="pdfViewerDash-overlay" - style={{ - pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none', - mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined, - transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, - }}> - {this.overlayOpaqueAnnotations} - {this.overlayClickableAnnotations} - </div> + {this.overlayTransparentAnnotations} + {this.overlayOpaqueAnnotations} </div> ); } |