1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
import { observer } from 'mobx-react';
import { makeInterface, listSpec } from '../../new_fields/Schema';
import { documentSchema } from '../../new_fields/documentSchemas';
import { CollectionSubView } from './collections/CollectionSubView';
import { DragManager } from '../util/DragManager';
import * as React from "react";
import { Doc, DocListCast } from '../../new_fields/Doc';
import { NumCast, Cast, StrCast } from '../../new_fields/Types';
import { List } from '../../new_fields/List';
import { ContentFittingDocumentView } from './nodes/ContentFittingDocumentView';
import { Utils } from '../../Utils';
import { Transform } from '../util/Transform';
import "./collectionMulticolumnView.scss";
import { computed } from 'mobx';
type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
const MulticolumnDocument = makeInterface(documentSchema);
interface LayoutUnit {
config: Doc;
target: Doc;
}
interface Fixed extends LayoutUnit {
pixels: number;
}
interface Proportional extends LayoutUnit {
ratio: number;
}
@observer
export default class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
private _dropDisposer?: DragManager.DragDropDisposer;
private get configuration() {
const { Document } = this.props;
if (!Document.multicolumnData) {
Document.multicolumnData = new List<Doc>();
}
return DocListCast(this.Document.multicolumnData);
}
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer && this._dropDisposer();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
}
}
getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
if (!ele.current) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
return new Transform(-translateX, -translateY, 1 / scale);
}
public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); }
@computed
private get layoutInformation() {
const fixed: Fixed[] = [];
const proportional: Proportional[] = [];
let ratioSum = 0;
for (const config of this.configuration) {
const { columnWidth, target } = config;
if (!(target instanceof Doc)) {
// we're still waiting on promises, so it's not worth rendering anything yet
return (null);
}
const widthSpecifier = Cast(columnWidth, "number");
let matches: RegExpExecArray | null;
if (widthSpecifier !== undefined) {
// we've gotten a number, referring to a pixel value
fixed.push({ config, target, pixels: widthSpecifier });
} else if ((matches = /^(\d+(\.\d+)?)\*/.exec(StrCast(columnWidth))) !== null) {
// we've gotten a proportional measure, like 1.8*
const ratio = Number(matches[1]);
ratioSum += ratio;
proportional.push({ config, target, ratio });
}
// otherwise, the particular configuration entry is ignored and the remaining
// space is allocated as if the document were absent from the configuration list
}
return { fixed, proportional, ratioSum };
}
@computed private get totalFixedPool() {
return this.layoutInformation?.fixed.reduce((sum, unit) => sum + unit.pixels, 0);
}
@computed private get totalProportionalPool() {
const { totalFixedPool } = this;
return totalFixedPool !== undefined ? this.props.PanelWidth() - totalFixedPool : undefined;
}
@computed private get columnUnitLength() {
const layout = this.layoutInformation;
const { totalProportionalPool } = this;
if (layout !== null && totalProportionalPool !== undefined) {
const { ratioSum, proportional } = layout;
return (totalProportionalPool - 2 * (proportional.length - 1)) / ratioSum;
}
return undefined;
}
@computed
private get contents(): JSX.Element[] | null {
const layout = this.layoutInformation;
if (layout === null) {
return (null);
}
const { fixed, proportional } = layout;
const { columnUnitLength } = this;
if (columnUnitLength === undefined) {
return (null);
}
const { GenerateGuid } = Utils;
const toView = ({ target, pixels }: Fixed) =>
<ContentFittingDocumentView
{...this.props}
key={GenerateGuid()}
Document={target}
DataDocument={undefined}
PanelWidth={() => pixels}
getTransform={this.props.ScreenToLocalTransform}
/>;
const collector: JSX.Element[] = fixed.map(toView);
const resolvedColumns = proportional.map(({ target, ratio, config }) => ({ target, pixels: ratio * columnUnitLength, config }));
for (let i = 0; i < resolvedColumns.length; i++) {
collector.push(toView(resolvedColumns[i]));
collector.push(
<MulticolumnSpacer
key={GenerateGuid()}
columnBaseUnit={columnUnitLength}
toLeft={resolvedColumns[i].config}
toRight={resolvedColumns[i + 1]?.config}
/>
);
}
collector.pop();
return collector;
}
render() {
return (
<div className={"collectionMulticolumnView_contents"}>
{this.contents}
</div>
);
}
}
interface SpacerProps {
columnBaseUnit: number;
toLeft?: Doc;
toRight?: Doc;
}
class MulticolumnSpacer extends React.Component<SpacerProps> {
private registerResizing = (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 onPointerMove = ({ movementX }: PointerEvent) => {
const { toLeft, toRight, columnBaseUnit } = this.props;
const target = movementX > 0 ? toRight : toLeft;
if (target) {
let widthSpecifier = Number(StrCast(target.columnWidth).replace("*", ""));
widthSpecifier -= Math.abs(movementX) / columnBaseUnit;
target.columnWidth = `${widthSpecifier}*`;
}
}
private onPointerUp = () => {
window.removeEventListener("pointermove", this.onPointerMove);
window.removeEventListener("pointerup", this.onPointerUp);
}
render() {
return (
<div
className={"spacer"}
onPointerDown={this.registerResizing}
/>
);
}
}
|