From 0d263dba422a96c5d22f29a9b6411ff074c92645 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 25 Jan 2020 17:28:57 -0500
Subject: added carousel view

---
 .../views/collections/CollectionCarouselView.tsx   | 62 ++++++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 src/client/views/collections/CollectionCarouselView.tsx

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

diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
new file mode 100644
index 000000000..f2cc2479f
--- /dev/null
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -0,0 +1,62 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { makeInterface } from '../../../new_fields/Schema';
+import { NumCast } from '../../../new_fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
+import "./CollectionCarouselView.scss";
+import { CollectionSubView } from './CollectionSubView';
+import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
+import { Doc } from '../../../new_fields/Doc';
+
+
+
+
+type CarouselDocument = makeInterface<[typeof documentSchema,]>;
+const CarouselDocument = makeInterface(documentSchema);
+
+@observer
+export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
+    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+    private _dropDisposer?: DragManager.DragDropDisposer;
+
+    componentWillUnmount() {
+        this._dropDisposer && this._dropDisposer();
+    }
+
+    componentDidMount() {
+    }
+    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+        this._dropDisposer && this._dropDisposer();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
+        }
+    }
+
+    render() {
+        const index = NumCast(this.layoutDoc._itemIndex);
+        return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) :
+            <div className="collectionCarouselView-outer">
+                <ContentFittingDocumentView
+                    {...this.props}
+                    Document={this.childLayoutPairs[index].layout}
+                    DataDocument={this.childLayoutPairs[index].data}
+                    getTransform={this.props.ScreenToLocalTransform} />
+                <div className="carouselView-back" onClick={() => this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length}>
+                    <FontAwesomeIcon
+                        icon={faCaretLeft}
+                        size={"2x"}
+                    />
+                </div>
+                <div className="carouselView-fwd" onClick={() => this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length}>
+                    <FontAwesomeIcon
+                        icon={faCaretRight}
+                        size={"2x"}
+                    />
+                </div>
+            </div>;
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 5e67aa8ab95e40730acb584cb5ff812fe470608f Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 27 Jan 2020 17:32:47 -0500
Subject: template fixes

---
 src/client/util/DropConverter.ts                   |  8 +-
 src/client/util/RichTextRules.ts                   | 62 +++++++-------
 src/client/util/RichTextSchema.tsx                 | 97 +++++++++++++---------
 src/client/views/DocComponent.tsx                  |  4 +-
 .../views/collections/CollectionCarouselView.tsx   |  4 +-
 src/client/views/nodes/FieldView.tsx               |  6 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  2 +-
 7 files changed, 104 insertions(+), 79 deletions(-)

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

diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 9c068d2d7..8d92de28f 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -4,7 +4,9 @@ import { DocumentType } from "../documents/DocumentTypes";
 import { ObjectField } from "../../new_fields/ObjectField";
 import { StrCast } from "../../new_fields/Types";
 import { Docs } from "../documents/Documents";
-import { ScriptField } from "../../new_fields/ScriptField";
+import { ScriptField, ComputedField } from "../../new_fields/ScriptField";
+import { RichTextField } from "../../new_fields/RichTextField";
+import { Compute } from "google-auth-library";
 
 export function makeTemplate(doc: Doc): boolean {
     const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
@@ -18,6 +20,10 @@ export function makeTemplate(doc: Doc): boolean {
             Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc));
         } else if (d.type === DocumentType.COL) {
             any = makeTemplate(d) || any;
+        } else if (d.data instanceof RichTextField) {
+            d._textTemplate = ComputedField.MakeFunction("copyField(this.data)", { this: Doc.name });
+            d.isTemplateForField = "data";
+            any = true;
         }
     });
     return any;
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index bc8a0abb1..0de0e21fd 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -2,14 +2,15 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from
 import { schema } from "./RichTextSchema";
 import { wrappingInputRule } from "./prosemirrorPatches";
 import { NodeSelection, TextSelection } from "prosemirror-state";
-import { StrCast, Cast } from "../../new_fields/Types";
-import { Doc } from "../../new_fields/Doc";
+import { StrCast, Cast, NumCast } from "../../new_fields/Types";
+import { Doc, DataSym } from "../../new_fields/Doc";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { Docs, DocUtils } from "../documents/Documents";
 import { Id } from "../../new_fields/FieldSymbols";
 import { DocServer } from "../DocServer";
 import { returnFalse, Utils } from "../../Utils";
 import RichTextMenu from "./RichTextMenu";
