aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2020-01-31 18:30:42 -0500
committerbob <bcz@cs.brown.edu>2020-01-31 18:30:42 -0500
commitd20a21384fc685082955cfbb9deb9c0f1176e7ad (patch)
tree76b0fe775943914674e332bd98eb671ee3175132
parent349a4c81c8e8864174c1965039b0046be137c98a (diff)
added multirow collection. cleaned up CollectionTimeview a bit.
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx5
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx9
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss34
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx259
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx56
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx116
8 files changed, 484 insertions, 8 deletions
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index dfcf93ef6..19a48d6ef 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -74,7 +74,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
}
_canClick = false;
_facetWidthOnDown = 0;
- @observable _facetWidth = 200;
+ @observable _facetWidth = 0;
onPointerDown = (e: React.PointerEvent) => {
this._canClick = true;
this._facetWidthOnDown = e.screenX;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 027cf2404..5a7bdef61 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -37,6 +37,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
import './CollectionView.scss';
import { CollectionViewBaseChrome } from './CollectionViewChromes';
import { CollectionTimeView } from './CollectionTimeView';
+import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy);
@@ -50,6 +51,7 @@ export enum CollectionViewType {
Stacking,
Masonry,
Multicolumn,
+ Multirow,
Pivot,
Time,
Carousel,
@@ -68,6 +70,7 @@ export namespace CollectionViewType {
["stacking", CollectionViewType.Stacking],
["masonry", CollectionViewType.Masonry],
["multicolumn", CollectionViewType.Multicolumn],
+ ["multirow", CollectionViewType.Multirow],
["pivot", CollectionViewType.Pivot],
["time", CollectionViewType.Time],
["carousel", CollectionViewType.Carousel],
@@ -183,6 +186,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />);
case CollectionViewType.Staff: return (<CollectionStaffView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView chromeCollapsed={true} key="collview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
+ case CollectionViewType.Multirow: return (<CollectionMultirowView chromeCollapsed={true} key="rpwview" {...props} ChromeHeight={this.chromeHeight} CollectionView={this} />);
case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); }
case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
@@ -227,6 +231,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
});
subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" });
subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" });
+ subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" });
subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" });
subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
subItems.push({ description: "Pivot", event: () => this.props.Document._viewType = CollectionViewType.Pivot, icon: "columns" });
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 9cb668d20..c5db6f10f 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -417,11 +417,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking</option>
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">Multicolumn</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">Pivot</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Time</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Carousel</option>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="11">Linear</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="7">MultiCol</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">MultiRow</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivot</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Time</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="11">Carousel</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="12">Linear</option>
</select>
<div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
<div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 8a1147c61..6e4af3520 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -10,7 +10,6 @@ import { ObservableMap, runInAction } from "mobx";
import { Id, ToString } from "../../../../new_fields/FieldSymbols";
import { ObjectField } from "../../../../new_fields/ObjectField";
import { RefField } from "../../../../new_fields/RefField";
-import { createPromiseCapability } from "../../../../../deploy/assets/pdf.worker";
interface PivotData {
type: string;
@@ -142,8 +141,11 @@ export function computeTimelineLayout(
poolData: ObservableMap<string, any>,
pivotDoc: Doc,
childDocs: Doc[],
- childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: any) => ViewDefResult[]
+ childPairs: { layout: Doc, data?: Doc }[],
+ panelDim: number[],
+ viewDefsToJSX: (views: any) => ViewDefResult[]
) {
+ const fieldKey = "data";
const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200);
const pivotDateGroups = new Map<number, Doc[]>();
@@ -159,6 +161,9 @@ export function computeTimelineLayout(
minTime = Math.min(num, minTime);
maxTime = Math.max(num, maxTime);
}
+ minTime = NumCast(pivotDoc[fieldKey + "-timelineMin"], minTime);
+ maxTime = NumCast(pivotDoc[fieldKey + "-timelineMax"], maxTime);
+ const curTime = Cast(pivotDoc[fieldKey + "-timelineCur"], "number", null);
const docMap = new Map<Doc, ViewDefBounds>();
const groupNames: PivotData[] = [];
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
new file mode 100644
index 000000000..ef4b4a19c
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -0,0 +1,34 @@
+.collectionMultirowView_contents {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ flex-direction: column;
+
+ .document-wrapper {
+ display: flex;
+ flex-direction: row;
+
+ .label-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ height: 20px;
+ }
+
+ }
+
+ .resizer {
+ cursor: ew-resize;
+ transition: 0.5s opacity ease;
+ display: flex;
+ flex-direction: row;
+
+ .internal {
+ width: 100%;
+ height: 100%;
+ transition: 0.5s background-color ease;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
new file mode 100644
index 000000000..e07985bb4
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -0,0 +1,259 @@
+import { observer } from 'mobx-react';
+import { makeInterface } from '../../../../new_fields/Schema';
+import { documentSchema } from '../../../../new_fields/documentSchemas';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
+import * as React from "react";
+import { Doc } from '../../../../new_fields/Doc';
+import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types';
+import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
+import { Utils } from '../../../../Utils';
+import "./collectionMultirowView.scss";
+import { computed, trace, observable, action } from 'mobx';
+import { Transform } from '../../../util/Transform';
+import HeightLabel from './MultirowHeightLabel';
+import ResizeBar from './MultirowResizer';
+import { undoBatch } from '../../../util/UndoManager';
+import { DragManager } from '../../../util/DragManager';
+
+type MultirowDocument = makeInterface<[typeof documentSchema]>;
+const MultirowDocument = makeInterface(documentSchema);
+
+interface HeightSpecifier {
+ magnitude: number;
+ unit: string;
+}
+
+interface LayoutData {
+ heightSpecifiers: HeightSpecifier[];
+ starSum: number;
+}
+
+export const HeightUnit = {
+ Pixel: "px",
+ Ratio: "*"
+};
+
+const resolvedUnits = Object.values(HeightUnit);
+const resizerHeight = 4;
+
+@observer
+export class CollectionMultirowView extends CollectionSubView(MultirowDocument) {
+
+ /**
+ * @returns the list of layout documents whose width unit is
+ * *, denoting that it will be displayed with a ratio, not fixed pixel, value
+ */
+ @computed
+ private get ratioDefinedDocs() {
+ return this.childLayoutPairs.map(({ layout }) => layout).filter(({ widthUnit }) => StrCast(widthUnit) === HeightUnit.Ratio);
+ }
+
+ /**
+ * This loops through all childLayoutPairs and extracts the values for widthUnit
+ * and widthMagnitude, ignoring any that are malformed. Additionally, it then
+ * normalizes the ratio values so that one * value is always 1, with the remaining
+ * values proportionate to that easily readable metric.
+ * @returns the list of the resolved width specifiers (unit and magnitude pairs)
+ * as well as the sum of the * coefficients, i.e. the ratio magnitudes
+ */
+ @computed
+ private get resolvedLayoutInformation(): LayoutData {
+ let starSum = 0;
+ const heightSpecifiers: HeightSpecifier[] = [];
+ this.childLayoutPairs.map(({ layout: { heightUnit, heightMagnitude } }) => {
+ const unit = StrCast(heightUnit);
+ const magnitude = NumCast(heightMagnitude);
+ if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
+ (unit === HeightUnit.Ratio) && (starSum += magnitude);
+ heightSpecifiers.push({ magnitude, unit });
+ }
+ /**
+ * Otherwise, the child document is ignored and the remaining
+ * space is allocated as if the document were absent from the child list
+ */
+ });
+
+ /**
+ * Here, since these values are all relative, adjustments during resizing or
+ * manual updating can, though their ratios remain the same, cause the values
+ * themselves to drift toward zero. Thus, whenever we change any of the values,
+ * we normalize everything (dividing by the smallest magnitude).
+ */
+ setTimeout(() => {
+ const { ratioDefinedDocs } = this;
+ if (this.childLayoutPairs.length) {
+ const minimum = Math.min(...ratioDefinedDocs.map(({ heightMagnitude }) => NumCast(heightMagnitude)));
+ if (minimum !== 0) {
+ ratioDefinedDocs.forEach(layout => layout.heightMagnitude = NumCast(layout.heightMagnitude) / minimum);
+ }
+ }
+ });
+
+ return { heightSpecifiers, starSum };
+ }
+
+ /**
+ * This returns the total quantity, in pixels, that this
+ * view needs to reserve for child documents that have
+ * (with higher priority) requested a fixed pixel width.
+ *
+ * If the underlying resolvedLayoutInformation returns null
+ * because we're waiting on promises to resolve, this value will be undefined as well.
+ */
+ @computed
+ private get totalFixedAllocation(): number | undefined {
+ return this.resolvedLayoutInformation?.heightSpecifiers.reduce(
+ (sum, { magnitude, unit }) => sum + (unit === HeightUnit.Pixel ? magnitude : 0), 0);
+ }
+
+ /**
+ * @returns the total quantity, in pixels, that this
+ * view needs to reserve for child documents that have
+ * (with lower priority) requested a certain relative proportion of the
+ * remaining pixel width not allocated for fixed widths.
+ *
+ * If the underlying totalFixedAllocation returns undefined
+ * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
+ */
+ @computed
+ private get totalRatioAllocation(): number | undefined {
+ const layoutInfoLen = this.resolvedLayoutInformation.heightSpecifiers.length;
+ if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
+ return this.props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1));
+ }
+ }
+
+ /**
+ * @returns the total quantity, in pixels, that
+ * 1* (relative / star unit) is worth. For example,
+ * if the configuration has three documents, with, respectively,
+ * widths of 2*, 2* and 1*, and the panel width returns 1000px,
+ * this accessor returns 1000 / (2 + 2 + 1), or 200px.
+ * Elsewhere, this is then multiplied by each relative-width
+ * document's (potentially decimal) * count to compute its actual width (400px, 400px and 200px).
+ *
+ * If the underlying totalRatioAllocation or this.resolveLayoutInformation return undefined
+ * because we're waiting indirectly on promises to resolve, this value will be undefined as well.
+ */
+ @computed
+ private get rowUnitLength(): number | undefined {
+ if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) {
+ return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum;
+ }
+ }
+
+ /**
+ * This wrapper function exists to prevent mobx from
+ * needlessly rerendering the internal ContentFittingDocumentViews
+ */
+ private getRowUnitLength = () => this.rowUnitLength;
+
+ /**
+ * @param layout the document whose transform we'd like to compute
+ * Given a layout document, this function
+ * returns the resolved width it has requested, in pixels.
+ * @returns the stored row width if already in pixels,
+ * or the ratio width evaluated to a pixel value
+ */
+ private lookupPixels = (layout: Doc): number => {
+ const rowUnitLength = this.rowUnitLength;
+ if (rowUnitLength === undefined) {
+ return 0; // we're still waiting on promises to resolve
+ }
+ let height = NumCast(layout.heightMagnitude);
+ if (StrCast(layout.heightUnit) === HeightUnit.Ratio) {
+ height *= rowUnitLength;
+ }
+ return height;
+ }
+
+ /**
+ * @returns the transform that will correctly place
+ * the document decorations box, shifted to the right by
+ * the sum of all the resolved row widths of the
+ * documents before the target.
+ */
+ private lookupIndividualTransform = (layout: Doc) => {
+ const rowUnitLength = this.rowUnitLength;
+ if (rowUnitLength === undefined) {
+ return Transform.Identity(); // we're still waiting on promises to resolve
+ }
+ let offset = 0;
+ for (const { layout: candidate } of this.childLayoutPairs) {
+ if (candidate === layout) {
+ return this.props.ScreenToLocalTransform().translate(0, -offset);
+ }
+ offset += this.lookupPixels(candidate) + resizerHeight;
+ }
+ return Transform.Identity(); // type coersion, this case should never be hit
+ }
+
+ @undoBatch
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ if (super.drop(e, de)) {
+ de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
+ d.heightUnit = "*";
+ d.heightMagnitude = 1;
+ }));
+ }
+ return false;
+ }
+
+
+ @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+
+ /**
+ * @returns the resolved list of rendered child documents, displayed
+ * at their resolved pixel widths, each separated by a resizer.
+ */
+ @computed
+ private get contents(): JSX.Element[] | null {
+ const { childLayoutPairs } = this;
+ const { Document, PanelWidth } = this.props;
+ const collector: JSX.Element[] = [];
+ for (let i = 0; i < childLayoutPairs.length; i++) {
+ const { layout } = childLayoutPairs[i];
+ collector.push(
+ <div
+ className={"document-wrapper"}
+ key={Utils.GenerateGuid()}
+ >
+ <ContentFittingDocumentView
+ {...this.props}
+ Document={layout}
+ DataDocument={layout.resolvedDataDoc as Doc}
+ CollectionDoc={this.props.Document}
+ PanelHeight={() => this.lookupPixels(layout)}
+ PanelWidth={() => PanelWidth() - (BoolCast(Document.showHeightLabels) ? 20 : 0)}
+ getTransform={() => this.lookupIndividualTransform(layout)}
+ onClick={this.onChildClickHandler}
+ renderDepth={this.props.renderDepth + 1}
+ />
+ <HeightLabel
+ layout={layout}
+ collectionDoc={Document}
+ />
+ </div>,
+ <ResizeBar
+ height={resizerHeight}
+ key={Utils.GenerateGuid()}
+ columnUnitLength={this.getRowUnitLength}
+ toTop={layout}
+ toBottom={childLayoutPairs[i + 1]?.layout}
+ />
+ );
+ }
+ collector.pop(); // removes the final extraneous resize bar
+ return collector;
+ }
+
+ render(): JSX.Element {
+ return (
+ <div className={"collectionMultirowView_contents"} ref={this.createDashEventsTarget}>
+ {this.contents}
+ </div>
+ );
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
new file mode 100644
index 000000000..56a2e868d
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -0,0 +1,56 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { computed } from "mobx";
+import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
+import { EditableView } from "../../EditableView";
+import { HeightUnit } from "./CollectionMultirowView";
+
+interface HeightLabelProps {
+ layout: Doc;
+ collectionDoc: Doc;
+ decimals?: number;
+}
+
+@observer
+export default class HeightLabel extends React.Component<HeightLabelProps> {
+
+ @computed
+ private get contents() {
+ const { layout, decimals } = this.props;
+ const getUnit = () => StrCast(layout.heightUnit);
+ const getMagnitude = () => String(+NumCast(layout.heightMagnitude).toFixed(decimals ?? 3));
+ return (
+ <div className={"label-wrapper"}>
+ <EditableView
+ GetValue={getMagnitude}
+ SetValue={value => {
+ const converted = Number(value);
+ if (!isNaN(converted) && converted > 0) {
+ layout.heightMagnitude = converted;
+ return true;
+ }
+ return false;
+ }}
+ contents={getMagnitude()}
+ />
+ <EditableView
+ GetValue={getUnit}
+ SetValue={value => {
+ if (Object.values(HeightUnit).includes(value)) {
+ layout.heightUnit = value;
+ return true;
+ }
+ return false;
+ }}
+ contents={getUnit()}
+ />
+ </div>
+ );
+ }
+
+ render() {
+ return BoolCast(this.props.collectionDoc.showHeightLabels) ? this.contents : (null);
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
new file mode 100644
index 000000000..20c6cd3df
--- /dev/null
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -0,0 +1,116 @@
+import * as React from "react";
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+import { Doc } from "../../../../new_fields/Doc";
+import { NumCast, StrCast } from "../../../../new_fields/Types";
+import { HeightUnit } from "./CollectionMultirowView";
+
+interface ResizerProps {
+ height: number;
+ columnUnitLength(): number | undefined;
+ toTop?: Doc;
+ toBottom?: Doc;
+}
+
+enum ResizeMode {
+ Global = "blue",
+ Pinned = "red",
+ Undefined = "black"
+}
+
+const resizerOpacity = 1;
+
+@observer
+export default class ResizeBar extends React.Component<ResizerProps> {
+ @observable private isHoverActive = false;
+ @observable private isResizingActive = false;
+ @observable private resizeMode = ResizeMode.Undefined;
+
+ @action
+ private registerResizing = (e: React.PointerEvent<HTMLDivElement>, mode: ResizeMode) => {
+ e.stopPropagation();
+ e.preventDefault();
+ this.resizeMode = mode;
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ window.addEventListener("pointermove", this.onPointerMove);
+ window.addEventListener("pointerup", this.onPointerUp);
+ this.isResizingActive = true;
+ }
+
+ private onPointerMove = ({ movementY }: PointerEvent) => {
+ const { toTop: toTop, toBottom: toBottom, columnUnitLength } = this.props;
+ const movingDown = movementY > 0;
+ const toNarrow = movingDown ? toBottom : toTop;
+ const toWiden = movingDown ? toTop : toBottom;
+ const unitLength = columnUnitLength();
+ if (unitLength) {
+ if (toNarrow) {
+ const { heightUnit, heightMagnitude } = toNarrow;
+ const scale = heightUnit === HeightUnit.Ratio ? unitLength : 1;
+ toNarrow.heightMagnitude = NumCast(heightMagnitude) - Math.abs(movementY) / scale;
+ }
+ if (this.resizeMode === ResizeMode.Pinned && toWiden) {
+ const { heightUnit, heightMagnitude } = toWiden;
+ const scale = heightUnit === HeightUnit.Ratio ? unitLength : 1;
+ toWiden.heightMagnitude = NumCast(heightMagnitude) + Math.abs(movementY) / scale;
+ }
+ }
+ }
+
+ private get isActivated() {
+ const { toTop, toBottom } = this.props;
+ if (toTop && toBottom) {
+ if (StrCast(toTop.heightUnit) === HeightUnit.Pixel && StrCast(toBottom.heightUnit) === HeightUnit.Pixel) {
+ return false;
+ }
+ return true;
+ } else if (toTop) {
+ if (StrCast(toTop.heightUnit) === HeightUnit.Pixel) {
+ return false;
+ }
+ return true;
+ } else if (toBottom) {
+ if (StrCast(toBottom.heightUnit) === HeightUnit.Pixel) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @action
+ private onPointerUp = () => {
+ this.resizeMode = ResizeMode.Undefined;
+ this.isResizingActive = false;
+ this.isHoverActive = false;
+ window.removeEventListener("pointermove", this.onPointerMove);
+ window.removeEventListener("pointerup", this.onPointerUp);
+ }
+
+ render() {
+ return (
+ <div
+ className={"resizer"}
+ style={{
+ height: this.props.height,
+ opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0
+ }}
+ onPointerEnter={action(() => this.isHoverActive = true)}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
+ >
+ <div
+ className={"internal"}
+ onPointerDown={e => this.registerResizing(e, ResizeMode.Pinned)}
+ style={{ backgroundColor: this.resizeMode }}
+ />
+ <div
+ className={"internal"}
+ onPointerDown={e => this.registerResizing(e, ResizeMode.Global)}
+ style={{ backgroundColor: this.resizeMode }}
+ />
+ </div>
+ );
+ }
+
+} \ No newline at end of file