From 07e53a4a244eff804e47c6b48b8eb4f2a5096b0e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 31 Jan 2020 15:46:42 -0500
Subject: added collectionTime view

---
 src/client/views/collections/CollectionViewChromes.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 073a30330..9cb668d20 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -71,6 +71,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
             case CollectionViewType.Masonry: return this._stacking_commands;
             case CollectionViewType.Freeform: return this._freeform_commands;
             case CollectionViewType.Pivot: return this._freeform_commands;
+            case CollectionViewType.Time: return this._freeform_commands;
             case CollectionViewType.Carousel: return this._freeform_commands;
         }
         return [];
@@ -418,8 +419,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <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">Carousel</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Linear</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>
                         </select>
                         <div className="collectionViewBaseChrome-viewSpecs" title="filter documents to show" style={{ display: collapsed ? "none" : "grid" }}>
                             <div className="collectionViewBaseChrome-filterIcon" onPointerDown={this.openViewSpecs} >
-- 
cgit v1.2.3-70-g09d2


From d20a21384fc685082955cfbb9deb9c0f1176e7ad Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 31 Jan 2020 18:30:42 -0500
Subject: added multirow collection.  cleaned up CollectionTimeview a bit.

---
 .../views/collections/CollectionTimeView.tsx       |   2 +-
 src/client/views/collections/CollectionView.tsx    |   5 +
 .../views/collections/CollectionViewChromes.tsx    |  11 +-
 .../CollectionFreeFormLayoutEngines.tsx            |   9 +-
 .../CollectionMultirowView.scss                    |  34 +++
 .../CollectionMultirowView.tsx                     | 259 +++++++++++++++++++++
 .../collectionMulticolumn/MultirowHeightLabel.tsx  |  56 +++++
 .../collectionMulticolumn/MultirowResizer.tsx      | 116 +++++++++
 8 files changed, 484 insertions(+), 8 deletions(-)
 create mode 100644 src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
 create mode 100644 src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
 create mode 100644 src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
 create mode 100644 src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

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
-- 
cgit v1.2.3-70-g09d2


From feaed0e1fbfb505684c87996b020ee36b7e96224 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 1 Feb 2020 21:49:23 -0500
Subject: moved all of pivot view into TimeView. tweaked layout for pivot.
 better fix is straightforward.

---
 .../views/collections/CollectionPivotView.scss     |  88 -----------
 .../views/collections/CollectionPivotView.tsx      | 174 ---------------------
 .../views/collections/CollectionTimeView.scss      |   9 +-
 .../views/collections/CollectionTimeView.tsx       |  38 ++++-
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 src/client/views/collections/CollectionView.tsx    |   7 +-
 .../views/collections/CollectionViewChromes.tsx    |  34 +---
 .../CollectionFreeFormLayoutEngines.tsx            |  77 ++++-----
 .../views/presentationview/PresElementBox.scss     |   2 +-
 9 files changed, 80 insertions(+), 351 deletions(-)
 delete mode 100644 src/client/views/collections/CollectionPivotView.scss
 delete mode 100644 src/client/views/collections/CollectionPivotView.tsx

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