+import { RichTextField } from "../../new_fields/RichTextField";
 
 export const inpRules = {
     rules: [
@@ -70,36 +71,26 @@ export const inpRules = {
                 return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
             }),
 
-        // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode)
+        // create a text display of a metadata field
         new InputRule(
-            new RegExp(/!$/),
+            new RegExp(/\[\[([a-zA-Z_ \-0-9]+)\]\]$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
-                const value = state.doc.textBetween(start, end);
-
-                const node = (state.doc.resolve(start) as any).nodeAfter;
-                const sm = state.storedMarks || undefined;
-                const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(value) });
-                const replaced = node ? state.tr.replaceRangeWith(start, end, fieldView).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
-                return replaced.doc.nodeSize > end - 2 ? replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))) : replaced;
+                const fieldKey = match[1];
+                const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(fieldKey) });
+                return state.tr.deleteRange(start, end).insert(start, fieldView);
             }),
-        // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode)
+        // create a hyperlink portal
         new InputRule(
-            new RegExp(/@$/),
+            new RegExp(/\[\[:([a-zA-Z_ \-0-9]+)\]\]$/),
             (state, match, start, end) => {
-                if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null;
-
-                const value = state.doc.textBetween(start, end);
-                if (value) {
-                    DocServer.GetRefField(value).then(docx => {
-                        const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500, }, value);
-                        DocUtils.Publish(target, value, returnFalse, returnFalse);
-                        DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", "");
-                    });
-                    const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value, targetId: value });
-                    return state.tr.addMark(start, end, link);
-                }
-                return state.tr;
+                const docId = match[1].substring(1);
+                DocServer.GetRefField(docId).then(docx => {
+                    const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, }, docId);
+                    DocUtils.Publish(target, docId, returnFalse, returnFalse);
+                    DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", "");
+                });
+                const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId });
+                return state.tr.addMark(start, end, link);
             }),
         // stop using active style
         new InputRule(
@@ -209,10 +200,21 @@ export const inpRules = {
         new InputRule(
             new RegExp(/%#$/),
             (state, match, start, end) => {
-                const target = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, _autoHeight: true, fontSize: 9, title: "inline comment" });
+                const textDoc = Doc.GetProto(Cast((schema as any).Document[DataSym], Doc, null)!);
+                const numInlines = NumCast(textDoc.inlineTextCount);
+                textDoc.inlineTextCount = numInlines + 1;
+                const inlineFieldKey = "inline" + numInlines;
+                const textDocInline = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" });
+                textDocInline.layoutKey = "layout_" + inlineFieldKey;
+                textDocInline.customTitle = true;
+                textDocInline.title = "inline";
+                textDocInline.isTemplateForField = inlineFieldKey;
+                textDocInline.proto = textDoc;
+                textDoc[textDocInline.layoutKey] = FormattedTextBox.LayoutString(inlineFieldKey);
+                textDoc[inlineFieldKey] = "-inline-";
                 const node = (state.doc.resolve(start) as any).nodeAfter;
-                const newNode = schema.nodes.dashComment.create({ docid: target[Id] });
-                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" });
+                const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] });
+                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: textDocInline[Id], float: "right" });
                 const sm = state.storedMarks || undefined;
                 const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
                     state.tr;
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 21916e3d6..0deedbe39 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -18,6 +18,7 @@ import { Transform } from "./Transform";
 import React = require("react");
 import { BoolCast, NumCast, StrCast } from "../../new_fields/Types";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
+import { ObjectField } from "../../new_fields/ObjectField";
 
 const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -682,7 +683,7 @@ export class DashDocCommentView {
             if (target) {
                 const expand = target.hidden;
                 const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
-                view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs 
+                view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs
                 setTimeout(() => {
                     expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
                     try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { }
@@ -734,12 +735,6 @@ export class DashDocView {
         this._dashSpan.style.height = node.attrs.height;
         this._dashSpan.style.position = "absolute";
         this._dashSpan.style.display = "inline-block";
-        const removeDoc = () => {
-            const pos = getPos();
-            const ns = new NodeSelection(view.state.doc.resolve(pos));
-            view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
-            return true;
-        };
         this._dashSpan.onpointerleave = () => {
             const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid);
             if (ele) {
@@ -752,46 +747,24 @@ export class DashDocView {
                 (ele as HTMLDivElement).style.backgroundColor = "orange";
             }
         };
+        const removeDoc = () => {
+            const pos = getPos();
+            const ns = new NodeSelection(view.state.doc.resolve(pos));
+            view.dispatch(view.state.tr.setSelection(ns).deleteSelection());
+            return true;
+        };
         DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
             if (dashDoc instanceof Doc) {
                 self._dashDoc = dashDoc;
                 dashDoc.hideSidebar = true;
-                if (node.attrs.width !== dashDoc.width + "px" || node.attrs.height !== dashDoc.height + "px") {
+                if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
                     try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
-                        view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc.width + "px", height: dashDoc.height + "px" }));
+                        view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" }));
                     } catch (e) {
                         console.log(e);
                     }
                 }
-                this._reactionDisposer && this._reactionDisposer();
-                this._reactionDisposer = reaction(() => dashDoc[HeightSym]() + dashDoc[WidthSym](), () => {
-                    this._dashSpan.style.height = this._outer.style.height = dashDoc[HeightSym]() + "px";
-                    this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px";
-                });
-                ReactDOM.render(<DocumentView
-                    Document={dashDoc}
-                    LibraryPath={tbox.props.LibraryPath}
-                    fitToBox={BoolCast(dashDoc._fitToBox)}
-                    addDocument={returnFalse}
-                    removeDocument={removeDoc}
-                    ScreenToLocalTransform={this.getDocTransform}
-                    addDocTab={self._textBox.props.addDocTab}
-                    pinToPres={returnFalse}
-                    renderDepth={1}
-                    PanelWidth={self._dashDoc[WidthSym]}
-                    PanelHeight={self._dashDoc[HeightSym]}
-                    focus={self.outerFocus}
-                    backgroundColor={returnEmptyString}
-                    parentActive={returnFalse}
-                    whenActiveChanged={returnFalse}
-                    bringToFront={emptyFunction}
-                    zoomToScale={emptyFunction}
-                    getScale={returnOne}
-                    dontRegisterView={false}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                    ContentScaling={this.contentScaling}
-                />, this._dashSpan);
+                self.doRender(dashDoc, removeDoc);
             }
         });
         const self = this;
