diff options
author | anika-ahluwalia <anika.ahluwalia@gmail.com> | 2020-05-22 11:27:38 -0500 |
---|---|---|
committer | anika-ahluwalia <anika.ahluwalia@gmail.com> | 2020-05-22 11:27:38 -0500 |
commit | 2fa84b20fa657835510cb9be876817845d3df61b (patch) | |
tree | d07ababb415470fd003c1ac5b40f9d83475682d3 /src | |
parent | 755ea0668b559a2ae5f2c6dac86261db86e9ad2e (diff) | |
parent | fdbff9dbb60b4af8f6feba67feda5376263dd7ca (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into script_documents
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 3 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMasonryViewFieldRow.tsx | 10 | ||||
-rw-r--r-- | src/client/views/collections/CollectionPileView.scss | 3 | ||||
-rw-r--r-- | src/client/views/collections/CollectionPileView.tsx | 59 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 15 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.scss | 5 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 131 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 6 | ||||
-rw-r--r-- | src/client/views/presentationview/PresElementBox.tsx | 9 | ||||
-rw-r--r-- | src/fields/Doc.ts | 44 |
14 files changed, 147 insertions, 165 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c91a7d976..69f8f9b33 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -110,6 +110,7 @@ export interface DocumentOptions { _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor ) color?: string; // foreground color data doc _color?: string; // foreground color for each template layout doc (overrides color) + _clipWidth?: number; // percent transition from before to after in comparisonBox caption?: RichTextField; ignoreClick?: boolean; lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged @@ -559,7 +560,7 @@ export namespace Docs { } export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) { - return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", { targetDropAction: "alias", ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", { _clipWidth: 50, _backgroundColor: "gray", targetDropAction: "alias", ...options }); } export function AudioDocument(url: string, options: DocumentOptions = {}) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8841f7307..b7a75dfb9 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -5,7 +5,7 @@ import { faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, - faThumbtack, faTree, faTv, faUndoAlt, faVideo + faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -118,7 +118,7 @@ export class MainView extends React.Component { faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, - faThumbtack, faTree, faTv, faUndoAlt, faVideo); + faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 1e3bf11de..cc7a9f5ac 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -240,13 +240,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr toggle: this.toggleVisibility, color: this.color }; + const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled'); + const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `; return this.collapsed ? (null) : <div style={{ position: "relative" }}> - {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? + {showChrome ? <div className="collectionStackingView-addDocumentButton" style={{ - width: style.columnWidth / style.numGroupColumns, - padding: NumCast(this.props.parent.layoutDoc._yPadding) + //width: style.columnWidth / style.numGroupColumns, + padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px` }}> <EditableView {...newEditableViewProps} /> </div> : null @@ -254,7 +256,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr <div className={`collectionStackingView-masonryGrid`} ref={this._contRef} style={{ - padding: `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px`, + padding: stackPad, width: this.props.parent.NodeWidth, gridGap: this.props.parent.gridGap, gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ""), diff --git a/src/client/views/collections/CollectionPileView.scss b/src/client/views/collections/CollectionPileView.scss index ac874b663..48d07e42b 100644 --- a/src/client/views/collections/CollectionPileView.scss +++ b/src/client/views/collections/CollectionPileView.scss @@ -5,4 +5,7 @@ height: 100%; width: 100%; overflow: visible; + .collectionPileView-innards { + width:100%; + } } diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index e3bcf2a21..020a87b2e 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -1,18 +1,17 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { HeightSym, Opt, WidthSym, Doc } from "../../../fields/Doc"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, NumCast, StrCast } from "../../../fields/Types"; -import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionPileView.scss"; import React = require("react"); import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; -import { UndoManager } from "../../util/UndoManager"; +import { UndoManager, undoBatch } from "../../util/UndoManager"; import { SnappingManager } from "../../util/SnappingManager"; +import { DragManager } from "../../util/DragManager"; @observer export class CollectionPileView extends CollectionSubView(doc => doc) { @@ -20,10 +19,12 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { _doubleTap: boolean | undefined = false; _originalChrome: string = ""; @observable _contentsActive = true; - @observable _layoutEngine = "pass"; @observable _collapsed: boolean = false; @observable _childClickedScript: Opt<ScriptField>; componentDidMount() { + if (this.layoutEngine() !== "pass" && this.layoutEngine() !== "starburst") { + this.Document._layoutEngine = "pass"; + } this._originalChrome = StrCast(this.layoutDoc._chromeStatus); this.layoutDoc._chromeStatus = "disabled"; this.layoutDoc.hideFilterView = true; @@ -33,49 +34,54 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { this.layoutDoc._chromeStatus = this._originalChrome; } - layoutEngine = () => this._layoutEngine; + layoutEngine = () => StrCast(this.Document._layoutEngine); @computed get contents() { - return <div className="collectionPileView-innards" style={{ - width: "100%", - pointerEvents: this.layoutEngine() !== "pass" && (this.props.active() || this.layoutEngine() === "starburst") ? undefined : "none" - }} > + return <div className="collectionPileView-innards" style={{ pointerEvents: this.layoutEngine() === "starburst" ? undefined : "none" }} > <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} /> </div>; } - - specificMenu = (e: React.MouseEvent) => { - const layoutItems: ContextMenuProps[] = []; - const doc = this.props.Document; - - ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); - } - toggleStarburst = action(() => { - if (this._layoutEngine === 'starburst') { + if (this.layoutEngine() === 'starburst') { const defaultSize = 110; this.layoutDoc._overflow = undefined; + this.childDocs.forEach(d => Doc.iconify(d)); this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize); - this._layoutEngine = 'pass'; + Doc.pileup(this.childDocs); + this.layoutDoc._panX = 0; + this.layoutDoc._panY = -10; + this.props.Document._layoutEngine = 'pass'; } else { const defaultSize = 25; this.layoutDoc._overflow = 'visible'; !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500); !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); - if (this._layoutEngine === 'pass') { + if (this.layoutEngine() === 'pass') { this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym](); } + this.layoutDoc._panX = this.layoutDoc._panY = 0; this.layoutDoc._width = this.layoutDoc._height = defaultSize; - this._layoutEngine = 'starburst'; + this.props.Document._layoutEngine = 'starburst'; } }); + @undoBatch + @action + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { + if (super.onInternalDrop(e, de)) { + if (de.complete.docDragData) { + Doc.pileup(this.childDocs); + } + } + return true; + } + _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { let dist = 0; @@ -107,20 +113,17 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { } onClick = (e: React.MouseEvent) => { - if (e.button === 0 && (this._doubleTap || this.layoutEngine() === "starburst")) { + if (e.button === 0 && this._doubleTap) { SelectionManager.DeselectAll(); this.toggleStarburst(); e.stopPropagation(); } - // else if (this.layoutEngine() === "pass") { - // runInAction(() => this._contentsActive = false); - // setTimeout(action(() => this._contentsActive = true), 300); - // } } render() { - return <div className={"collectionPileView"} onContextMenu={this.specificMenu} onClick={this.onClick} onPointerDown={this.pointerDown} + return <div className={"collectionPileView"} onClick={this.onClick} onPointerDown={this.pointerDown} + ref={this.createDashEventsTarget} style={{ width: this.props.PanelWidth(), height: `calc(100% - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}> {this.contents} </div>; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 53acc15c3..a6b0d03b8 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -451,7 +451,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: if (generatedDocuments.length) { const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d)); if (set) { - addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!)); + addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!)!); } else { generatedDocuments.forEach(addDocument); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 972c09484..1611b6935 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -207,7 +207,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const d = droppedDocs[i]; const layoutDoc = Doc.Layout(d); if (this.Document.currentTimecode !== undefined && !this.props.isAnnotationOverlay) { - CollectionFreeFormDocumentView.setValues(this.Document.currentTimecode, d, x + NumCast(d.x) - dropX, y + NumCast(d.y) - dropY, Cast(d.opacity, "number", null)); + const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.displayTimecode, 1000)); + CollectionFreeFormDocumentView.setValues(this.Document.currentTimecode, d, x + vals.x - dropX, y + vals.y - dropY, vals.opacity); } else { d.x = x + NumCast(d.x) - dropX; d.y = y + NumCast(d.y) - dropY; @@ -1362,7 +1363,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} <CollectionFreeFormOverlayView elements={this.elementFunc} /> - {this.isAnnotationOverlay || !this.props.isSelected() ? (null) : + {this.isAnnotationOverlay || !this.props.isSelected() || this.props.Document._viewType === CollectionViewType.Pile ? (null) : <> <div key="back" className="backKeyframe" onClick={this.prevKeyframe}> <FontAwesomeIcon icon={"caret-left"} size={"lg"} /> diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 57f484214..a4120f958 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -100,13 +100,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF } public static setupKeyframes(docs: Doc[], timecode: number, collection: Doc) { - docs.forEach(doc => { - doc["x-indexed"] = new List<number>(numberRange(timecode).map(i => undefined) as any as number[]); - doc["y-indexed"] = new List<number>(numberRange(timecode).map(i => undefined) as any as number[]); - doc["opacity-indexed"] = new List<number>(numberRange(timecode).map(i => 0)); - (doc["x-indexed"] as any).push(NumCast(doc.x)); - (doc["y-indexed"] as any).push(NumCast(doc.y)); - (doc["opacity-indexed"] as any).push(NumCast(doc.opacity, 1)); + docs.forEach((doc, i) => { + const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]); + const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]); + xlist[Math.max(i - 1)] = xlist[timecode + 1] = NumCast(doc.x); + ylist[Math.max(i - 1)] = ylist[timecode + 1] = NumCast(doc.y); + doc["x-indexed"] = xlist; + doc["y-indexed"] = ylist; + doc["opacity-indexed"] = new List<number>(numberRange(timecode).map(i => 1)); doc.displayTimecode = ComputedField.MakeFunction("collection ? collection.currentTimecode : 0", {}, { collection }); doc.x = ComputedField.MakeInterpolated("x", "displayTimecode"); doc.y = ComputedField.MakeInterpolated("y", "displayTimecode"); diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss index 3d48d96e2..7849c9976 100644 --- a/src/client/views/nodes/ComparisonBox.scss +++ b/src/client/views/nodes/ComparisonBox.scss @@ -2,7 +2,6 @@ border-radius: inherit; width: 100%; height: 100%; - background-color: grey; position: absolute; z-index: 0; pointer-events: none; @@ -17,7 +16,6 @@ .beforeBox-cont { height: 100%; overflow: hidden; - background-color: rgb(240, 240, 240); } } @@ -26,7 +24,7 @@ height: 100%; width: 3px; display: inline-block; - background: gray; + background: white; cursor: ew-resize; .slide-handle { position: absolute; @@ -48,7 +46,6 @@ height: 100%; width: 100%; overflow: hidden; - background-color: lightgray; } .clear-button { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 7a4d40db1..f79fe6e78 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,13 +1,10 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEye } from '@fortawesome/free-regular-svg-icons'; -import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush, faTimes, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction, Lambda } from 'mobx'; +import { action, computed, observable, runInAction, Lambda, IReactionDisposer } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { createSchema, makeInterface } from '../../../fields/Schema'; -import { NumCast, Cast } from '../../../fields/Types'; +import { NumCast, Cast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; @@ -17,9 +14,7 @@ import { ContentFittingDocumentView } from './ContentFittingDocumentView'; import { undoBatch } from '../../util/UndoManager'; import { setupMoveUpEvents, emptyFunction } from '../../../Utils'; import { SnappingManager } from '../../util/SnappingManager'; - -library.add(faImage, faEye as any, faPaintBrush, faBrain); -library.add(faFileAudio, faAsterisk); +import { DocumentViewProps } from './DocumentView'; export const comparisonSchema = createSchema({}); @@ -28,17 +23,16 @@ const ComparisonDocument = makeInterface(comparisonSchema, documentSchema); @observer export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, ComparisonDocument>(ComparisonDocument) { - protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } + protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; + private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; - private _beforeDropDisposer?: DragManager.DragDropDisposer; - private _afterDropDisposer?: DragManager.DragDropDisposer; - private resizeUpdater: Lambda | undefined; + @observable _animating = ""; - protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string) => { + protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => { + this._disposers[disposerId]?.(); if (ele) { - return DragManager.MakeDropTarget(ele, (event, dropEvent) => this.dropHandler(event, dropEvent, fieldKey), this.layoutDoc); + this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.dropHandler(e, dropEvent, fieldKey), this.layoutDoc); } } @@ -48,36 +42,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments; if (droppedDocs?.length) { this.dataDoc[fieldKey] = droppedDocs[0]; - droppedDocs[0].isBackgound = true; } } - componentWillMount() { - this.dataDoc.clipWidth = this.props.PanelWidth() / 2; - - //preserve before/after ratio during resizing - this.resizeUpdater = computed(() => this.props.PanelWidth()).observe(({ oldValue, newValue }) => - this.dataDoc.clipWidth = NumCast(this.dataDoc.clipWidth) / (oldValue || 0) * newValue - ); - } - - componentWillUnmount() { - this.resizeUpdater?.(); - } - private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => { setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, action(() => { - this._animating = true; - this.dataDoc.clipWidth = targetWidth; - setTimeout(action(() => this._animating = false), 1000); + this._animating = "all 1s"; + this.layoutDoc._clipWidth = targetWidth * 100 / this.props.PanelWidth(); + setTimeout(action(() => this._animating = ""), 1000); }), false); } @action private onPointerMove = ({ movementX }: PointerEvent) => { - const width = movementX * this.props.ScreenToLocalTransform().Scale + NumCast(this.dataDoc.clipWidth); + const width = movementX * this.props.ScreenToLocalTransform().Scale + NumCast(this.layoutDoc._clipWidth) / 100 * this.props.PanelWidth(); if (width && width > 5 && width < this.props.PanelWidth()) { - this.dataDoc.clipWidth = width; + this.layoutDoc._clipWidth = width * 100 / this.props.PanelWidth(); } return false; } @@ -85,66 +65,43 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C @undoBatch clearDoc = (e: React.MouseEvent, fieldKey: string) => { e.stopPropagation; - e.preventDefault; delete this.dataDoc[fieldKey]; } - @observable _animating = false; render() { - const beforeDoc = Cast(this.dataDoc.beforeDoc, Doc, null); - const afterDoc = Cast(this.dataDoc.afterDoc, Doc, null); - const clipWidth = NumCast(this.dataDoc.clipWidth); + const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%"; + const childProps: DocumentViewProps = { ...this.props, pointerEvents: false, parentActive: this.props.active }; + const clearButton = (which: string) => { + return <div className={`clear-button ${which}`} onPointerDown={e => e.stopPropagation()} onClick={e => this.clearDoc(e, `${which}Doc`)}> + <FontAwesomeIcon className={`clear-button ${which}`} icon={"times"} size="sm" /> + </div> + } + const displayDoc = (which: string) => { + const whichDoc = Cast(this.dataDoc[`${which}Doc`], Doc, null); + return whichDoc ? <> + <ContentFittingDocumentView {...childProps} Document={whichDoc} /> + {clearButton(which)} + </> : // placeholder image if doc is missing + <div className="placeholder"> + <FontAwesomeIcon className="upload-icon" icon={"cloud-upload-alt"} size="lg" /> + </div> + } + const displayBox = (which: string, index: number, cover: number) => { + return <div className={`${which}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} + onPointerDown={e => this.registerSliding(e, cover)} + ref={ele => this.createDropTarget(ele, `${which}Doc`, index)} > + {displayDoc(which)} + </div>; + } + return ( <div className={`comparisonBox${this.active() || SnappingManager.GetIsDragging() ? "-interactive" : ""}`}> - <div className="afterBox-cont" key={"after"} onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() - 5)} - ref={(ele) => { - this._afterDropDisposer?.(); - this._afterDropDisposer = this.createDropTarget(ele, "afterDoc"); - }}> - {afterDoc ? <> - <ContentFittingDocumentView {...this.props} - Document={afterDoc} - pointerEvents={false} - parentActive={this.props.active} - /> - <div className="clear-button after" onClick={e => this.clearDoc(e, "afterDoc")}> - <FontAwesomeIcon className="clear-button after" icon={faTimes} size="sm" /> - </div> - </> : - <div className="placeholder"> - <FontAwesomeIcon className="upload-icon" icon={faCloudUploadAlt} size="lg" /> - </div>} - </div> - <div className="clip-div" onPointerDown={e => this.registerSliding(e, 5)} style={{ width: clipWidth + "px", transition: this._animating ? "all 1s" : undefined }}> - {/* wraps around before image and slider bar */} - <div - className="beforeBox-cont" - key={"before"} - ref={(ele) => { - this._beforeDropDisposer?.(); - this._beforeDropDisposer = this.createDropTarget(ele, "beforeDoc"); - }} - style={{ width: this.props.PanelWidth() }}> - { - beforeDoc ? - <> - <ContentFittingDocumentView {...this.props} - Document={beforeDoc} - pointerEvents={false} - parentActive={this.props.active} /> - <div className="clear-button before" onClick={e => this.clearDoc(e, "beforeDoc")}> - <FontAwesomeIcon className="clear-button before" icon={faTimes} size="sm" /> - </div> - </> - : - <div className="placeholder"> - <FontAwesomeIcon className="upload-icon" icon={faCloudUploadAlt} size="lg" /> - </div> - } - </div> + {displayBox("after", 1, this.props.PanelWidth() - 5)} + <div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}> + {displayBox("before", 0, 5)} </div> - <div className="slide-bar" style={{ left: `calc(${NumCast(this.dataDoc.clipWidth) * 100 / this.props.PanelWidth()}% - 0.5px)` }}> + <div className="slide-bar" style={{ left: `calc(${clipWidth} - 0.5px)` }}> <div className="slide-handle" /> </div> </div >); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 993cabc36..00d19752f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1111,6 +1111,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu }), 400); }); + renderLock() { + return (this.Document.isBackground !== undefined || this.isSelected(false)) && + ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) && + this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ? + <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> + <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" /> + </div> + : (null); + } + render() { if (!(this.props.Document instanceof Doc)) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); @@ -1161,11 +1171,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu <div className="documentView-contentBlocker" /> </> : this.innards} - {(this.Document.isBackground !== undefined || this.isSelected(false)) && (this.Document.type === DocumentType.COL || this.Document.type === DocumentType.IMG) && this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ? - <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> - <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" /> - </div> - : (null)} + {this.renderLock()} </div>; { this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined; } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 77abfef1d..6913dfbc7 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -266,7 +266,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) { requestImageSize(imgPath).then(action((inquiredSize: any) => { const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180; - const rotatedNativeSize = rotation === 90 || rotation === 270 ? { height: inquiredSize.width, width: inquiredSize.height } : inquiredSize; + const rotatedNativeSize = { width: inquiredSize.width, height: inquiredSize.height }; + if (inquiredSize.orientation === 6 || rotation === 90 || rotation === 270) { + rotatedNativeSize.width = inquiredSize.height; + rotatedNativeSize.height = inquiredSize.width; + } const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width; if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) { this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index a2a6882b9..526a3dbf4 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -100,13 +100,13 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc e.stopPropagation(); this.rootDoc.presProgressivize = !this.rootDoc.presProgressivize; const rootTarget = Cast(this.rootDoc.presentationTargetDoc, Doc, null); + const docs = DocListCast(rootTarget[Doc.LayoutFieldKey(rootTarget)]); if (this.rootDoc.presProgressivize && !rootTarget?.lastTimecode) { - const docs = DocListCast(rootTarget[Doc.LayoutFieldKey(rootTarget)]); rootTarget.currentTimecode = 0; CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, this.presBox); - docs.forEach((d, i) => numberRange(docs.length - i).forEach(f => Cast(d["opacity-indexed"], listSpec("number"), [])[f + i] = 1)); rootTarget.lastTimecode = docs.length - 1; } + docs.forEach((d, i) => i && numberRange(i).forEach(f => Cast(d["opacity-indexed"], listSpec("number"), [])[f] = 0)); } /** @@ -216,7 +216,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc <strong className="presElementBox-name"> {`${this.indexInPres + 1}. ${this.targetDoc?.title}`} </strong> - <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument?.(this.rootDoc)}>X</button> + <button className="presElementBox-closeIcon" onPointerDown={e => e.stopPropagation()} onClick={e => { + this.props.removeDocument?.(this.rootDoc); + e.stopPropagation(); + }}>X</button> <br /> </>} <div className="presElementBox-buttons"> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9f38f4369..1ea686cbb 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1028,28 +1028,32 @@ export namespace Doc { if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); } - export function pileup(selected: Doc[], x: number, y: number) { - const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true }); + export function pileup(docList: Doc[], x?: number, y?: number) { let w = 0, h = 0; - selected.forEach((d, i) => { - Doc.iconify(d); - w = Math.max(d[WidthSym](), w); - h = Math.max(d[HeightSym](), h); - }); - h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable - selected.forEach((d, i) => { - d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; - d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; - d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + runInAction(() => { + docList.forEach(d => { + Doc.iconify(d); + w = Math.max(d[WidthSym](), w); + h = Math.max(d[HeightSym](), h); + }); + h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable + docList.forEach((d, i) => { + d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2; + d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2; + d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + }); }); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; - newCollection._width = newCollection._height = 110; - //newCollection.borderRounding = "40px"; - newCollection._jitterRotation = 10; - newCollection._backgroundColor = "gray"; - newCollection._overflow = "visible"; - return newCollection; + if (x !== undefined && y !== undefined) { + const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; + newCollection._width = newCollection._height = 110; + //newCollection.borderRounding = "40px"; + newCollection._jitterRotation = 10; + newCollection._backgroundColor = "gray"; + newCollection._overflow = "visible"; + return newCollection; + } } |