diff --git a/src/client/views/collections/CollectionPivotView.scss b/src/client/views/collections/CollectionPivotView.scss
deleted file mode 100644
index 505091e98..000000000
--- a/src/client/views/collections/CollectionPivotView.scss
+++ /dev/null
@@ -1,88 +0,0 @@
-.collectionPivotView {
-    display: flex;
-    flex-direction: row;
-    position: absolute;
-    height: 100%;
-    width: 100%;
-
-    .collectionPivotView-flyout {
-        width: 400px;
-        height: 300px;
-        display: inline-block;
-
-        .collectionPivotView-flyout-item {
-            background-color: lightgray;
-            text-align: left;
-            display: inline-block;
-            position: relative;
-            width: 100%;
-        }
-    }
-
-    .pivotKeyEntry {
-        position: absolute;
-        top: 5px;
-        right: 5px;
-        z-index: 10;
-        pointer-events: all;
-        padding: 5px;
-        border: 1px solid black;
-    }
-
-    .collectionPivotView-treeView {
-        display: flex;
-        flex-direction: column;
-        width: 200px;
-        height: 100%;
-
-        .collectionPivotView-addfacet {
-            display: inline-block;
-            width: 200px;
-            height: 30px;
-            background: darkGray;
-            text-align: center;
-
-            .collectionPivotView-button {
-                align-items: center;
-                display: flex;
-                width: 100%;
-                height: 100%;
-
-                .collectionPivotView-span {
-                    margin: auto;
-                }
-            }
-
-            >div,
-            >div>div {
-                width: 100%;
-                height: 100%;
-                text-align: center;
-            }
-        }
-
-        .collectionPivotView-tree {
-            display: inline-block;
-            width: 100%;
-            height: calc(100% - 30px);
-        }
-    }
-
-    .collectionPivotView-pivot {
-        display: inline-block;
-        width: calc(100% - 200px);
-        height: 100%;
-    }
-
-    .collectionPivotView-dragger {
-        background-color: lightgray;
-        height: 40px;
-        width: 20px;
-        position: absolute;
-        border-radius: 10px;
-        top: 55%;
-        border: 1px black solid;
-        z-index: 2;
-        left: -10px;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
deleted file mode 100644
index 992018a3f..000000000
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import { faEdit } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Set } from "typescript-collections";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { Docs } from "../../documents/Documents";
-import { EditableView } from "../EditableView";
-import { anchorPoints, Flyout } from "../TemplateMenu";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import "./CollectionPivotView.scss";
-import { CollectionSubView } from "./CollectionSubView";
-import { CollectionTreeView } from "./CollectionTreeView";
-import React = require("react");
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { RichTextField } from "../../../new_fields/RichTextField";
-
-@observer
-export class CollectionPivotView extends CollectionSubView(doc => doc) {
-    componentDidMount() {
-        this.props.Document._freeformLayoutEngine = "pivot";
-        const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked;
-        if (!this.props.Document._facetCollection) {
-            const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true });
-            facetCollection.target = this.props.Document;
-            this.props.Document.excludeFields = new List<string>(["_facetCollection", "_docFilter"]);
-
-            const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)";
-            const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailed'); useRightSplit(alias); ";
-            facetCollection.onCheckedClick = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name });
-            this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "boolean", containingCollection: Doc.name });
-            this.props.Document._facetCollection = facetCollection;
-            this.props.Document._fitToBox = true;
-        }
-    }
-    bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth;
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-this._facetWidth, 0);
-
-    @computed get _allFacets() {
-        const facets = new Set<string>();
-        this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
-        return facets.toArray();
-    }
-
-    /**
-     * Responds to clicking the check box in the flyout menu
-     */
-    facetClick = (facetHeader: string) => {
-        const facetCollection = this.props.Document._facetCollection;
-        if (facetCollection instanceof Doc) {
-            const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader);
-            if (found !== -1) {
-                (facetCollection.data as List<Doc>).splice(found, 1);
-                const docFilter = Cast(this.props.Document._docFilter, listSpec("string"));
-                if (docFilter) {
-                    let index: number;
-                    while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
-                        docFilter.splice(index, 3);
-                    }
-                }
-            } else {
-                const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true });
-                const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc };
-                const params = { layoutDoc: Doc.name, dataDoc: Doc.name, };
-                newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables);
-                Doc.AddDocToList(facetCollection, "data", newFacet);
-            }
-        }
-    }
-    _canClick = false;
-    _facetWidthOnDown = 0;
-    @observable _facetWidth = 200;
-    onPointerDown = (e: React.PointerEvent) => {
-        this._canClick = true;
-        this._facetWidthOnDown = e.screenX;
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        document.addEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointerup", this.onPointerUp);
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-
-    @action
-    onPointerMove = (e: PointerEvent) => {
-        this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0);
-        Math.abs(e.movementX) > 6 && (this._canClick = false);
-    }
-    @action
-    onPointerUp = (e: PointerEvent) => {
-        if (Math.abs(e.screenX - this._facetWidthOnDown) < 6 && this._canClick) {
-            this._facetWidth = this._facetWidth < 15 ? 200 : 0;
-        }
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
-
-    menuCallback = (x: number, y: number) => {
-        ContextMenu.Instance.clearItems();
-        const docItems: ContextMenuProps[] = [];
-        const keySet: Set<string> = new Set();
-
-        this.childLayoutPairs.map(pair =>
-            Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof (pair.layout[fieldKey]) === "string").map(fieldKey =>
-                keySet.add(fieldKey)));
-        keySet.toArray().map(fieldKey =>
-            docItems.push({ description: ":" + fieldKey, event: () => this.props.Document.pivotField = fieldKey, icon: "compress-arrows-alt" }));
-        docItems.push({ description: ":(null)", event: () => this.props.Document.pivotField = undefined, icon: "compress-arrows-alt" })
-        ContextMenu.Instance.addItem({ description: "Pivot Fields ...", subitems: docItems, icon: "eye" });
-        const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
-        ContextMenu.Instance.displayMenu(x, y, ":");
-    }
-
-    @observable private collapsed: boolean = false;
-    private toggleVisibility = action(() => this.collapsed = !this.collapsed);
-
-    render() {
-        const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null);
-        const flyout = (
-            <div className="collectionPivotView-flyout" style={{ width: `${this._facetWidth}` }}>
-                {this._allFacets.map(facet => <label className="collectionPivotView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
-                    <input type="checkbox" onChange={e => { }} checked={DocListCast((this.props.Document._facetCollection as Doc)?.data).some(d => d.title === facet)} />
-                    <span className="checkmark" />
-                    {facet}
-                </label>)}
-            </div>
-        );
-        const newEditableViewProps = {
-            GetValue: () => "",
-            SetValue: (value: any) => {
-                if (value?.length) {
-                    this.props.Document.pivotField = value;
-                    return true;
-                }
-                return false;
-            },
-            showMenuOnLoad: true,
-            contents: ":" + StrCast(this.props.Document.pivotField),
-            toggle: this.toggleVisibility,
-            color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
-        };
-        return !facetCollection ? (null) :
-            <div className="collectionPivotView" style={{ height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
-                <div className={"pivotKeyEntry"}>
-                    <EditableView {...newEditableViewProps} menuCallback={this.menuCallback} />
-                </div>
-                <div className="collectionPivotView-dragger" key="dragger" onPointerDown={this.onPointerDown} style={{ transform: `translate(${this._facetWidth}px, 0px)` }} >
-                    <span title="library View Dragger" style={{ width: "5px", position: "absolute", top: "0" }} />
-                </div>
-                <div className="collectionPivotView-treeView" style={{ width: `${this._facetWidth}px`, overflow: this._facetWidth < 15 ? "hidden" : undefined }}>
-                    <div className="collectionPivotView-addFacet" style={{ width: `${this._facetWidth}px` }} onPointerDown={e => e.stopPropagation()}>
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
-                            <div className="collectionPivotView-button">
-                                <span className="collectionPivotView-span">Facet Filters</span>
-                                <FontAwesomeIcon icon={faEdit} size={"lg"} />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="collectionPivotView-tree" key="tree">
-                        <CollectionTreeView {...this.props} Document={facetCollection} />
-                    </div>
-                </div>
-                <div className="collectionPivotView-pivot" key="pivot" style={{ width: this.bodyPanelWidth() }}>
-                    <CollectionFreeFormView  {...this.props} ScreenToLocalTransform={this.getTransform} PanelWidth={this.bodyPanelWidth} />
-                </div>
-            </div>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index 805d06d3d..3aac2bc75 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -1,4 +1,4 @@
-.collectionTimeView {
+.collectionTimeView, .collectionTimeView-pivot {
     display: flex;
     flex-direction: row;
     position: absolute;
@@ -93,7 +93,7 @@
         }
     }
 
-    .collectionTimeView-pivot {
+    .collectionTimeView-innards {
         display: inline-block;
         width: calc(100% - 200px);
         height: 100%;
@@ -110,4 +110,9 @@
         z-index: 2;
         left: -10px;
     }
+}
+.collectionTimeView-pivot {
+    .collectionFreeform-customText {
+        text-align: center;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index b4ab88f9a..253dfa890 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -107,8 +107,10 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         const keySet: Set<string> = new Set();
 
         this.childLayoutPairs.map(pair =>
-            Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(fieldKey => pair.layout[fieldKey] instanceof RichTextField || typeof (pair.layout[fieldKey]) === "number" || typeof (pair.layout[fieldKey]) === "string").map(fieldKey =>
-                keySet.add(fieldKey)));
+            Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(fieldKey =>
+                pair.layout[fieldKey] instanceof RichTextField ||
+                typeof (pair.layout[fieldKey]) === "number" ||
+                typeof (pair.layout[fieldKey]) === "string").map(fieldKey => keySet.add(fieldKey)));
         keySet.toArray().map(fieldKey =>
             docItems.push({ description: ":" + fieldKey, event: () => this.props.Document.pivotField = fieldKey, icon: "compress-arrows-alt" }));
         docItems.push({ description: ":(null)", event: () => this.props.Document.pivotField = undefined, icon: "compress-arrows-alt" })
@@ -189,11 +191,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     }
 
     @computed get contents() {
-        return <div className="collectionTimeView-pivot" key="pivot" style={{ width: this.bodyPanelWidth() }}>
+        return <div className="collectionTimeView-innards" key="timeline" style={{ width: this.bodyPanelWidth() }}>
             <CollectionFreeFormView  {...this.props} ScreenToLocalTransform={this.getTransform} PanelWidth={this.bodyPanelWidth} />
         </div>;
     }
 
+    _changing = false;
     render() {
         const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null);
         const flyout = (
@@ -219,8 +222,33 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
             toggle: this.toggleVisibility,
             color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
         };
+
+        let nonNumbers = 0;
+        this.childDocs.map(doc => {
+            const num = NumCast(doc[StrCast(this.props.Document.pivotField)], Number(StrCast(doc[StrCast(this.props.Document.pivotField)])));
+            if (Number.isNaN(num)) {
+                nonNumbers++;
+            }
+        });
+        const doTimeline = nonNumbers / this.childDocs.length < 0.1;
+        if (doTimeline !== (this.props.Document._freeformLayoutEngine === "timeline")) {
+            if (!this._changing) {
+                this._changing = true;
+                setTimeout(() => {
+                    if (nonNumbers / this.childDocs.length > 0.1) {
+                        this.childDocs.map(child => child.isMinimized = false);
+                        this.props.Document._freeformLayoutEngine = "pivot";
+                    } else {
+                        this.props.Document._freeformLayoutEngine = "timeline";
+                    }
+                    this._changing = false;
+                }, 0);
+            }
+            return (null);
+        }
+
         return !facetCollection ? (null) :
-            <div className="collectionTimeView" style={{ height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
+            <div className={"collectionTimeView" + (doTimeline ? "" : "-pivot")} style={{ height: `calc(100%  - ${this.props.Document._chromeStatus === "enabled" ? 51 : 0}px)` }}>
                 <div className={"pivotKeyEntry"}>
                     <EditableView {...newEditableViewProps} menuCallback={this.menuCallback} />
                 </div>
@@ -243,7 +271,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
                     </div>
                 </div>
                 {this.contents}
-                {!this.props.isSelected() ? (null) : <>
+                {!this.props.isSelected() || !doTimeline ? (null) : <>
                     <div className="collectionTimeView-thumb-min collectionTimeView-thumb" key="min" onPointerDown={this.onMinDown} />
                     <div className="collectionTimeView-thumb-max collectionTimeView-thumb" key="mid" onPointerDown={this.onMaxDown} />
                     <div className="collectionTimeView-thumb-mid collectionTimeView-thumb" key="max" onPointerDown={this.onMidDown} />
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3cb24f079..631870866 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -659,7 +659,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
 
                 Document.childLayout = cardLayout;
                 Document.childDetailed = detailedLayout;
-                Document._viewType = CollectionViewType.Pivot;
+                Document._viewType = CollectionViewType.Time;
                 Document.pivotField = "company";
             }
         });
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 5a7bdef61..d5d62159b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -29,7 +29,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionCarouselView } from './CollectionCarouselView';
 import { CollectionLinearView } from './CollectionLinearView';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
-import { CollectionPivotView } from './CollectionPivotView';
 import { CollectionSchemaView } from "./CollectionSchemaView";
 import { CollectionStackingView } from './CollectionStackingView';
 import { CollectionStaffView } from './CollectionStaffView';
@@ -52,7 +51,6 @@ export enum CollectionViewType {
     Masonry,
     Multicolumn,
     Multirow,
-    Pivot,
     Time,
     Carousel,
     Linear,
@@ -71,7 +69,6 @@ export namespace CollectionViewType {
         ["masonry", CollectionViewType.Masonry],
         ["multicolumn", CollectionViewType.Multicolumn],
         ["multirow", CollectionViewType.Multirow],
-        ["pivot", CollectionViewType.Pivot],
         ["time", CollectionViewType.Time],
         ["carousel", CollectionViewType.Carousel],
         ["linear", CollectionViewType.Linear],
@@ -191,7 +188,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
             case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); }
             case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (<CollectionStackingView key="collview" {...props} />); }
             case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
-            case CollectionViewType.Pivot: { return (<CollectionPivotView key="collview" {...props} />); }
             case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
             case CollectionViewType.Freeform:
             default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
@@ -234,8 +230,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
             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" });
-            subItems.push({ description: "Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
+            subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
             switch (this.props.Document._viewType) {
                 case CollectionViewType.Freeform: {
                     subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index c5db6f10f..f00af52cf 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -70,7 +70,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
             case CollectionViewType.Stacking: return this._stacking_commands;
             case CollectionViewType.Masonry: return this._stacking_commands;
             case CollectionViewType.Freeform: return this._freeform_commands;
-            case CollectionViewType.Pivot: return this._freeform_commands;
             case CollectionViewType.Time: return this._freeform_commands;
             case CollectionViewType.Carousel: return this._freeform_commands;
         }
@@ -271,32 +270,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         return this.props.CollectionView.props.Document;
     }
 
-    private get pivotKey() {
-        return StrCast(this.document.pivotField);
-    }
-
-    private set pivotKey(value: string) {
-        this.document.pivotField = value;
-    }
-
-    @observable private pivotKeyDisplay = this.pivotKey;
-    getPivotInput = () => {
-        if (StrCast(this.document._freeformLayoutEngine) !== "pivot") {
-            return (null);
-        }
-        return (<input className="collectionViewBaseChrome-viewSpecsInput"
-            placeholder="PIVOT ON..."
-            value={this.pivotKeyDisplay}
-            onChange={action((e: React.ChangeEvent<HTMLInputElement>) => this.pivotKeyDisplay = e.currentTarget.value)}
-            onKeyPress={action((e: React.KeyboardEvent<HTMLInputElement>) => {
-                const value = e.currentTarget.value;
-                if (e.which === 13) {
-                    this.pivotKey = value;
-                    this.pivotKeyDisplay = "";
-                }
-            })} />);
-    }
-
     @action.bound
     clearFilter = () => {
         this.props.CollectionView.props.Document.viewSpecScript = ScriptField.MakeFunction("true", { doc: Doc.name });
@@ -419,10 +392,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry</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>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivo/Time</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Carousel</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="11">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 d2a2b42a0..6f84346df 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -46,10 +46,14 @@ export function computePivotLayout(
     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 pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200);
+    const fieldKey = "data";
+    const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 1000);
     const pivotColumnGroups = new Map<FieldResult<Field>, Doc[]>();
+    const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
 
     const pivotFieldKey = toLabel(pivotDoc.pivotField);
     for (const doc of childDocs) {
@@ -69,16 +73,20 @@ export function computePivotLayout(
     const expander = 1.05;
     const gap = .15;
     let x = 0;
+    let max_text = 60;
     pivotColumnGroups.forEach((val, key) => {
         let y = 0;
         let xCount = 0;
+        const text = toLabel(key);
+        max_text = Math.max(max_text, Math.min(500, text.length));
         groupNames.push({
             type: "text",
-            text: toLabel(key),
+            text,
             x,
             y: pivotAxisWidth,
             width: pivotAxisWidth * expander * numCols,
-            fontSize: NumCast(pivotDoc.pivotFontSize, 20)
+            height: max_text,
+            fontSize
         });
         for (const doc of val) {
             const layoutDoc = Doc.Layout(doc);
@@ -103,37 +111,7 @@ export function computePivotLayout(
         x += pivotAxisWidth * (numCols * expander + gap);
     });
 
-    const grpEles = groupNames.map(gn => { return { x: gn.x, y: gn.y, width: gn.width, height: undefined } as PivotData; });
-    const docEles = childPairs.map(pair =>
-        docMap.get(pair.layout) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), width: NumCast(pair.layout._width), height: NumCast(pair.layout._height) } // new pos is computed pos, or pos written to the document's fields
-    );
-    const minLabelHeight = 56;
-    const aggBounds = aggregateBounds(docEles.concat(grpEles), 0, 0);
-    const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
-    const scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] - (2 * minLabelHeight) ? (panelDim[1] - (2 * minLabelHeight)) / (aggBounds.b - aggBounds.y) : wscale;
-    const centerY = ((panelDim[1] - 2 * minLabelHeight) - (aggBounds.b - aggBounds.y) * scale) / 2;
-    const centerX = (panelDim[0] - (aggBounds.r - aggBounds.x) * scale) / 2;
-
-    childPairs.map(pair => {
-        const fallbackPos = {
-            x: NumCast(pair.layout.x),
-            y: NumCast(pair.layout.y),
-            z: NumCast(pair.layout.z),
-            width: NumCast(pair.layout._width),
-            height: NumCast(pair.layout._height)
-        };
-        const newPosRaw = docMap.get(pair.layout) || fallbackPos; // new pos is computed pos, or pos written to the document's fields
-        const newPos = { x: newPosRaw.x * scale + centerX, y: (newPosRaw.y - aggBounds.y) * scale + centerY, z: newPosRaw.z, width: (newPosRaw.width || 0) * scale, height: newPosRaw.height! * scale };
-        const lastPos = poolData.get(pair.layout[Id]); // last computed pos
-        if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.width !== lastPos.width || newPos.height !== lastPos.height) {
-            runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
-        }
-    });
-    return {
-        elements: viewDefsToJSX([{ type: "text", text: "", x: 0, y: -aggBounds.y * scale - minLabelHeight, width: panelDim[0], height: panelDim[1], fontSize: 1 }].concat(groupNames.map(gname => {
-            return { type: gname.type, text: gname.text, x: gname.x * scale + centerX, y: (gname.y - aggBounds.y) * scale + centerY, width: (gname.width || 0) * scale, height: Math.max(minLabelHeight, centerY), fontSize: gname.fontSize };
-        })))
-    };
+    return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
 
 
@@ -186,7 +164,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize });
     }
 