@@ -807,6 +780,49 @@ export class DashDocView {
         this._outer.appendChild(this._dashSpan);
         (this as any).dom = this._outer;
     }
+    doRender(dashDoc: Doc, removeDoc: any) {
+        const self = this;
+        const finalLayout = Doc.expandTemplateLayout(dashDoc, this._textBox.dataDoc);
+        if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc), 0);
+        else {
+            const layoutKey = StrCast(finalLayout.layoutKey);
+            const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
+            if (finalLayout !== dashDoc && finalKey) {
+                const finalLayoutField = finalLayout[finalKey]
+                finalLayoutField instanceof ObjectField && (finalLayout._textTemplate = ObjectField.MakeCopy(finalLayoutField));
+            }
+            this._reactionDisposer && this._reactionDisposer();
+            this._reactionDisposer = reaction(() => [finalLayout[WidthSym](), finalLayout[HeightSym]()], (dim) => {
+                this._dashSpan.style.width = this._outer.style.width = dim[0] + "px";
+                this._dashSpan.style.height = this._outer.style.height = dim[1] + "px";
+            }, { fireImmediately: true });
+            ReactDOM.render(<DocumentView
+                Document={finalLayout}
+                DataDoc={this._textBox.dataDoc}
+                LibraryPath={this._textBox.props.LibraryPath}
+                fitToBox={BoolCast(dashDoc._fitToBox)}
+                addDocument={returnFalse}
+                removeDocument={removeDoc}
+                ScreenToLocalTransform={this.getDocTransform}
+                addDocTab={this._textBox.props.addDocTab}
+                pinToPres={returnFalse}
+                renderDepth={1}
+                PanelWidth={finalLayout[WidthSym]}
+                PanelHeight={finalLayout[HeightSym]}
+                focus={this.outerFocus}
+                backgroundColor={returnEmptyString}
+                parentActive={returnFalse}
+                whenActiveChanged={returnFalse}
+                bringToFront={emptyFunction}
+                zoomToScale={emptyFunction}
+                getScale={returnOne}
+                dontRegisterView={false}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+                ContentScaling={this.contentScaling}
+            />, this._dashSpan);
+        }
+    }
     destroy() {
         this._reactionDisposer && this._reactionDisposer();
     }
