aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ComparisonBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ComparisonBox.tsx')
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
new file mode 100644
index 000000000..7b35b2811
--- /dev/null
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -0,0 +1,169 @@
+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 { observer } from "mobx-react";
+import { Doc } from '../../../new_fields/Doc';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { Id } from '../../../new_fields/FieldSymbols';
+import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { NumCast, StrCast } from '../../../new_fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
+import { FieldView, FieldViewProps } from './FieldView';
+import "./ComparisonBox.scss";
+import React = require("react");
+import { ContentFittingDocumentView } from './ContentFittingDocumentView';
+import { undoBatch } from '../../util/UndoManager';
+
+library.add(faImage, faEye as any, faPaintBrush, faBrain);
+library.add(faFileAudio, faAsterisk);
+
+export const pageSchema = createSchema({});
+
+type ComparisonDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
+const ComparisonDocument = makeInterface(pageSchema, 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); }
+
+ private _beforeDropDisposer?: DragManager.DragDropDisposer;
+ private _afterDropDisposer?: DragManager.DragDropDisposer;
+
+ protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string) => {
+ if (ele) {
+ this.props.Document.targetDropAction = "alias";
+ return DragManager.MakeDropTarget(ele, (event, dropEvent) => this.dropHandler(event, dropEvent, fieldKey), this.props.Document);
+ }
+ }
+
+ @undoBatch
+ private dropHandler = (event: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
+ event.stopPropagation();
+ const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
+ if (droppedDocs?.length) {
+ this.props.Document[fieldKey] = droppedDocs[0];
+ }
+ }
+
+ @action
+ private registerSliding = (e: React.PointerEvent<HTMLDivElement>) => {
+ e.stopPropagation();
+ e.preventDefault();
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ window.addEventListener("pointermove", this.onPointerMove);
+ window.addEventListener("pointerup", this.onPointerUp);
+ }
+
+ private resizeUpdater: Lambda | undefined;
+ componentWillMount() {
+ this.props.Document.clipWidth = this.props.PanelWidth() / 2;
+
+ //preserve before/after ratio during resizing
+ this.resizeUpdater = computed(() => this.props.PanelWidth()).observe(({ oldValue, newValue }) =>
+ this.props.Document.clipWidth = NumCast(this.props.Document.clipWidth) / NumCast(oldValue) * newValue
+ );
+ }
+
+ componentWillUnmount() {
+ if (this.resizeUpdater) this.resizeUpdater();
+ }
+
+ private onPointerMove = ({ movementX }: PointerEvent) => {
+ const width = movementX * this.props.ScreenToLocalTransform().Scale + NumCast(this.props.Document.clipWidth);
+ if (width && width > 5 && width < this.props.PanelWidth()) {
+ this.props.Document.clipWidth = width;
+ }
+ }
+
+ @action
+ private onPointerUp = () => {
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ @undoBatch
+ clearBeforeDoc = (e: React.MouseEvent) => {
+ e.stopPropagation;
+ e.preventDefault;
+ delete this.props.Document.beforeDoc;
+ }
+
+ @undoBatch
+ clearAfterDoc = (e: React.MouseEvent) => {
+ e.stopPropagation;
+ e.preventDefault;
+ delete this.props.Document.afterDoc;
+ }
+
+ get fieldKey() {
+ return this.props.fieldKey.startsWith("@") ? StrCast(this.props.Document[this.props.fieldKey]) : this.props.fieldKey;
+ }
+
+ render() {
+ const beforeDoc = this.props.Document.beforeDoc as Doc;
+ const afterDoc = this.props.Document.afterDoc as Doc;
+ const clipWidth = this.props.Document.clipWidth as Number;
+ return (
+ <div className="comparisonBox">
+ <div className="content-wrapper">
+ <div className="clip-div" style={{ width: clipWidth + "px" }}>
+ {/* wraps around before image and slider bar */}
+ <div
+ className="beforeBox-cont"
+ key={this.props.Document[Id]}
+ ref={(ele) => {
+ this._beforeDropDisposer && this._beforeDropDisposer();
+ this._beforeDropDisposer = this.createDropTarget(ele, "beforeDoc");
+ }}
+ style={{ width: this.props.PanelWidth() }}>
+ {
+ beforeDoc ?
+ <>
+ <ContentFittingDocumentView {...this.props}
+ Document={beforeDoc}
+ getTransform={this.props.ScreenToLocalTransform} />
+ <div className="clear-button before" onClick={(e) => this.clearBeforeDoc(e)}>
+ <FontAwesomeIcon className="clear-button before" icon={faTimes} size="sm" />
+ </div>
+ </>
+ :
+ <div className="placeholder">
+ <FontAwesomeIcon className="upload-icon" icon={faCloudUploadAlt} size="lg" />
+ </div>
+ }
+ </div>
+ <div className="slide-bar" onPointerDown={e => this.registerSliding(e)} />
+ </div>
+ <div
+ className="afterBox-cont"
+ key={this.props.Document[Id]}
+ ref={(ele) => {
+ this._afterDropDisposer && this._afterDropDisposer();
+ this._afterDropDisposer = this.createDropTarget(ele, "afterDoc");
+ }}>
+ {
+ afterDoc ?
+ <>
+ <ContentFittingDocumentView {...this.props}
+ Document={afterDoc}
+ getTransform={this.props.ScreenToLocalTransform} />
+ <div className="clear-button after" onClick={(e) => this.clearAfterDoc(e)}>
+ <FontAwesomeIcon className="clear-button after" icon={faTimes} size="sm" />
+ </div>
+ </>
+ :
+ <div className="placeholder">
+ <FontAwesomeIcon className="upload-icon" icon={faCloudUploadAlt} size="lg" />
+ </div>
+ }
+ </div>
+ </div>
+ </div >);
+ }
+} \ No newline at end of file