-    const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, panelDim[1] / 2.5);
+    const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
     let stacking: number[] = [];
     sortedKeys.forEach(key => {
         const keyDocs = pivotDateGroups.get(key)!;
@@ -212,12 +190,19 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize });
     }
 
-    const grpEles = groupNames.map(gn => { return { x: gn.x, y: gn.y, height: gn.height } as PivotData; });
+    const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any;
+    return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
+}
+
+function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: any,
+    poolData: any, viewDefsToJSX: any, groupNames: PivotData[], minWidth: number, extras: any[]) {
+
+    const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, height: gn.height }) as PivotData);
     const docEles = childPairs.filter(d => !d.layout.isMinimized).map(pair =>
         docMap.get(pair.layout) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), width: pair.layout[WidthSym](), height: pair.layout[HeightSym]() } as PivotData // new pos is computed pos, or pos written to the document's fields
     );
     const aggBounds = aggregateBounds(docEles.concat(grpEles), 0, 0);
-    aggBounds.r = Math.max((maxTime - minTime) * scaling, aggBounds.r - aggBounds.x);
+    aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
     if (Number.isNaN(scale)) scale = 1;
@@ -237,12 +222,18 @@ export function computeTimelineLayout(
             runInAction(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
         }
     });
+
     return {
-        elements: viewDefsToJSX([
-            { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any
-        ].concat(groupNames.map(gname => {
-            return { type: gname.type, text: gname.text, x: gname.x * scale, y: gname.y * scale, width: gname.width === undefined ? gname.width : gname.width * scale, height: Math.max(fontHeight, gname.height! * scale), fontSize: gname.fontSize };
-        })))
+        elements: viewDefsToJSX(extras.concat(groupNames.map(gname => ({
+            type: gname.type,
+            text: gname.text,
+            x: gname.x * scale,
+            y: gname.y * scale,
+            width: gname.width === undefined ? undefined : gname.width * scale,
+            height: Math.max(fontHeight, gname.height! * scale),
+            // height: gname.height === undefined ? undefined : gname.height * scale,
+            fontSize: gname.fontSize
+        }))))
     };
 }
 
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss
index fd9de90e0..8370af490 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/presentationview/PresElementBox.scss
@@ -71,7 +71,7 @@
 }
 
 .presElementBox-name {
-    font-size: 12px;
+    font-size: 12pxππ;
     position: absolute;
     display: inline-block;
     width: calc(100% - 45px);
-- 
cgit v1.2.3-70-g09d2


From d3abbcfc975247839b587b37cabd4f07db73f28c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 8 Feb 2020 02:19:56 -0500
Subject: cleaned up links on images.  fixed axis sorting on pivot views. 
 fixed dragging link button

---
 src/client/views/DocumentButtonBar.tsx             |  2 +-
 .../views/collections/CollectionSchemaView.scss    |  6 +++---
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionViewChromes.tsx    |  5 ++---
 .../CollectionFreeFormLayoutEngines.tsx            | 16 +++++++++++----
 .../collections/collectionFreeForm/MarqueeView.tsx | 23 ++--------------------
 .../views/nodes/ContentFittingDocumentView.scss    |  2 +-
 8 files changed, 23 insertions(+), 35 deletions(-)

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 963d97d59..d6029dbe5 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -237,7 +237,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
     @computed
     get metadataButton() {
         const view0 = this.view0;
-        return !view0 ? (null) : <div title="Show metadata panel" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+        return !view0 ? (null) : <div title="Show metadata panel" className="documentButtonBar-linkFlyout">
             <Flyout anchorPoint={anchorPoints.LEFT_TOP}
                 content={<MetadataEntryMenu docs={() => this.props.views.filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
                 <div className={"documentButtonBar-linkButton-" + "empty"} >
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 8b3d332af..a24140b48 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -39,9 +39,9 @@
         cursor: col-resize;
     }
 
-    .documentView-node:first-child {
-        background: $light-color;
-    }
+    // .documentView-node:first-child {
+    //     background: $light-color;
+    // }
 }
 
 .ReactTable {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 171aecc70..d772ace23 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -39,7 +39,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
     @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); }
     @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); }
     @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); }