@@ -837,8 +853,9 @@ export class DashFieldView {
         this._labelSpan.style.fontWeight = "bold";
         this._labelSpan.style.fontSize = "larger";
         this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `;
+        const ddoc = tbox.props.DataDoc || tbox.dataDoc;
         this._reactionDisposer && this._reactionDisposer();
-        this._reactionDisposer = reaction(() => this._textBoxDoc[DataSym][node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true });
+        this._reactionDisposer = reaction(() => ddoc[node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true });
         this._fieldWrapper.appendChild(this._labelSpan);
         this._fieldWrapper.appendChild(this._fieldSpan);
         (this as any).dom = this._fieldWrapper;
@@ -1012,7 +1029,7 @@ export class SummaryView {
             view.dispatch(view.state.tr.
                 setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed)
                 replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it
-                setNodeMarkup(getPos(), undefined, attrs)); // update the attrs 
+                setNodeMarkup(getPos(), undefined, attrs)); // update the attrs
             e.preventDefault();
             e.stopPropagation();
             this._collapsed.className = this.className(visible);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index d98301cf6..a31997a10 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -34,7 +34,7 @@ export function DocExtendableComponent<P extends DocExtendableProps, T>(schemaCt
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
-        @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
+        @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Cast(this.props.Document.resolvedDataDoc, Doc, null) || Doc.GetProto(this.props.Document)) as Doc; }
         active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);//  && !InkingControl.Instance.selectedTool;  // bcz: inking state shouldn't affect static tools 
     }
     return Component;
@@ -57,7 +57,7 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(schema
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document(): T { return schemaCtor(this.props.Document); }
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
-        @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; }
+        @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Cast(this.props.Document.resolvedDataDoc, Doc, null) || Doc.GetProto(this.props.Document)) as Doc; }
 
         _annotationKey: string = "annotations";
         public set annotationKey(val: string) { this._annotationKey = val; }
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index f2cc2479f..9f32bb0c9 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -45,13 +45,13 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
                     Document={this.childLayoutPairs[index].layout}
                     DataDocument={this.childLayoutPairs[index].data}
                     getTransform={this.props.ScreenToLocalTransform} />
-                <div className="carouselView-back" onClick={() => this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length}>
+                <div className="carouselView-back" onClick={e => { e.stopPropagation(); this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length }}>
                     <FontAwesomeIcon
                         icon={faCaretLeft}
                         size={"2x"}
                     />
                 </div>
-                <div className="carouselView-fwd" onClick={() => this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length}>
+                <div className="carouselView-fwd" onClick={e => { e.stopPropagation(); this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length }}>
                     <FontAwesomeIcon
                         icon={faCaretRight}
                         size={"2x"}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 538e433a9..4904ee5c3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -69,9 +69,9 @@ export class FieldView extends React.Component<FieldViewProps> {
         // if (typeof field === "string") {
         //     return <p>{field}</p>;
         // }
-        else if (field instanceof RichTextField) {
-            return <FormattedTextBox {...this.props} />;
-        }
+        // else if (field instanceof RichTextField) {
+        //     return <FormattedTextBox {...this.props} />;
+        // }
         else if (field instanceof ImageField) {
             return <ImageBox {...this.props} />;
         }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fab267358..5c9f1a754 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -496,7 +496,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
 
         this._reactionDisposer = reaction(
             () => {
-                const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
+                const field = Cast(this.props.Document._textTemplate || this.dataDoc[this.props.fieldKey], RichTextField);
                 return field ? field.Data : RichTextUtils.Initialize();
             },
             incomingValue => {
-- 
cgit v1.2.3-70-g09d2


From d1ed73e0a0fa3f3da9811edfe3233c663d34cffa Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 28 Jan 2020 15:15:47 -0500
Subject: functional child templates for Buxton view

---
 .../views/collections/CollectionCarouselView.tsx   | 52 ++++++++++-------
 .../views/collections/CollectionPivotView.tsx      | 66 +++++++---------------
 .../CollectionStackingViewFieldColumn.tsx          |  1 +
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  1 +
 src/new_fields/Doc.ts                              |  4 ++
 5 files changed, 58 insertions(+), 66 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 9f32bb0c9..815dfb35a 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observable } from 'mobx';
+import { observable, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { documentSchema } from '../../../new_fields/documentSchemas';
@@ -36,27 +36,37 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
         }
     }
 
-    render() {
+    advance = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) + 1) % this.childLayoutPairs.length;
+    }
+    goback = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
+    }
+
+    @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) :
-            <div className="collectionCarouselView-outer">
-                <ContentFittingDocumentView
-                    {...this.props}
-                    Document={this.childLayoutPairs[index].layout}
-                    DataDocument={this.childLayoutPairs[index].data}
-                    getTransform={this.props.ScreenToLocalTransform} />
-                <div className="carouselView-back" onClick={e => { e.stopPropagation(); this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length }}>
-                    <FontAwesomeIcon
-                        icon={faCaretLeft}
-                        size={"2x"}
-                    />
-                </div>
-                <div className="carouselView-fwd" onClick={e => { e.stopPropagation(); this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length }}>
-                    <FontAwesomeIcon
-                        icon={faCaretRight}
-                        size={"2x"}
-                    />
-                </div>
-            </div>;
+            <ContentFittingDocumentView {...this.props}
+                Document={this.childLayoutPairs[index].layout}
+                DataDocument={this.childLayoutPairs[index].data}
+                getTransform={this.props.ScreenToLocalTransform} />
+    }
+    @computed get buttons() {
+        return <>
+            <div key="back" className="carouselView-back" onClick={this.goback}>
+                <FontAwesomeIcon icon={faCaretLeft} size={"2x"} />
+            </div>
+            <div key="fwd" className="carouselView-fwd" onClick={this.advance}>
+                <FontAwesomeIcon icon={faCaretRight} size={"2x"} />
+            </div>
+        </>;
+    }
+    render() {
+        return <div className="collectionCarouselView-outer">
+            {this.content}
+            {this.buttons}
+        </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 98fc54b3b..66c7c9745 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -1,61 +1,37 @@
-import { CollectionSubView } from "./CollectionSubView";
-import React = require("react");
-import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons";
-import { Doc, DocListCast, Field, DocCastAsync } from "../../../new_fields/Doc";
-import "./CollectionPivotView.scss";
-import { observer } from "mobx-react";
-import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
-import { CollectionTreeView } from "./CollectionTreeView";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
-import { Docs } from "../../documents/Documents";
-import { ScriptField, ComputedField } from "../../../new_fields/ScriptField";
-import { CompileScript, Scripting } from "../../util/Scripting";
-import { anchorPoints, Flyout } from "../TemplateMenu";
+import { faEdit } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { List } from "../../../new_fields/List";
+import { action, computed, IReactionDisposer, observable } from "mobx";
+import { observer } from "mobx-react";
 import { Set } from "typescript-collections";
-import { PrefetchProxy } from "../../../new_fields/Proxy";
-import { EditableView } from "../EditableView";
+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 { CompileScript } from "../../util/Scripting";
+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");
 
 @observer
 export class CollectionPivotView extends CollectionSubView(doc => doc) {
-    private _narrativeDisposer: IReactionDisposer | undefined;
-    componentWillUnmount() {
-        this._narrativeDisposer?.();
-    }
     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", "viewSpecScript"]);
+            this.props.Document.excludeFields = new List<string>(["_facetCollection", "_docFilter"]);
 
             const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)";
-            const script = CompileScript(scriptText, {
-                params: { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name },
-                typecheck: false,
-                editable: true,
-            });
-            if (script.compiled) {
-                facetCollection.onCheckedClick = new ScriptField(script);
-            }
-            const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); ";
-            this._narrativeDisposer = reaction(() => DocCastAsync(this.props.Document.childDetailed),
-                (childDetailedPromise) => childDetailedPromise.then(childDetailed => {
-                    if (childDetailed) {
-                        const openDocScript = CompileScript(openDocText, {
-                            params: { this: Doc.name, heading: "boolean", containingTreeView: Doc.name },
-                            capturedVariables: { childDetailed: new PrefetchProxy(childDetailed) },
-                            typecheck: false,
-                            editable: true,
-                        });
-                        if (openDocScript.compiled) {
-                            this.props.Document.onChildClick = new ScriptField(openDocScript);
-                        }
-                    }
-                }), { fireImmediately: true });
+            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;
         }
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6a6f7aa6d..9cd9c1465 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -283,6 +283,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                 }, icon: "compress-arrows-alt"
             }));
         layoutItems.push({ description: ":freeform", event: () => this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
+        layoutItems.push({ description: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" });
         layoutItems.push({ description: ":columns", event: () => this.props.parent.props.addDocument(Docs.Create.MulticolumnDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
         layoutItems.push({ description: ":image", event: () => this.props.parent.props.addDocument(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { _width: 200, _height: 200 })), icon: "compress-arrows-alt" });
 
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ea3c505d8..846d402ab 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -114,6 +114,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 PanelWidth={this.finalPanelWidth}
                 PanelHeight={this.finalPanelHeight}
             /> : <ContentFittingDocumentView {...this.props}
+                CollectionDoc={this.props.ContainingCollectionDoc}
                 DataDocument={this.props.DataDoc}
                 getTransform={this.getTransform}
                 active={returnFalse}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 5ad8ad412..ca3aa0167 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -16,6 +16,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u
 import { intersectRect } from "../Utils";
 import { UndoManager } from "../client/util/UndoManager";
 import { computedFn } from "mobx-utils";
+import { RichTextField } from "./RichTextField";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -610,6 +611,9 @@ export namespace Doc {
             Cast(templateField.data, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
             (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateField.data));
         }
+        if (templateField.data instanceof RichTextField && templateField.data.Data) {
+            templateField._textTemplate = ObjectField.MakeCopy(templateField.data);
+        }
 
         // get the layout string that the template uses to specify its layout
         const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
-- 
cgit v1.2.3-70-g09d2


From 0361c04360362c31e4bdd5d27b52707b4e288662 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 28 Jan 2020 18:14:59 -0500
Subject: added captions to carousel. cleaned up some stuff.

---
 src/client/documents/Documents.ts                  |  7 +++-
 src/client/util/RichTextSchema.tsx                 |  2 +-
 .../views/collections/CollectionCarouselView.scss  | 12 ++++++
 .../views/collections/CollectionCarouselView.tsx   | 24 ++++++++----
 .../views/collections/CollectionDockingView.tsx    | 44 +++++++++++++++++++++-
 .../views/collections/CollectionPivotView.tsx      |  5 +--
 src/client/views/collections/CollectionSubView.tsx |  6 +--
 .../views/collections/CollectionTreeView.tsx       | 44 ++++++++++++----------
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +-
 src/scraping/buxton/scraper.py                     | 20 ++++------
 10 files changed, 116 insertions(+), 52 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a7f939d7d..e481b1a4a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -74,7 +74,8 @@ export interface DocumentOptions {
     _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts
     _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts
     _textTemplate?: RichTextField; // template used by a formattedTextBox to create a text box to render
-    _itemIndex?: number;
+    _itemIndex?: number; // which item index the carousel viewer is showing
+    _hideSidebar?: boolean;  //whether an annotationsidebar should be displayed for text docuemnts
     x?: number;
     y?: number;
     z?: number;
@@ -127,6 +128,7 @@ export interface DocumentOptions {
     color?: string;
     treeViewHideTitle?: boolean; // whether to hide the title of a tree view
     treeViewOpen?: boolean; // whether this document is expanded in a tree view
+    treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked
     isFacetFilter?: boolean; // whether document functions as a facet filter in a tree view
     limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
     // [key: string]: Opt<Field>;
@@ -336,7 +338,8 @@ export namespace Docs {
      */
     export namespace Create {
 
-        const delegateKeys = ["x", "y", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "_dropAction", "_annotationOn", "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex"];
+        const delegateKeys = ["x", "y", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "_dropAction", "_annotationOn",
+            "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_hideSidebar"];
 
         /**
          * This function receives the relevant document prototype and uses
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 7051ba1c3..f667b86af 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -757,7 +757,7 @@ export class DashDocView {
         DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
             if (dashDoc instanceof Doc) {
                 self._dashDoc = dashDoc;
-                dashDoc.hideSidebar = true;
+                dashDoc._hideSidebar = true;
                 if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
                     try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
                         view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" }));
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 2e0e293b8..4815f1a59 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,6 +1,18 @@
 
 .collectionCarouselView-outer {
     background: gray;
+    .collectionCarouselView-caption {
+        margin-left: 10%;
+        margin-right: 10%;
+        height: 50; 
+        display: inline-block;
+        width: 80%;
+    }
+    .collectionCarouselView-image {
+        height: calc(100% - 50px);
+        display: inline-block;
+        width: 100%;
+    }
 }
 .carouselView-back  {
     position: absolute;
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 815dfb35a..0933d5924 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,13 +4,14 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { documentSchema } from '../../../new_fields/documentSchemas';
 import { makeInterface } from '../../../new_fields/Schema';
-import { NumCast } from '../../../new_fields/Types';
+import { NumCast, StrCast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
 import "./CollectionCarouselView.scss";
 import { CollectionSubView } from './CollectionSubView';
 import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons';
 import { Doc } from '../../../new_fields/Doc';
+import { FormattedTextBox } from '../nodes/FormattedTextBox';
 
 
 
@@ -45,20 +46,29 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument)
         this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
     }
 
+    panelHeight = () => this.props.PanelHeight() - 50;
     @computed get content() {
         const index = NumCast(this.layoutDoc._itemIndex);
         return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) :
-            <ContentFittingDocumentView {...this.props}
-                Document={this.childLayoutPairs[index].layout}
-                DataDocument={this.childLayoutPairs[index].data}
-                getTransform={this.props.ScreenToLocalTransform} />
+            <div>
+                <div className="collectionCarouselView-image">
+                    <ContentFittingDocumentView {...this.props}
+                        Document={this.childLayoutPairs[index].layout}
+                        DataDocument={this.childLayoutPairs[index].data}
+                        PanelHeight={this.panelHeight}
+                        getTransform={this.props.ScreenToLocalTransform} />
+                </div>
+                <div className="collectionCarouselView-caption" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }}>
+                    <FormattedTextBox key={index} {...this.props} Document={this.childLayoutPairs[index].layout} DataDoc={undefined} fieldKey={"caption"}></FormattedTextBox>
+                </div>
+            </div>
     }
     @computed get buttons() {
         return <>
-            <div key="back" className="carouselView-back" onClick={this.goback}>
+            <div key="back" className="carouselView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.goback}>
                 <FontAwesomeIcon icon={faCaretLeft} size={"2x"} />
             </div>
-            <div key="fwd" className="carouselView-fwd" onClick={this.advance}>
+            <div key="fwd" className="carouselView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.advance}>
                 <FontAwesomeIcon icon={faCaretRight} size={"2x"} />
             </div>
         </>;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8cfaa5951..160279efd 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -174,6 +174,45 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         if (removed) CollectionDockingView.Instance._removedDocs.push(removed);
         this.stateChanged();
     }
+    @undoBatch
+    @action
+    public static ReplaceRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[]): boolean {
+        if (!CollectionDockingView.Instance) return false; const instance = CollectionDockingView.Instance;
+        const newItemStackConfig = {
+            type: 'stack',
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+        };
+
+        const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+
+        let retVal = false;
+        if (instance._goldenLayout.root.contentItems[0].isRow) {
+            retVal = Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
+                if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
+                    DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanle) {
+                    child.contentItems[0].remove();
+                    child.addChild(newContentItem, undefined, true);
+                    instance.layoutChanged(document);
+                    return true;
+                } else {
+                    Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
+                        if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
+                            child.contentItems[j].remove();
+                            child.addChild(newContentItem, undefined, true);
+                            return true;
+                        }
+                        return false;
+                    });
+                }
+                return false;
+            });
+        }
+        if (retVal) {
+            instance.stateChanged();
+        }
+        return retVal;
+    }
+
 
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split
@@ -217,8 +256,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     @action
     public static UseRightSplit(document: Doc, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
         document.isDisplayPanel = true;
-        CollectionDockingView.CloseRightSplit(undefined);
-        CollectionDockingView.AddRightSplit(document, dataDoc, libraryPath);
+        if (!CollectionDockingView.ReplaceRightSplit(document, dataDoc, libraryPath)) {
+            CollectionDockingView.AddRightSplit(document, dataDoc, libraryPath);
+        }
     }
 
     @undoBatch
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 66c7c9745..6de20593b 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -9,7 +9,6 @@ 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 { CompileScript } from "../../util/Scripting";
 import { EditableView } from "../EditableView";
 import { anchorPoints, Flyout } from "../TemplateMenu";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
@@ -104,9 +103,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) {
         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={this.props.Document._facetCollection instanceof Doc && DocListCast(this.props.Document._facetCollection.data).some(d => {
-                        return d.title === 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>)}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 321a6d34c..65d421c52 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -70,10 +70,10 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
                 (args) => {
                     const childLayout = Cast(this.props.Document.childLayout, Doc);
                     if (childLayout instanceof Doc) {
-                        this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout, doc, "layoutFromParent"));
+                        this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout, doc, "layout_fromParent"));
                     }
                     else if (!(childLayout instanceof Promise)) {
-                        this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout"));
+                        this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout"));
                     }
                 }, { fireImmediately: true });
 
@@ -183,7 +183,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
             if (docDragData && !docDragData.applyAsTemplate) {
                 if (de.altKey && docDragData.draggedDocuments.length) {
                     this.childDocs.map(doc =>
-                        Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layoutFromParent"));
+                        Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layout_fromParent"));
                     e.stopPropagation();
                     return true;
                 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6927f2856..66055d31d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -34,6 +34,7 @@ import "./CollectionTreeView.scss";
 import React = require("react");
 import { CollectionViewType } from './CollectionView';
 import { RichTextField } from '../../../new_fields/RichTextField';
+import { ObjectField } from '../../../new_fields/ObjectField';
 
 
 export interface TreeViewProps {
@@ -629,13 +630,23 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
         ContextMenu.Instance.addItem({
             description: "Buxton Layout", icon: "eye", event: () => {
-                const { TextDocument, ImageDocument, MulticolumnDocument, TreeDocument, CarouselDocument } = Docs.Create;
+                DocListCast(this.dataDoc[this.props.fieldKey]).map(d => {
+                    DocListCast(d.data).map((img, i) => {
+                        const caption = (d.captions as any)[i]?.data;
+                        if (caption instanceof ObjectField) {
+                            Doc.GetProto(img).caption = ObjectField.MakeCopy(caption as ObjectField);
+                        }
+                        img._hideSidebar = true;
+                        d.captions = undefined;
+                    });
+                });
+                const { TextDocument, ImageDocument, CarouselDocument } = Docs.Create;
                 const { Document } = this.props;
                 const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
                 const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "short_description" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] },  { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] }  ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`;
 
                 const detailedLayout = Docs.Create.StackingDocument([
-                    CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0 }),
+                    CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }),
                     TextDocument("", { title: "details", _autoHeight: true, _textTemplate: new RichTextField(detailedTemplate, "short_description year company") })
                 ], { _chromeStatus: "disabled", title: "detailed layout stack" });
                 detailedLayout.isTemplateDoc = makeTemplate(detailedLayout);
