diff options
author | Sam Wilkins <samwilkins333@gmail.com> | 2020-01-16 22:09:55 -0500 |
---|---|---|
committer | Sam Wilkins <samwilkins333@gmail.com> | 2020-01-16 22:09:55 -0500 |
commit | 1055a2db6b14cbcd1eb63727995a51153ba79128 (patch) | |
tree | 45346a909be1262d9eb86da87517e2ce64939a34 /src | |
parent | 42b20c17047037ddc2c8e7f3c3625276ea8df5fa (diff) |
logical differentiation between unit and magnitude for multicolumn view config width data, additional commenting and cleanup
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/CollectionMulticolumnView.tsx | 221 |
1 files changed, 116 insertions, 105 deletions
diff --git a/src/client/views/CollectionMulticolumnView.tsx b/src/client/views/CollectionMulticolumnView.tsx index 653ff5b84..61ce8d99c 100644 --- a/src/client/views/CollectionMulticolumnView.tsx +++ b/src/client/views/CollectionMulticolumnView.tsx @@ -16,27 +16,31 @@ import { computed } from 'mobx'; type MulticolumnDocument = makeInterface<[typeof documentSchema]>; const MulticolumnDocument = makeInterface(documentSchema); -interface UnitBase { +interface Unresolved { config: Doc; target: Doc; + magnitude: number; + unit: string; } -type WidthSpecifier = { pixels: number } | { ratio: number }; -interface LayoutUnit extends UnitBase { - specifier: WidthSpecifier; -} - -interface Fixed extends UnitBase { - specifier: { pixels: number }; +interface Resolved { + config: Doc; + target: Doc; + pixels: number; } -interface Ratio extends UnitBase { - specifier: { ratio: number }; -} +const resolvedUnits = ["*", "px"]; @observer export default class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) { private _dropDisposer?: DragManager.DragDropDisposer; + + /** + * Returns the list of so-called configuration documents. + * Each one is a wrapper around what we typically think of as + * the child document, just also encoding the magnitude and unit + * of the specified width. + */ private get configuration() { const { Document } = this.props; if (!Document.multicolumnData) { @@ -45,126 +49,132 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico 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 values: LayoutUnit[] = []; - let ratioSum = 0; - let fixedCount = 0; - let ratioCount = 0; + private get resolvedLayoutInformation() { + const unresolved: Unresolved[] = []; + let ratioSum = 0, numFixed = 0, numRatio = 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 { target, widthMagnitude, widthUnit } = config; + if (target instanceof Doc) { + const unit = StrCast(widthUnit); + const magnitude = NumCast(widthMagnitude); + if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { + switch (unit) { + case "*": + ratioSum += magnitude; + numRatio++; + break; + case "px": + numFixed++; + break; + } + unresolved.push({ config, target, magnitude, unit }); + } + // otherwise, the particular configuration entry is ignored and the remaining + // space is allocated as if the document were absent from the configuration list } - const widthSpecifier = Cast(columnWidth, "number"); - let matches: RegExpExecArray | null; - let specifier: WidthSpecifier | null = null; - if (widthSpecifier !== undefined) { - // we've gotten a number, referring to a pixel value - specifier = { pixels: widthSpecifier }; - fixedCount++; - } 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; - specifier = { ratio }; - ratioCount++; - } - if (specifier !== null) { - values.push({ config, target, specifier }); - } - // otherwise, the particular configuration entry is ignored and the remaining - // space is allocated as if the document were absent from the configuration list } - return { values, ratioCount, fixedCount, ratioSum }; + return { unresolved, numRatio, numFixed, ratioSum }; } - @computed private get totalFixedPool() { - const fixed: Fixed[] = []; - const layout = this.layoutInformation; + /** + * 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 { + const layout = this.resolvedLayoutInformation; if (!layout) { return undefined; } - layout.values.forEach(unit => { - ("pixels" in unit.specifier) && fixed.push(unit as Fixed); - }); - return fixed.reduce((sum, unit) => sum + unit.specifier.pixels, 0); + let sum = 0; + for (const { magnitude, unit } of layout.unresolved) { + if (unit === "px") { + sum += magnitude; + } + } + return sum; } - @computed private get totalProportionalPool() { - const { totalFixedPool } = this; - return totalFixedPool !== undefined ? this.props.PanelWidth() - totalFixedPool : undefined; + /** + * This 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 { totalFixedAllocation } = this; + return totalFixedAllocation !== undefined ? this.props.PanelWidth() - totalFixedAllocation : undefined; } - @computed private get columnUnitLength() { - const layout = this.layoutInformation; - const { totalProportionalPool } = this; - if (layout !== null && totalProportionalPool !== undefined) { - const { ratioSum, values } = layout; - return (totalProportionalPool - 2 * (values.length - 1)) / ratioSum; + /** + * This returns the total quantity, in pixels, that + * 1* (relative / star unit) is worth. For example, + * if the configuration had three documents, with, respectively, + * widths of 2*, 2* and 1*, and the panel width was 1000px, + * this value would return 1000 / (2 + 2 + 1), or 200px. + * This is then multiplied by each relative-width + * document's * factor 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 columnUnitLength(): number | undefined { + const layout = this.resolvedLayoutInformation; + const { totalRatioAllocation } = this; + if (layout !== null && totalRatioAllocation !== undefined) { + const { ratioSum, unresolved } = layout; + return (totalRatioAllocation - 2 * (unresolved.length - 1)) / ratioSum; } return undefined; } @computed private get contents(): JSX.Element[] | null { - const layout = this.layoutInformation; - if (layout === null) { - return (null); - } - const { values } = layout; - const { columnUnitLength } = this; - if (columnUnitLength === undefined) { - return (null); + const layout = this.resolvedLayoutInformation; + const columnUnitLength = this.columnUnitLength; + if (layout === null || columnUnitLength === undefined) { + return (null); // we're still waiting on promises to resolve } - const { GenerateGuid } = Utils; - const toView = ({ target, specifier: { pixels } }: Fixed) => - <ContentFittingDocumentView + const resolved: Resolved[] = []; + layout.unresolved.forEach(value => { + const { unit, magnitude, ...remaining } = value; + let width = magnitude; + if (unit === "*") { + width = magnitude * columnUnitLength; + } + resolved.push({ pixels: width, ...remaining, }); + }); + const collector: JSX.Element[] = []; + for (let i = 0; i < resolved.length; i++) { + const { target, pixels, config } = resolved[i]; + collector.push(<ContentFittingDocumentView {...this.props} - key={GenerateGuid()} + key={Utils.GenerateGuid()} Document={target} DataDocument={undefined} PanelWidth={() => pixels} getTransform={this.props.ScreenToLocalTransform} - />; - const resolved: Fixed[] = []; - values.forEach(value => { - const { specifier, ...remaining } = value; - if ("ratio" in specifier) { - resolved.push({ ...remaining, specifier: { pixels: specifier.ratio * columnUnitLength } }); - } else { - resolved.push(value as Fixed); - } - }); - const collector: JSX.Element[] = []; - for (let i = 0; i < resolved.length; i++) { - collector.push(toView(resolved[i])); + />); collector.push( <MulticolumnSpacer - key={GenerateGuid()} - columnBaseUnit={columnUnitLength} - toLeft={resolved[i].config} + key={Utils.GenerateGuid()} + columnUnitLength={columnUnitLength} + toLeft={config} toRight={resolved[i + 1]?.config} /> ); } - collector.pop(); + collector.pop(); // not the cleanest, but simply removes the final extraneous spacer return collector; } @@ -179,7 +189,7 @@ export default class CollectionMulticolumnView extends CollectionSubView(Multico } interface SpacerProps { - columnBaseUnit: number; + columnUnitLength: number; toLeft?: Doc; toRight?: Doc; } @@ -196,12 +206,13 @@ class MulticolumnSpacer extends React.Component<SpacerProps> { } private onPointerMove = ({ movementX }: PointerEvent) => { - const { toLeft, toRight, columnBaseUnit } = this.props; + const { toLeft, toRight, columnUnitLength } = 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}*`; + const { widthUnit, widthMagnitude } = target; + if (widthUnit === "*") { + target.widthMagnitude = NumCast(widthMagnitude) - Math.abs(movementX) / columnUnitLength; + } } } |