-    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * this.gridGap); }
+    @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
     @computed get yMargin() { return Math.max(this.props.Document.showTitle && !this.props.Document.showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
     @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
     @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e0e99d635..62b9e8380 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -320,7 +320,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                                     nativeWidth && (proto["data-nativeWidth"] = nativeWidth);
                                     nativeHeight && (proto["data-nativeHeight"] = nativeHeight);
                                     contentSize && (proto.contentSize = contentSize);
-                                    this.props.addDocument(doc);
+                                    this.props?.addDocument(doc);
                                 }
                             });
                         }));
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index f00af52cf..a21e78188 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -386,15 +386,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             onChange={this.viewChanged}
                             value={NumCast(this.props.CollectionView.props.Document._viewType)}>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">schema</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema</option>
                             <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">MultiCol</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="8">MultiRow</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivo/Time</option>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="9">Pivot/Time</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="10">Carousel</option>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="11">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 95f7794bb..baf09fe5b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -42,13 +42,13 @@ export interface ViewDefResult {
     ele: JSX.Element;
     bounds?: ViewDefBounds;
 }
-
 function toLabel(target: FieldResult<Field>) {
     if (target instanceof ObjectField || target instanceof RefField) {
         return target[ToString]();
     }
     return String(target);
-}/**
+}
+/**
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
  * 
  * @param {String} text The text to be rendered.
@@ -91,9 +91,17 @@ export function computePivotLayout(
             pivotColumnGroups.get(val)!.docs.push(doc);
         }
     }
+    let nonNumbers = 0;
+    childDocs.map(doc => {
+        const num = toNumber(doc[pivotFieldKey]);
+        if (num === undefined || Number.isNaN(num)) {
+            nonNumbers++;
+        }
+    });
+    const pivotNumbers = nonNumbers / childDocs.length < .1;
     if (pivotColumnGroups.size > 10) {
         const arrayofKeys = Array.from(pivotColumnGroups.keys());
-        const sortedKeys = arrayofKeys.sort();
+        const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
         const clusterSize = Math.ceil(pivotColumnGroups.size / 10);
         const numClusters = Math.ceil(sortedKeys.length / clusterSize);
         for (let i = 0; i < numClusters; i++) {
@@ -135,7 +143,7 @@ export function computePivotLayout(
     const expander = 1.05;
     const gap = .15;
     let x = 0;
-    const sortedPivotKeys = Array.from(pivotColumnGroups.keys()).sort();
+    const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
     sortedPivotKeys.forEach(key => {
         const val = pivotColumnGroups.get(key)!;
         let y = 0;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1b54a92eb..e16f4011e 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -301,25 +301,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     getCollection = (selected: Doc[], asTemplate: boolean) => {
         const bounds = this.Bounds;
-        const defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
-            "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
-        const colorPalette = Cast(this.props.Document.colorPalette, listSpec("string"));
-        if (!colorPalette) this.props.Document.colorPalette = new List<string>(defaultPalette);
-        const palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]);
-        const usedPaletted = new Map<string, number>();
-        [...this.props.activeDocuments(), this.props.Document].map(child => {
-            const bg = StrCast(Doc.Layout(child).backgroundColor);
-            if (palette.indexOf(bg) !== -1) {
-                palette.splice(palette.indexOf(bg), 1);
-                if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
-                else usedPaletted.set(bg, 1);
-            }
-        });
-        usedPaletted.delete("#f1efeb");
-        usedPaletted.delete("white");
-        usedPaletted.delete("rgba(255,255,255,1)");
-        const usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
-        const chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
         // const inkData = this.ink ? this.ink.inkData : undefined;
         const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
         const newCollection = creator(selected, {
@@ -327,8 +308,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             y: bounds.top,
             _panX: 0,
             _panY: 0,
-            backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
-            defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+            backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
+            defaultBackgroundColor: this.props.isAnnotationOverlay ? "#00000015" : "white",
             _width: bounds.width,
             _height: bounds.height,
             _LODdisable: true,
diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss
index 2801af441..eb2d93b9a 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.scss
+++ b/src/client/views/nodes/ContentFittingDocumentView.scss
@@ -19,6 +19,6 @@
 
     .documentView-node:first-child {
         position: relative;
-        background: $light-color;
+        background: "#B59B66"; //$light-color;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From ba9351bc009b6f78ca9815b481bc5643f8af96e0 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 16 Feb 2020 22:59:36 -0500
Subject: fixed warnings

---
 src/Utils.ts                                       |  4 +-
 src/client/util/RichTextRules.ts                   |  2 +-
 src/client/views/DocumentButtonBar.tsx             |  1 -
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionSchemaHeaders.tsx  |  4 +-
 .../views/collections/CollectionViewChromes.tsx    |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            | 38 ++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../caption_toggle/DetailedCaptionToggle.tsx       | 72 ----------------------
 .../document_templates/image_card/ImageCard.tsx    | 15 -----
 .../views/nodes/ContentFittingDocumentView.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  7 +--
 src/new_fields/util.ts                             |  2 +-
 17 files changed, 38 insertions(+), 125 deletions(-)
 delete mode 100644 src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
 delete mode 100644 src/client/views/document_templates/image_card/ImageCard.tsx

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 6a0b3fad8..9162771aa 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -495,7 +495,7 @@ export function setupMoveUpEvents(
         (target as any)._lastX = e.clientX;
         (target as any)._lastY = e.clientY;
         e.stopPropagation();
-    }
+    };
     const _upEvent = (e: PointerEvent): void => {
         upEvent(e);
         if (Math.abs(e.clientX - (target as any)._downX) < 4 || Math.abs(e.clientY - (target as any)._downY) < 4) {
@@ -503,7 +503,7 @@ export function setupMoveUpEvents(
         }
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
-    }
+    };
     e.stopPropagation();
     document.removeEventListener("pointermove", _moveEvent);
     document.removeEventListener("pointerup", _upEvent);
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index de0f46202..a4f1ff22c 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -298,5 +298,5 @@ export class RichTextRules {
                     return null;
                 }),
         ]
-    }
+    };
 }
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index ec1f879c2..5d289c5e5 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -318,7 +318,6 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView |
         const isText = this.view0.props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field.  need to add fieldKey to DocumentView
         const considerPull = isText && this.considerGoogleDocsPull;
         const considerPush = isText && this.considerGoogleDocsPush;
-        Doc.UserDoc().pr
         return <div className="documentButtonBar">
             <div className="documentButtonBar-button">
                 {this.linkButton}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cc7388a61..44e8a3a48 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -109,7 +109,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 UndoManager.RunInBatch(() => selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d => {
                     const value = typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle;
                     didAnything = didAnything || d.props.Document[selectionTitleFieldKey] !== value;
-                    Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true)
+                    Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true);
                 }), "title blur");
                 if (!didAnything) UndoManager.Undo();
             }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index bf0333d55..902016365 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -434,7 +434,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 tab.titleElement[0].onchange = (e: any) => {
                     tab.titleElement[0].size = e.currentTarget.value.length + 1;
                     Doc.SetInPlace(doc, "title", e.currentTarget.value, true);
-                }
+                };
                 tab.titleElement[0].size = StrCast(doc.title).length + 1;
                 tab.titleElement[0].value = doc.title;
                 const gearSpan = document.createElement("span");
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 92dc8780e..c585506b3 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -5,11 +5,13 @@ import "./CollectionSchemaView.scss";
 import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
 import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Flyout, anchorPoints } from "../DocumentDecorations";
 import { ColumnType } from "./CollectionSchemaView";
 import { faFile } from "@fortawesome/free-regular-svg-icons";
 import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
 import { undoBatch } from "../../util/UndoManager";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
 
 library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
 
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index a21e78188..0378c818c 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -179,7 +179,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @action closeViewSpecs = () => {
         this._viewSpecsOpen = false;
         document.removeEventListener("pointerdown", this.closeViewSpecs);
-    };
+    }
 
     @action
     openDatePicker = (e: React.PointerEvent) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 671a3e0c4..8132d2f7c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -27,15 +27,15 @@ export interface ViewDefBounds {
 }
 
 export interface PoolData {
-    x?: number,
-    y?: number,
-    z?: number,
-    zIndex?: number,
-    width?: number,
-    height?: number,
-    color?: string,
-    transition?: string,
-    highlight?: boolean,
+    x?: number;
+    y?: number;
+    z?: number;
+    zIndex?: number;
+    width?: number;
+    height?: number;
+    color?: string;
+    transition?: string;
+    highlight?: boolean;
 }
 
 export interface ViewDefResult {
@@ -63,16 +63,16 @@ function toLabel(target: FieldResult<Field>) {
  */
 function getTextWidth(text: string, font: string): number {
     // re-use canvas object for better performance
-    var canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
-    var context = canvas.getContext("2d");
+    const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
+    const context = canvas.getContext("2d");
     context.font = font;
-    var metrics = context.measureText(text);
+    const metrics = context.measureText(text);
     return metrics.width;
 }
 