@@ -711,23 +722,18 @@ export class CollectionTreeView extends CollectionSubView(Document) {
 }
 
 Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) {
-    const facetValues = new Set<string>();
-    DocListCast(dataDoc[dataKey]).forEach(child => {
-        Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facetHeader]?.toString() || "(null)"));
-        facetValues.add(Field.toString(child[facetHeader] as Field));
-    });
-    const text = "determineCheckedState(layoutDoc, facetHeader, facetValue)";
-    const params = {
-        layoutDoc: Doc.name,
-        facetHeader: "string",
-        facetValue: "string"
-    };
-    const capturedVariables = { layoutDoc, facetHeader };
-    return new List<Doc>(Array.from(facetValues).sort().map(facetValue => {
-        const value = Docs.Create.TextDocument("", { title: facetValue.toString() });
-        value.treeViewChecked = ComputedField.MakeFunction(text, params, { ...capturedVariables, facetValue });
-        return value;
-    }));
+    const allCollectionDocs = DocListCast(dataDoc[dataKey]);
+    const facetValues = Array.from(allCollectionDocs.reduce((set, child) =>
+        set.add(Field.toString(child[facetHeader] as Field)), new Set<string>()));
+
+    const facetValueDocSet = facetValues.sort().map(facetValue =>
+        Docs.Create.TextDocument("", {
+            title: facetValue.toString(),
+            treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)",
+                { layoutDoc: Doc.name, facetHeader: "string", facetValue: "string" },
+                { layoutDoc, facetHeader, facetValue })
+        }));
+    return new List<Doc>(facetValueDocSet);
 });
 
 Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5c9f1a754..732bbc976 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1099,7 +1099,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 <div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} ref={this._scrollRef}>
                     <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} />
                 </div>