-interface pivotColumn {
-    docs: Doc[],
-    filters: string[]
+interface PivotColumn {
+    docs: Doc[];
+    filters: string[];
 }
 
 
@@ -86,7 +86,7 @@ export function computePivotLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[]
 ) {
     const fieldKey = "data";
-    const pivotColumnGroups = new Map<FieldResult<Field>, pivotColumn>();
+    const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
 
     const pivotFieldKey = toLabel(pivotDoc._pivotField);
     for (const doc of filterDocs) {
@@ -123,7 +123,7 @@ export function computePivotLayout(
     const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`;
     const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number);
     const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
-    let maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
+    const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
 
     const colWidth = panelDim[0] / pivotColumnGroups.size;
     const colHeight = panelDim[1] - max_text;
@@ -223,7 +223,7 @@ export function computeTimelineLayout(
     const findStack = (time: number, stack: number[]) => {
         const index = stack.findIndex(val => val === undefined || val < x);
         return index === -1 ? stack.length : index;
-    }
+    };
 
     let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
     let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
@@ -266,7 +266,7 @@ export function computeTimelineLayout(
     }
 
     const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
-    let stacking: number[] = [];
+    const stacking: number[] = [];
     let zind = 0;
     sortedKeys.forEach(key => {
         if (curTime !== undefined && curTime > prevKey && curTime <= key) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 427a8d9fe..60410544b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -856,7 +856,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         });
         return rangeFilteredDocs;
     }
-    childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null) as Doc;
+    childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null);
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         runInAction(() =>
diff --git a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx b/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
deleted file mode 100644
index 3aaf4120c..000000000
--- a/src/client/views/document_templates/caption_toggle/DetailedCaptionToggle.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import * as React from 'react';
-import { FontStyleProperty, ColorProperty } from 'csstype';
-import { observer } from 'mobx-react';
-import { observable, action, runInAction } from 'mobx';
-import { FormattedTextBox } from '../../nodes/FormattedTextBox';
-import { FieldViewProps } from '../../nodes/FieldView';
-
-interface DetailedCaptionDataProps {
-    captionFieldKey?: string;
-    detailsFieldKey?: string;
-}
-
-interface DetailedCaptionStylingProps {
-    sharedFontColor?: ColorProperty;
-    captionFontStyle?: FontStyleProperty;
-    detailsFontStyle?: FontStyleProperty;
-    toggleSize?: number;
-}
-
-@observer
-export default class DetailedCaptionToggle extends React.Component<DetailedCaptionDataProps & DetailedCaptionStylingProps & FieldViewProps> {
-    @observable loaded: boolean = false;
-    @observable detailsExpanded: boolean = false;
-
-    @action toggleDetails = (e: React.MouseEvent<HTMLDivElement>) => {
-        e.preventDefault();
-        e.stopPropagation();
-        this.detailsExpanded = !this.detailsExpanded;
-    }
-
-    componentDidMount() {
-        runInAction(() => this.loaded = true);
-    }
-
-    render() {
-        const size = this.props.toggleSize || 20;
-        return (
-            <div style={{
-                transition: "0.5s opacity ease",
-                opacity: this.loaded ? 1 : 0,
-                bottom: 0,
-                fontSize: 14,
-                width: "100%",
-                position: "absolute"
-            }}>
-                {/* caption */}
-                <div style={{ opacity: this.detailsExpanded ? 0 : 1, transition: "opacity 0.3s ease" }}>
-                    <FormattedTextBox {...this.props} fieldKey={this.props.captionFieldKey || "caption"} />
-                </div>
-                {/* details */}
-                <div style={{ opacity: this.detailsExpanded ? 1 : 0, transition: "opacity 0.3s ease" }}>
-                    <FormattedTextBox {...this.props} fieldKey={this.props.detailsFieldKey || "captiondetails"} />
-                </div>
-                {/* toggle */}
-                <div
-                    style={{
-                        width: size,
-                        height: size,
-                        borderRadius: "50%",
-                        backgroundColor: "red",
-                        zIndex: 3,
-                        cursor: "pointer"
-                    }}
-                    onClick={this.toggleDetails}
-                >
-                    <span style={{ color: "white" }}></span>
-                </div>
-            </div>
-        );
-    }
-
-}
diff --git a/src/client/views/document_templates/image_card/ImageCard.tsx b/src/client/views/document_templates/image_card/ImageCard.tsx
deleted file mode 100644
index 868afc423..000000000
--- a/src/client/views/document_templates/image_card/ImageCard.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as React from 'react';
-import { FieldViewProps } from '../../nodes/FieldView';
-import { ImageBox } from '../../nodes/ImageBox';
-
-export default class ImageCard extends React.Component<FieldViewProps> {
-
-    render() {
-        return (
-            <div style={{ padding: 30, borderRadius: 15 }}>
-                <ImageBox {...this.props} />
-            </div>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 387da88f5..73fe4fb5d 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -55,7 +55,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
     private contentScaling = () => this.scaling;
 
     private PanelWidth = () => this.panelWidth;
-    private PanelHeight = () => this.panelHeight;;
+    private PanelHeight = () => this.panelHeight;
 
     @computed get panelWidth() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); }
     @computed get panelHeight() { return this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 330410a92..704da3c74 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -271,7 +271,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
             preventDefault && e.preventDefault();
         }
-    };
+    }
 
     buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
         const linkDocs = DocListCast(this.props.Document.links);
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 30be64040..c2f1c9ac7 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -369,7 +369,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.props.Document._showSidebar = !this.props.Document._showSidebar }, icon: "expand-arrows-alt" });
+        funcs.push({ description: "Toggle Sidebar", event: () => { e.stopPropagation(); this.props.Document._showSidebar = !this.props.Document._showSidebar; }, icon: "expand-arrows-alt" });
         funcs.push({ description: "Record Bullet", event: () => { e.stopPropagation(); this.recordBullet(); }, icon: "expand-arrows-alt" });
         ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
             funcs.push({
@@ -1067,7 +1067,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
     }
 
     @computed get sidebarWidthPercent() { return StrCast(this.props.Document.sidebarWidthPercent, "0%"); }
-    sidebarWidth = () => { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); }
+    sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
     sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()), 0);
     @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index a2aec699f..a96a26b61 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -259,7 +259,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
             if (!this._pdfjsRequested) {
                 this._pdfjsRequested = true;
                 const promise = Pdfjs.getDocument(pdfUrl.url.href).promise;
-                promise.then(pdf => { runInAction(() => { this._pdf = pdf; console.log("promise"); }) });
+                promise.then(action(pdf => { this._pdf = pdf; console.log("promise"); }));
 
             }
         }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 4f50be5b0..a9b8c6bbe 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -127,9 +127,8 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         // change the address to be the file address of the PNG version of each page
         // file address of the pdf
         const { url: { href } } = Cast(this.props.Document[this.props.fieldKey], PdfField)!;
-        this._coverPath = href.startsWith(window.location.origin) ?
-            JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/pdfs/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.png`))) :
-            { width: 100, height: 100, path: "" };
+        const addr = Utils.prepend(`/thumbnail${this.props.url.substring("files/pdfs/".length, this.props.url.length - ".pdf".length)}-${(this.Document.curPage || 1)}.png`);
+        this._coverPath = href.startsWith(window.location.origin) ? JSON.parse(await rp.get(addr)) : { width: 100, height: 100, path: "" };
         runInAction(() => this._showWaiting = this._showCover = true);
         this.props.startupLive && this.setupPdfJsViewer();
         this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => {
@@ -623,7 +622,7 @@ export class PDFViewer extends DocAnnotatableComponent<IViewerProps, PdfDocument
         TraceMobx();
         return <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.Document.nativeHeight), transform: `scale(${this._zoomed})` }} ref={this._annotationLayer}>
             {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) =>
-                <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc!} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)}
+                <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)}
         </div>;
     }
     overlayTransform = () => this.scrollXf().scale(1 / this._zoomed);
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index ebc0a1272..6d7f6b56e 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -100,7 +100,7 @@ export function makeEditable() {
     _setter = _setterImpl;
 }
 
-let layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
+const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
     "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
-- 
cgit v1.2.3-70-g09d2


From e816fa75571e2149bbc946dfddb0ca78cf33ca28 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 19 Feb 2020 14:40:34 -0500
Subject: chome display set to 'none' when not 'enabled'. fixed template
 expansion bug.

---
 src/client/views/MainView.tsx                       |  4 ++--
 src/client/views/collections/CollectionView.tsx     |  9 ++++-----
 .../views/collections/CollectionViewChromes.scss    |  3 ++-
 .../views/collections/CollectionViewChromes.tsx     |  7 +++++--
 src/client/views/nodes/ImageBox.tsx                 |  2 +-
 src/new_fields/Doc.ts                               | 21 ++-------------------
 6 files changed, 16 insertions(+), 30 deletions(-)

(limited to 'src/client/views/collections/CollectionViewChromes.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1849ec250..cc75a68fe 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -454,11 +454,11 @@ export class MainView extends React.Component {
             {this.docButtons}
         </div>;
     }