-                {this.props.Document.hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
+                {this.props.Document._hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
                     <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} onClick={e => this.toggleSidebar()} /> :
                     <div className={"formattedTextBox-sidebar" + (InkingControl.Instance.selectedTool !== InkTool.None ? "-inking" : "")}
                         style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
@@ -1116,7 +1116,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                             whenActiveChanged={this.whenActiveChanged}
                             removeDocument={this.removeDocument}
                             moveDocument={this.moveDocument}
-                            addDocument={(doc: Doc) => { doc.hideSidebar = true; return this.addDocument(doc); }}
+                            addDocument={(doc: Doc) => { doc._hideSidebar = true; return this.addDocument(doc); }}
                             CollectionView={undefined}
                             ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)}
                             renderDepth={this.props.renderDepth + 1}
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index 16b248ae7..7cfa4696d 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -112,10 +112,6 @@ def write_collection(parse_results, display_fields, storage_key, viewType):
             "url": parse_results["image_urls"][0],
             "__type": "image"
         }
-        fields["activeHero"] = {
-            "url": parse_results["image_urls"][0],
-            "__type": "image"
-        }
     fields["isPrototype"] = True
     fields["page"] = -1
 
@@ -139,7 +135,7 @@ def write_text_doc(content):
             "proto": protofy(data_doc_guid),
             "x": 10,
             "y": 10,
-            "width": 400,
+            "_width": 400,
             "zIndex": 2
         },
         "__type": "Doc"
@@ -154,7 +150,7 @@ def write_text_doc(content):
                 "__type": "RichTextField"
             },
             "title": content,
-            "nativeWidth": 200,
+            "_nativeWidth": 200,
             "author": "Bill Buxton",
             "creationDate": {
                 "date": datetime.datetime.utcnow().microsecond,
@@ -163,8 +159,8 @@ def write_text_doc(content):
             "isPrototype": True,
             "_autoHeight": True,
             "page": -1,
-            "nativeHeight": 200,
-            "height": 200,
+            "_nativeHeight": 200,
+            "_height": 200,
             "data_text": content
         },
         "__type": "Doc"
@@ -194,7 +190,7 @@ def write_image(folder, name):
             "proto": protofy(data_doc_guid),
             "x": 10,
             "y": 10,
-            "width": min(800, native_width),
+            "_width": min(800, native_width),
             "zIndex": 2,
             "widthUnit": "*",
             "widthMagnitude": 1
@@ -211,7 +207,7 @@ def write_image(folder, name):
                 "__type": "image"
             },
             "title": name,
-            "nativeWidth": native_width,
+            "_nativeWidth": native_width,
             "author": "Bill Buxton",
             "creationDate": {
                 "date": datetime.datetime.utcnow().microsecond,
@@ -219,8 +215,8 @@ def write_image(folder, name):
             },
             "isPrototype": True,
             "page": -1,
-            "nativeHeight": native_height,
-            "height": native_height
+            "_nativeHeight": native_height,
+            "_height": native_height
         },
         "__type": "Doc"
     }
-- 
cgit v1.2.3-70-g09d2