-    //`${StrCast(sidebar.backgroundColor, this.darkScheme ? "dimGray" : "black")}` }} >
+
     @computed get mainContent() {
         const sidebar = this.userDoc && this.userDoc.sidebarContainer;
         return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
-            <div className="mainView-mainContent" style={{ color: this.darkScheme ? "dimGray" : "black" }} >
+            <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black" }} >
                 <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
                     <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}
                         style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 95f9fa62f..157229ec8 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -109,9 +109,6 @@ export class CollectionView extends Touchable<FieldViewProps> {
         return viewField;
     }
 
-    // bcz: Argh?  What's the height of the collection chromes??  
-    chromeHeight = () => (this.props.Document._chromeStatus === "enabled" ? -60 : 0);
-
     active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0;
 
     whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); };
@@ -128,15 +125,17 @@ export class CollectionView extends Touchable<FieldViewProps> {
 
     @action.bound
     removeDocument(doc: Doc): boolean {
+        const targetDataDoc = this.props.Document[DataSym];
         const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
         docView && SelectionManager.DeselectDoc(docView);
-        const value = Cast(this.props.Document[DataSym][this.props.fieldKey], listSpec(Doc), []);
+        const value = DocListCast(targetDataDoc[this.props.fieldKey]);
         let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
         index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
 
         ContextMenu.Instance.clearItems();
         if (index !== -1) {
             value.splice(index, 1);
+            targetDataDoc[this.props.fieldKey] = new List<Doc>(value);
             return true;
         }
         return false;
@@ -162,7 +161,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
     }
 
     private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => {
-        const props: SubCollectionViewProps = { ...this.props, ...renderProps, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" };
+        const props: SubCollectionViewProps = { ...this.props, ...renderProps, CollectionView: this, annotationsKey: "" };
         switch (type) {
             case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />);
             case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 517f467b7..0c96a662c 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -2,7 +2,8 @@
 @import '~js-datepicker/dist/datepicker.min.css';
 
 .collectionViewChrome-cont {
-    position: relative;
+    position: absolute;
+    width:100%;
     opacity: 0.9;
     z-index: 9001;
     transition: top .5s;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 0378c818c..49a9e8200 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -257,6 +257,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     subChrome = () => {
+        const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
+        if (collapsed) return null;
         switch (this.props.type) {
             case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
             case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />);
@@ -368,7 +370,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled";
         return (
             <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0, height: collapsed ? 0 : undefined }}>
-                <div className="collectionViewChrome">
+                <div className="collectionViewChrome" style={{ border: "unset" }}>
                     <div className="collectionViewBaseChrome">
                         <button className="collectionViewBaseChrome-collapse"
                             style={{
@@ -384,6 +386,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                             className="collectionViewBaseChrome-viewPicker"
                             onPointerDown={stopPropagation}
                             onChange={this.viewChanged}
+                            style={{ display: collapsed ? "none" : undefined }}
                             value={NumCast(this.props.CollectionView.props.Document._viewType)}>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform</option>
                             <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema</option>
@@ -438,7 +441,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 </div>
                             </div>
                         </div>
-                        <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
+                        <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{ display: collapsed ? "none" : undefined }}>
                             <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
                                 <div className="commandEntry-drop">
                                     <FontAwesomeIcon icon="bullseye" size="2x"></FontAwesomeIcon>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ba09a02a3..bb421d315 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -96,7 +96,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
                 if (targetDoc[targetField] instanceof ImageField) {
                     this.dataDoc[this.props.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
                     this.dataDoc[this.props.fieldKey + "-nativeWidth"] = NumCast(targetDoc[targetField + "-nativeWidth"]);
-                    this.dataDoc[this.props.fieldKey + "-nativeHeight"] = NumCast(targeDoc[targetField + "-nativeHeight"]);
+                    this.dataDoc[this.props.fieldKey + "-nativeHeight"] = NumCast(targetDoc[targetField + "-nativeHeight"]);
                     e.stopPropagation();
                 }
             }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 1f2dd1c8f..2c7fc6672 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -471,7 +471,6 @@ export namespace Doc {
         const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + "]";
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
         if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
-
             expandedTemplateLayout = undefined;
         } else if (templateLayoutDoc.resolvedDataDoc === Doc.GetProto(targetDoc)) {
             expandedTemplateLayout = templateLayoutDoc;
@@ -484,7 +483,7 @@ export namespace Doc {
                     targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
                     const dataDoc = Doc.GetProto(targetDoc);
                     newLayoutDoc.resolvedDataDoc = dataDoc;
-                    if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List && Cast(templateLayoutDoc[templateField], listSpec(Doc), []).length) {
+                    if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) {
                         dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc: templateLayoutDoc });
                     }
                 }
@@ -497,25 +496,9 @@ export namespace Doc {
     // otherwise, it just returns the childDoc
     export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
         const existingResolvedDataDoc = childDoc[DataSym] !== Doc.GetProto(childDoc)[DataSym] && childDoc[DataSym];
-        const resolvedDataDoc = existingResolvedDataDoc || (containerDataDoc === containerDoc || !containerDataDoc || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc);
+        const resolvedDataDoc = existingResolvedDataDoc || (Doc.AreProtosEqual(containerDataDoc, containerDoc) || !containerDataDoc || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField) ? undefined : containerDataDoc);
         return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc), data: resolvedDataDoc };
     }
-    export function CreateDocumentExtensionForField(doc: Doc, fieldKey: string) {
-        let proto: Doc | undefined = doc;
-        while (proto && !Doc.IsPrototype(proto) && proto.proto) {
-            proto = proto.proto;
-        }
-        let docExtensionForField = ((proto || doc)[fieldKey + "_ext"] as Doc);
-        if (!docExtensionForField) {
-            docExtensionForField = new Doc(doc[Id] + fieldKey, true);
-            docExtensionForField.title = fieldKey + ".ext"; // courtesy field--- shouldn't be needed except maybe for debugging
-            docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends.
-            docExtensionForField.extendsField = fieldKey; // this can be used by search to map matches on the extension doc back to the field that was extended.
-            docExtensionForField.type = DocumentType.EXTENSION;
-            (proto || doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField);
-        }
-        return docExtensionForField;
-    }
 
     export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
         Object.keys(doc).forEach(key => {
-- 
cgit v1.2.3-70-g09d2

