From 6c7101d4f69dd79a83a48d04356748213c38a435 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 13:31:33 -0400
Subject: making layout icons work better.

---
 src/client/views/nodes/DocumentView.tsx | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 103c3624d..185eafa4a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -80,6 +80,7 @@ export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>
 export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void;
 export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
 export interface DocComponentView {
+    updateIcon?: () => void; // updates the icon representation of the document
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
@@ -1118,6 +1119,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
             onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
             style={{
+                display: "inline",
                 borderRadius: this.borderRounding,
                 pointerEvents: this.pointerEvents,
                 outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1233,6 +1235,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     public iconify() {
+        this.ComponentView?.updateIcon?.();
         const layoutKey = Cast(this.Document.layoutKey, "string", null);
         if (layoutKey !== "layout_icon") {
             this.switchViews(true, "icon");
@@ -1332,8 +1335,8 @@ export function deiconifyViewFunc(documentView: DocumentView) {
 }
 ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
     documentView.iconify();
-}
-);
+    documentView.select(false);
+});
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
     if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
-- 
cgit v1.2.3-70-g09d2


From 49437d51b277206f31cdcdf0106944bab8330f4d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 16:06:15 -0400
Subject: lots of layout fixes to groups, labels, ink to support iconification
 better.  simpliifed documentdecorations.  fixed display artifacts related to
 things not showing up when dragging, or otherwise not getting a halo of
 nested freeform colletions.

---
 src/client/util/CurrentUserUtils.ts                |   8 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/views/DocumentDecorations.scss          | 496 ++++++++++-----------
 src/client/views/DocumentDecorations.tsx           |  10 +-
 src/client/views/InkingStroke.tsx                  |   6 +-
 src/client/views/StyleProvider.tsx                 |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  11 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |   5 +-
 src/client/views/nodes/DocumentIcon.tsx            |   1 +
 src/client/views/nodes/DocumentView.scss           |   3 +
 src/client/views/nodes/DocumentView.tsx            |   8 +-
 src/client/views/nodes/LabelBigText.js             |   1 +
 src/client/views/nodes/LabelBox.scss               |   2 +-
 src/client/views/nodes/LabelBox.tsx                |  31 +-
 14 files changed, 275 insertions(+), 315 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index bc4dbcb2e..6d8e2d30c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -320,17 +320,17 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 180 / 4, _height: 135 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
             const proto = iconColView.proto as Doc;
-            proto["icon-nativeWidth"] = 180 / 4;
-            proto["icon-nativeHeight"] = 135 / 4;
+            proto["icon-nativeWidth"] = 360 / 4;
+            proto["icon-nativeHeight"] = 270 / 4;
             proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png");
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index d6d04db9a..94a09eac4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -424,7 +424,7 @@ export namespace DragManager {
 
         const hideDragShowOriginalElements = (hide: boolean) => {
             dragLabel.style.display = hide ? "" : "none";
-            //!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
+            !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             eles.forEach(ele => ele.hidden = hide);
         };
         options?.hideSource && hideDragShowOriginalElements(true);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 82dca1287..35e37a2cd 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -4,8 +4,8 @@ $linkGap: 3px;
 
 .documentDecorations-Dark,
 .documentDecorations {
-  position: absolute;
-  z-index: 2000;
+    position: absolute;
+    z-index: 2000;
 }
 .documentDecorations-Dark {
     background: dimgray;
@@ -17,11 +17,11 @@ $linkGap: 3px;
     left: 0;
     display: grid;
     grid-template-rows: 20px 8px 1fr 8px;
-    grid-template-columns: 8px 16px 1fr 8px 8px;
+    grid-template-columns: 8px 1fr 8px;
     pointer-events: none;
 
     .documentDecorations-centerCont {
-        grid-column: 3;
+        grid-column: 2;
         background: none;
     }
 
@@ -41,6 +41,7 @@ $linkGap: 3px;
         opacity: 1;
         transform: translate(10px, 10px);
         grid-row: 4;
+        grid-column: 3
     }
 
     .documentDecorations-topLeftResizer,
@@ -73,20 +74,18 @@ $linkGap: 3px;
 
     .documentDecorations-topResizer,
     .documentDecorations-bottomResizer {
-        grid-column-start: 2;
-        grid-column-end: 5;
+        grid-column: 2;
     }
 
     .documentDecorations-bottomRightResizer,
     .documentDecorations-topRightResizer,
     .documentDecorations-rightResizer {
-        grid-column-start: 5;
-        grid-column-end: 7;
+        grid-column: 3;
     }
 
     .documentDecorations-rotation,
     .documentDecorations-borderRadius {
-        grid-column: 5;
+        grid-column: 3;
         grid-row: 4;
         border-radius: 100%;
         background: black;
@@ -132,114 +131,113 @@ $linkGap: 3px;
         opacity: 1;
     }
 
-  .documentDecorations-topLeftResizer,
-  .documentDecorations-leftResizer,
-  .documentDecorations-bottomLeftResizer {
-    grid-column: 1;
-  }
-
-  .documentDecorations-topResizer,
-  .documentDecorations-bottomResizer {
-    grid-column-start: 2;
-    grid-column-end: 5;
-  }
-
-  .documentDecorations-bottomRightResizer,
-  .documentDecorations-topRightResizer,
-  .documentDecorations-rightResizer {
-    grid-column-start: 5;
-    grid-column-end: 7;
-  }
-
-  .documentDecorations-rotation,
-  .documentDecorations-borderRadius {
-    grid-column: 5;
-    grid-row: 4;
-    border-radius: 100%;
-    background: black;
-    height: 8;
-    right: -12;
-    top: 12;
-    position: relative;
-    pointer-events: all;
-    cursor: nwse-resize;
-
-    .borderRadiusTooltip {
-      width: 10px;
-      height: 10px;
-      position: absolute;
-    }
-  }
-  .documentDecorations-rotation {
-    background: transparent;
-    right: -15;
-  }
-
-  .documentDecorations-topLeftResizer,
-  .documentDecorations-bottomRightResizer {
-    cursor: nwse-resize;
-    background: unset;
-    opacity: 1;
-  }
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-bottomLeftResizer {
+        grid-column: 1;
+    }
 
-  .documentDecorations-topLeftResizer {
-    border-left: 2px solid;
-    border-top: solid 2px;
-  }
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
+        grid-column: 2;
+    }
 
-  .documentDecorations-bottomRightResizer {
-    border-right: 2px solid;
-    border-bottom: solid 2px;
-  }
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-rightResizer {
+        grid-column: 3
+    }
 
-  .documentDecorations-topLeftResizer:hover,
-  .documentDecorations-bottomRightResizer:hover {
-    opacity: 1;
-  }
+    .documentDecorations-rotation,
+    .documentDecorations-borderRadius {
+        grid-column: 3;
+        grid-row: 4;
+        border-radius: 100%;
+        background: black;
+        height: 8;
+        right: -12;
+        top: 12;
+        position: relative;
+        pointer-events: all;
+        cursor: nwse-resize;
 
-  .documentDecorations-bottomRightResizer {
-    grid-row: 4;
-  }
+        .borderRadiusTooltip {
+            width: 10px;
+            height: 10px;
+            position: absolute;
+        }
+    }
+    .documentDecorations-rotation {
+        background: transparent;
+        right: -15;
+    }
 
-  .documentDecorations-topRightResizer,
-  .documentDecorations-bottomLeftResizer {
-    cursor: nesw-resize;
-    background: unset;
-    opacity: 1;
-  }
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-bottomRightResizer {
+        cursor: nwse-resize;
+        background: unset;
+        opacity: 1;
+    }
+
+    .documentDecorations-topLeftResizer {
+        border-left: 2px solid;
+        border-top: solid 2px;
+    }
+
+    .documentDecorations-bottomRightResizer {
+        border-right: 2px solid;
+        border-bottom: solid 2px;
+    }
+
+    .documentDecorations-topLeftResizer:hover,
+    .documentDecorations-bottomRightResizer:hover {
+        opacity: 1;
+    }
+
+    .documentDecorations-bottomRightResizer {
+        grid-row: 4;
+    }
+
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer {
+        cursor: nesw-resize;
+        background: unset;
+        opacity: 1;
+    }
 
-  .documentDecorations-topRightResizer {
-    border-right: 2px solid;
-    border-top: 2px solid;
-  }
+    .documentDecorations-topRightResizer {
+        border-right: 2px solid;
+        border-top: 2px solid;
+    }
 
-  .documentDecorations-bottomLeftResizer {
-    border-left: 2px solid;
-    border-bottom: 2px solid;
-  }
+    .documentDecorations-bottomLeftResizer {
+        border-left: 2px solid;
+        border-bottom: 2px solid;
+    }
 
-  .documentDecorations-topRightResizer:hover,
-  .documentDecorations-bottomLeftResizer:hover {
+.documentDecorations-topRightResizer:hover,
+.documentDecorations-bottomLeftResizer:hover {
     cursor: nesw-resize;
     background: black;
     opacity: 1;
-  }
+}
 
-  .documentDecorations-topResizer,
-  .documentDecorations-bottomResizer {
+.documentDecorations-topResizer,
+.documentDecorations-bottomResizer {
     cursor: ns-resize;
-  }
+}
 
-    .documentDecorations-title-Dark,
-    .documentDecorations-title {
+.documentDecorations-title-Dark,
+.documentDecorations-title {
         opacity: 1;
-        grid-column-start: 2;
-        grid-column-end: 4;
+        width: 100%;
+        grid-column: 2;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
         display: flex;
-        margin-left: 5px;
+        padding-left: 5px;
+        padding-right: 12px;
         height: 20px;
         position: absolute;
         border-radius: 8px;
@@ -247,7 +245,7 @@ $linkGap: 3px;
 
         .documentDecorations-titleSpan, 
         .documentDecorations-titleSpan-Dark  {
-            width: 100%;
+            width: calc(100% - 17px); // = padding-left + padding-right
             border-radius: 8px;
             background: #ffffffa0;
             position: absolute;
@@ -263,105 +261,61 @@ $linkGap: 3px;
         background: black;
     }
 
-  .documentDecorations-contextMenu {
-    width: 25px;
-    height: calc(100% + 8px); // 8px for the height of the top resizer bar
-    grid-column-start: 2;
-    grid-column-end: 2;
-    pointer-events: all;
-    padding-left: 5px;
-    cursor: pointer;
-  }
-
-  .documentDecorations-titleBackground {
-    background: #ffffffcf;
-    border-radius: 8px;
-    width: 100%;
-    height: 100%;
-    position: absolute;
-  }
-
-  .documentDecorations-title {
-    opacity: 1;
-    grid-column-start: 2;
-    grid-column-end: 4;
-    pointer-events: auto;
-    overflow: hidden;
-    text-align: center;
-    display: flex;
-    margin-left: 5px;
-    height: 20px;
-    position: absolute;
-    .documentDecorations-titleSpan {
-      width: 100%;
-      border-radius: 8px;
-      background: #ffffffcf;
-      position: absolute;
-      display: inline-block;
-      cursor: move;
-    }
-  }
-
-  .focus-visible {
-    margin-left: 0px;
-  }
-}
+    .documentDecorations-titleBackground {
+        background: #ffffffcf;
+        border-radius: 8px;
+        width: 100%;
+        height: 100%;
+        position: absolute;
+    }
 
-.documentDecorations-iconifyButton {
-  opacity: 1;
-  grid-column-start: 4;
-  grid-column-end: 4;
-  pointer-events: all;
-  right: 0;
-  cursor: pointer;
-  position: absolute;
-  width: 20px;
+    .focus-visible {
+        margin-left: 0px;
+    }
 }
 
 .documentDecorations-openButton {
-  display: flex;
-  align-items: center;
-  opacity: 1;
-  grid-column-start: 5;
-  grid-column-end: 5;
-  pointer-events: all;
-  cursor: pointer;
+    display: flex;
+    align-items: center;
+    opacity: 1;
+    grid-column-start: 3;
+    pointer-events: all;
+    cursor: pointer;
 }
 
 .documentDecorations-closeButton {
-  display: flex;
-  align-items: center;
-  opacity: 1;
-  grid-column-start: 1;
-  grid-column-end: 3;
-  pointer-events: all;
-  cursor: pointer;
-
-  > svg {
-    margin: 0;
-  }
+    display: flex;
+    align-items: center;
+    opacity: 1;
+    grid-column: 1;
+    pointer-events: all;
+    cursor: pointer;
+
+    > svg {
+        margin: 0;
+    }
 }
 
 .documentDecorations-background {
-  background: lightblue;
-  position: absolute;
-  opacity: 0.1;
+    background: lightblue;
+    position: absolute;
+    opacity: 0.1;
 }
 
 .linkFlyout {
-  grid-column: 2/4;
+    grid-column: 2/4;
 }
 
 .linkButton-empty:hover {
-  background: $medium-gray;
-  transform: scale(1.05);
-  cursor: pointer;
+    background: $medium-gray;
+    transform: scale(1.05);
+    cursor: pointer;
 }
 
 .linkButton-nonempty:hover {
-  background: $medium-gray;
-  transform: scale(1.05);
-  cursor: pointer;
+    background: $medium-gray;
+    transform: scale(1.05);
+    cursor: pointer;
 }
 
 .link-button-container {
@@ -379,132 +333,132 @@ $linkGap: 3px;
 }
 
 .linkButtonWrapper {
-  pointer-events: auto;
-  padding-right: 5px;
-  width: 25px;
+    pointer-events: auto;
+    padding-right: 5px;
+    width: 25px;
 }
 
 .linkButton-linker {
-  height: 20px;
-  width: 20px;
-  text-align: center;
-  border-radius: 50%;
-  pointer-events: auto;
-  color: $dark-gray;
-  border: $dark-gray 1px solid;
+    height: 20px;
+    width: 20px;
+    text-align: center;
+    border-radius: 50%;
+    pointer-events: auto;
+    color: $dark-gray;
+    border: $dark-gray 1px solid;
 }
 
 .linkButton-linker:hover {
-  cursor: pointer;
-  transform: scale(1.05);
+    cursor: pointer;
+    transform: scale(1.05);
 }
 
 .linkButton-empty,
 .linkButton-nonempty {
-  height: 20px;
-  width: 20px;
-  border-radius: 50%;
-  opacity: 0.9;
-  pointer-events: auto;
-  background-color: $dark-gray;
-  color: $white;
-  text-transform: uppercase;
-  letter-spacing: 2px;
-  font-size: 75%;
-  transition: transform 0.2s;
-  text-align: center;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-
-  &:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-    cursor: pointer;
-  }
+    height: 20px;
+    width: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    pointer-events: auto;
+    background-color: $dark-gray;
+    color: $white;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &:hover {
+        background: $medium-gray;
+        transform: scale(1.05);
+        cursor: pointer;
+    }
 }
 
 .templating-menu {
-  position: absolute;
-  pointer-events: auto;
-  text-transform: uppercase;
-  letter-spacing: 2px;
-  font-size: 75%;
-  transition: transform 0.2s;
-  text-align: center;
-  display: flex;
-  justify-content: center;
-  align-items: center;
+    position: absolute;
+    pointer-events: auto;
+    text-transform: uppercase;
+    letter-spacing: 2px;
+    font-size: 75%;
+    transition: transform 0.2s;
+    text-align: center;
+    display: flex;
+    justify-content: center;
+    align-items: center;
 }
 
 .documentdecorations-icon {
-  margin: 0px;
+    margin: 0px;
 }
 
 .templating-button,
 .docDecs-tagButton {
-  width: 20px;
-  height: 20px;
-  border-radius: 50%;
-  opacity: 0.9;
-  font-size: 14;
-  background-color: $dark-gray;
-  color: $white;
-  text-align: center;
-  cursor: pointer;
-
-  &:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-  }
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    opacity: 0.9;
+    font-size: 14;
+    background-color: $dark-gray;
+    color: $white;
+    text-align: center;
+    cursor: pointer;
+
+    &:hover {
+        background: $medium-gray;
+        transform: scale(1.05);
+    }
 }
 
 #template-list {
-  position: absolute;
-  top: 25px;
-  left: 0px;
-  width: max-content;
-  font-family: $sans-serif;
-  font-size: 12px;
-  background-color: $light-gray;
-  padding: 2px 12px;
-  list-style: none;
-
-  .templateToggle,
-  .chromeToggle {
-    text-align: left;
-  }
-
-  input {
-    margin-right: 10px;
-  }
+    position: absolute;
+    top: 25px;
+    left: 0px;
+    width: max-content;
+    font-family: $sans-serif;
+    font-size: 12px;
+    background-color: $light-gray;
+    padding: 2px 12px;
+    list-style: none;
+
+    .templateToggle,
+    .chromeToggle {
+        text-align: left;
+    }
+
+    input {
+        margin-right: 10px;
+    }
 }
 
 @-moz-keyframes spin {
-  100% {
-    -moz-transform: rotate(360deg);
-  }
+    100% {
+        -moz-transform: rotate(360deg);
+    }
 }
 
 @-webkit-keyframes spin {
-  100% {
-    -webkit-transform: rotate(360deg);
-  }
+    100% {
+        -webkit-transform: rotate(360deg);
+    }
 }
 
 @keyframes spin {
-  100% {
-    -webkit-transform: rotate(360deg);
-    transform: rotate(360deg);
-  }
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
 }
 
 @keyframes shadow-pulse {
-  0% {
-    box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
-  }
+    0% {
+        box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+    }
 
-  100% {
-    box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
-  }
+    100% {
+        box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+    }
 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1487d5bb0..353843b8d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -449,7 +449,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
-        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles;
+        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc.isGroup;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
         const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
         const canDelete = SelectionManager.Views().some(docView => {
@@ -466,16 +466,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             </Tooltip>);
 
         const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
-        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} style={{ width: "100%" }} key="title" /> :
+        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
             this._edtingTitle ?
                 <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                    style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }}
+                    style={{ width: `100%` }}
                     type="text" name="dynbox" autoComplete="on"
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
                     onKeyPress={this.titleEntered} /> :
-                <div className="documentDecorations-title" style={{ width: `calc(100% - ${hideResizers ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
+                <div className="documentDecorations-title" style={{ width: `100%` }} key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
@@ -515,8 +515,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
                         <>
-                            {SelectionManager.Views().length !== 1 || hideTitle ? (null) :
-                                topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
                             <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                             <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                             <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 5e589fdea..06671961d 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -70,11 +70,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
 
     // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke
     // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc)
-    screenToLocal = () => {
-        console.log("Scaling = " + this.props.scaling?.())
-        console.log("Stolocal = " + this.props.ScreenToLocalTransform().Scale)
-        return this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
-    }
+    screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1);
 
     getAnchor = () => {
         console.log(document.activeElement);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 739670ecf..eb6551fc8 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -98,7 +98,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc &&
             StrCast(doc._showTitle,
                 props?.showTitle?.() ||
-                (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
+                (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
                     (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
                         "author;creationDate") : "")) || "");
         case StyleProp.Color:
@@ -114,7 +114,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
         case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
-            doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
+            doc?.type === DocumentType.RTF || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
             let docColor: Opt<string> =
@@ -143,7 +143,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor ||
-                        (doc?._isGroup ? "#00000004" : // very faint highlight to show bounds of group
+                        (doc?._isGroup ? "#00000010" : // very faint highlight to show bounds of group
                             (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                                 isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 7ca6dfd93..04beed541 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1014,8 +1014,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
         } else {
-            const docs = this.childLayoutPairs.map(pair => pair.layout);
-            docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+            const docs = this.childLayoutPairs.map(pair => pair.layout).slice();
+            docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
             let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1;
             if (zlast - docs.length > 100) {
                 for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1;
@@ -1414,12 +1414,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    clicks = 0;
     updateIcon = () => {
-        if (this.clicks) {
-            console.log("UPDATE ICON");
-        }
-        this.clicks++;
         this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString();
         this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString();
         var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!);
@@ -1431,7 +1426,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         CreateImage(
             "",
             document.styleSheets,
-            htmlString?.replace(/"marqueeView"/g, '"marqueeView marqueeView2"'),
+            htmlString?.replace(/docView-hack/g, 'documentView-hack'),
             nativeWidth,
             nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
             NumCast(this.layoutDoc._scrollTop)
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5149d370f..ba34d846e 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -167,7 +167,10 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelHeight: this.panelHeight,
         };
         const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
+        const mixBlendMode = "";//StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
+        if (mixBlendMode) {
+            console.log(mixBlendMode);
+        }
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 433a0bf48..a9c998757 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -1,3 +1,4 @@
+
 import { observer } from "mobx-react";
 import * as React from "react";
 import { DocumentView } from "./DocumentView";
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 4565f8504..5c5d2df10 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -3,6 +3,9 @@
 .documentView-effectsWrapper {
     border-radius: inherit;
 }
+.documentView-hack {
+    display: inline;
+}
 
 .documentView-customBorder {
     width:100%;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 185eafa4a..423a5edbf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -887,10 +887,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 focus={this.focus}
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
-            {!this.props.isSelected() || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
+            {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
                 <DocumentLinksButton View={this.props.DocumentView()}
                     ContentScaling={this.props.ContentScaling}
-                    Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+                    Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} />
             }
             {audioView}
         </div>;
@@ -1111,7 +1111,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
 
         // Return surrounding highlight 
-        return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
+        return <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
             onKeyDown={this.onKeyDown}
             onPointerDown={this.onPointerDown}
@@ -1119,7 +1119,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
             onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
             style={{
-                display: "inline",
                 borderRadius: this.borderRounding,
                 pointerEvents: this.pointerEvents,
                 outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1244,6 +1243,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
             this.Document.deiconifyLayout = undefined;
+            this.props.bringToFront(this.rootDoc);
         }
     }
     @undoBatch
diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js
index 78fe2839e..02c36c4bc 100644
--- a/src/client/views/nodes/LabelBigText.js
+++ b/src/client/views/nodes/LabelBigText.js
@@ -156,6 +156,7 @@ export default function BigText(element, options) {
         width: element.offsetWidth, //Note: This is slightly larger than the jQuery version
         height: element.offsetHeight,
     };
+    if (!box.width || !box.height) return element;
 
 
     if (options.rotateText !== null) {
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index 6a0d651d2..42e158584 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -4,7 +4,7 @@
     border-radius: inherit;
     display: flex;
     flex-direction: column;
-    position: absolute;
+    position: relative;
 }
 
 .labelBox-mainButton {
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index c689d9f40..3405a7635 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -30,6 +30,11 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
 
     componentDidMount() {
         this.props.setContentView?.(this);
+        console.log("MOUNTING")
+    }
+
+    componentDidUpdate() {
+        console.log("UPDATING")
     }
 
     getTitle() {
@@ -73,6 +78,19 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     @observable _mouseOver = false;
     @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; }
 
+    fitTextToBox = (r: any) => {
+        BigText(r, {
+            rotateText: null,
+            fontSizeFactor: 1,
+            minimumFontSize: NumCast(this.layoutDoc._minFontSize),
+            maximumFontSize: NumCast(this.layoutDoc._maxFontSize),
+            limitingDimension: "both",
+            horizontalAlign: "center",
+            verticalAlign: "center",
+            textAlign: "center",
+            whiteSpace: "nowrap"
+        })
+    }
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
         const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
@@ -97,17 +115,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
                 }} >
                     <span ref={r => {
                         if (r) {
-                            BigText(r, {
-                                rotateText: null,
-                                fontSizeFactor: 1,
-                                minimumFontSize: NumCast(this.layoutDoc._minFontSize),
-                                maximumFontSize: NumCast(this.layoutDoc._maxFontSize),
-                                limitingDimension: "both",
-                                horizontalAlign: "center",
-                                verticalAlign: "center",
-                                textAlign: "center",
-                                whiteSpace: "nowrap"
-                            });
+                            if (!r.offsetWidth || !r.offsetHeight) setTimeout(() => this.fitTextToBox(r));
+                            else this.fitTextToBox(r);
                         }
                     }}>{label.startsWith("#") ? (null) : label}</span>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 0f8b556ac88632d31db4218bcea2e7a559deb6e4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 20:11:42 -0400
Subject: fixed sendtoback menu and functionality.  fixed mixedblendmode for
 groups.  fixed orange highlighting when docs are toggled hidden.   fixed
 placement of linking button.

---
 src/client/util/DocumentManager.ts                        |  5 ++++-
 src/client/views/StyleProvider.tsx                        | 11 ++++++-----
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 10 ++++------
 src/client/views/nodes/DocumentLinksButton.tsx            |  2 +-
 src/client/views/nodes/DocumentView.tsx                   |  4 ++--
 5 files changed, 17 insertions(+), 15 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ad6d90bc3..6076de055 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -169,7 +169,10 @@ export class DocumentManager {
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
         const docView = getFirstDocView(targetDoc, originatingDoc);
         const wasHidden = targetDoc.hidden;  // 
-        if (wasHidden) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here.
+        if (wasHidden) runInAction(() => {
+            targetDoc.hidden = false;
+            docView?.props.bringToFront(targetDoc);
+        });  // if the target is hidden, un-hide it here.
         const focusAndFinish = (didFocus: boolean) => {
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index eb6551fc8..37ef96782 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -143,13 +143,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor ||
-                        (doc?._isGroup ? "#00000010" : // very faint highlight to show bounds of group
-                            (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
-                                isBackground() ? "cyan" : // ?? is there a good default for a background collection
-                                    doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
+                        (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
+                            isBackground() ? "cyan" : // ?? is there a good default for a background collection
+                                doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
+                                    (doc?._isGroup ? undefined : // groups get a faint background color in CollectionfreeformDocumentView
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE))));
+                                            Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE))
+                                    ));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ba34d846e..277d6dc53 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -166,11 +166,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
         };
-        const background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = "";//StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
-        if (mixBlendMode) {
-            console.log(mixBlendMode);
-        }
+        var background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
+        if (!background && this.rootDoc.isGroup) background = "#00000004";
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
@@ -179,7 +177,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 transform: this.transform,
                 transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
-                mixBlendMode,
+                mixBlendMode: mixBlendMode,
                 display: this.ZInd === -99 ? "none" : undefined
             }} >
             {this.props.renderCutoffProvider(this.props.Document) ?
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 6d96a9ab6..46537df7e 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -278,7 +278,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
         //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
         return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
-            <div className="documentLinksButton-wrapper" >
+            <div className="documentLinksButton-wrapper" style={{ transform: this.props.InMenu ? undefined : `scale(${(this.props.ContentScaling?.() || 1) * this.props.View.screenToLocalTransform().Scale})` }} >
                 {
                     (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
                         (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 423a5edbf..9ec7b9a58 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -752,7 +752,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
                     zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
                 }
-                !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
+                !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
 
                 !Doc.UserDoc().noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
                 !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
@@ -1119,6 +1119,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
             onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
             style={{
+                display: this.hidden ? "inline" : undefined,
                 borderRadius: this.borderRounding,
                 pointerEvents: this.pointerEvents,
                 outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1322,7 +1323,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         ContentScaling={this.ContentScale}
                         ScreenToLocalTransform={this.screenToLocalTransform}
                         focus={this.props.focus || emptyFunction}
-                        bringToFront={emptyFunction}
                         ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
                 </div>)}
         </div>);
-- 
cgit v1.2.3-70-g09d2


From 7cd890392f79f1783af2bdb0fe86fa6a49db849a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 11 Apr 2022 23:17:07 -0400
Subject: fixes to header to be transparent for icons.  layout fixes for doc
 decoratoins title row.  groups are not semi transnparent anymore to avoid
 mixBlendMode confusion

---
 src/client/util/CurrentUserUtils.ts                       | 13 +++++--------
 src/client/views/DocumentDecorations.scss                 |  9 +++++----
 src/client/views/DocumentDecorations.tsx                  |  3 +--
 src/client/views/StyleProvider.tsx                        |  2 +-
 src/client/views/nodes/CollectionFreeFormDocumentView.tsx |  5 +++--
 src/client/views/nodes/DocumentView.tsx                   |  6 ++++--
 6 files changed, 19 insertions(+), 19 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0e392cc85..bf722157a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -303,7 +303,7 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-rtf"] === undefined) {
             const iconRtfView = Docs.Create.LabelDocument({
-                title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
+                title: "icon_" + DocumentType.RTF, _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"),
                 _singleLine: false, _minFontSize: 18, _maxFontSize: 24,
                 _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
@@ -320,13 +320,13 @@ export class CurrentUserUtils {
         }
         if (doc["template-icon-view-img"] === undefined) {
             const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", {
-                title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
+                title: "data", _width: 150, isTemplateDoc: true, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true
             });
             iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);
             doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);
         }
         if (doc["template-icon-view-col"] === undefined) {
-            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _showTitle: "title", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL);
             const proto = iconColView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -335,7 +335,7 @@ export class CurrentUserUtils {
             doc["template-icon-view-col"] = new PrefetchProxy(iconColView);
         }
         if (doc["template-icon-view-video"] === undefined) {
-            const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID);
             const proto = iconVidView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -344,7 +344,7 @@ export class CurrentUserUtils {
             doc["template-icon-view-video"] = new PrefetchProxy(iconVidView);
         }
         if (doc["template-icon-view-pdf"] === undefined) {
-            const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
+            const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });
             iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF);
             const proto = iconPdfView.proto as Doc;
             proto["icon-nativeWidth"] = 360 / 4;
@@ -1344,9 +1344,6 @@ export class CurrentUserUtils {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
             doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
         }
-        if (doc.activeCollectionBackground === "white") { // temporary to avoid having to rebuild the databse for old accounts that have this set by default.
-            doc.activeCollectionBackground = undefined;
-        }
         return doc;
     }
 
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 7432d45bf..481b90249 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -230,15 +230,15 @@ $linkGap: 3px;
 .documentDecorations-title-Dark,
 .documentDecorations-title {
         opacity: 1;
-        width: 100%;
+        width: calc(100% - 8px); // = margin-left + margin-right
         grid-column: 2;
         grid-column-end: 2;
         pointer-events: auto;
         overflow: hidden;
         text-align: center;
         display: flex;
-        padding-left: 2px;
-        padding-right: 2px;
+        margin-left: 6px; // closeButton width (14) - leftColumn width (8)
+        margin-right: 2px;
         height: 20px;
         position: absolute;
         border-radius: 8px;
@@ -246,7 +246,7 @@ $linkGap: 3px;
 
         .documentDecorations-titleSpan, 
         .documentDecorations-titleSpan-Dark  {
-            width: calc(100% - 17px); // = padding-left + padding-right
+            width: 100% ;
             border-radius: 8px;
             background: #ffffffa0;
             position: absolute;
@@ -290,6 +290,7 @@ $linkGap: 3px;
     opacity: 1;
     grid-column: 1;
     pointer-events: all;
+    width: 14px;
     cursor: pointer;
 
     > svg {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 353843b8d..9d9505c08 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -469,13 +469,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
             this._edtingTitle ?
                 <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                    style={{ width: `100%` }}
                     type="text" name="dynbox" autoComplete="on"
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
                     onKeyPress={this.titleEntered} /> :
-                <div className="documentDecorations-title" style={{ width: `100%` }} key="title" onPointerDown={this.onTitleDown} >
+                <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 37ef96782..2ce78f64f 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -146,7 +146,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                             isBackground() ? "cyan" : // ?? is there a good default for a background collection
                                 doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
-                                    (doc?._isGroup ? undefined : // groups get a faint background color in CollectionfreeformDocumentView
+                                    (doc?._isGroup ? undefined :
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
                                             Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE))
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 277d6dc53..7fb2c235a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -18,6 +18,8 @@ import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
 import { DocumentType } from "../../documents/DocumentTypes";
+import { collectionSchema } from "../../../fields/documentSchemas";
+import { CollectionViewType } from "../collections/CollectionView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -167,8 +169,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             PanelHeight: this.panelHeight,
         };
         var background = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
-        if (!background && this.rootDoc.isGroup) background = "#00000004";
+        const mixBlendMode = StrCast(this.layoutDoc.mixBlendMode) as any || (typeof background === "string" && background && DashColor(background).alpha() !== 1 ? "multiply" : undefined);
         return <div className={"collectionFreeFormDocumentView-container"}
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9ec7b9a58..195f6c9e5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -859,9 +859,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
                     icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
             </div>;
+
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
+                pointerEvents: this.props.pointerEvents as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             {!this._retryThumb || !this.thumbShown() ? (null) :
@@ -1030,7 +1031,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 />
             </div>;
         const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
-        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
+        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
+            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
         const titleView = !showTitle ? (null) :
             <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
                 position: this.headerMargin ? "relative" : "absolute",
-- 
cgit v1.2.3-70-g09d2


From 594f585b74bf6ecec3795a4b780da619c66f341d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 13 Apr 2022 10:57:08 -0400
Subject: added images to treeView bullets for docs with icons.  fixed
 following pushpin links to prototypes to  toggle delegates when shown.  gave
 groups a default padding.  fixed creating icons to not create unnecessary
 template docs.  fixed toggling isPushpin. made summaries simple links instead
 of templates.

---
 src/client/documents/Documents.ts                  | 38 ++++++++++++----------
 src/client/util/DocumentManager.ts                 |  5 +--
 src/client/views/DocumentDecorations.tsx           |  5 ++-
 src/client/views/StyleProvider.tsx                 | 13 ++++++--
 src/client/views/collections/TreeView.scss         |  2 +-
 src/client/views/collections/TreeView.tsx          |  5 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  9 ++---
 src/client/views/nodes/DocumentView.tsx            |  8 +++--
 src/client/views/nodes/LinkDocPreview.tsx          |  9 ++++-
 9 files changed, 59 insertions(+), 35 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 652f8e7e0..465cdf3ec 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -223,6 +223,7 @@ export class DocumentOptions {
     dockingConfig?: string;
     annotationOn?: Doc;
     isPushpin?: boolean;
+    isGroup?: boolean; // whether a collection should use a grouping UI behavior
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
 
     // BACKGROUND GRID
@@ -807,7 +808,7 @@ export namespace Docs {
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Freeform }, id);
+            const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
             documents.map(d => d.context = inst);
             return inst;
         }
@@ -1317,22 +1318,25 @@ export namespace DocUtils {
         const _height = NumCast(doc._height);
         const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
 
-        let fieldTemplate: Opt<Doc>;
-        if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
-            fieldTemplate = Docs.Create.TextDocument("", options);
-        } else if (doc.data instanceof PdfField) {
-            fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
-        } else if (doc.data instanceof VideoField) {
-            fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
-        } else if (doc.data instanceof AudioField) {
-            fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
-        } else if (doc.data instanceof ImageField) {
-            fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
-        }
-        const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
-
-        fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
-        docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
+        if (docLayoutTemplate) {
+            Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
+        } else {
+            let fieldTemplate: Opt<Doc>;
+            if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
+                fieldTemplate = Docs.Create.TextDocument("", options);
+            } else if (doc.data instanceof PdfField) {
+                fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
+            } else if (doc.data instanceof VideoField) {
+                fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
+            } else if (doc.data instanceof AudioField) {
+                fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
+            } else if (doc.data instanceof ImageField) {
+                fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+            }
+            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
+            fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
+            docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
+        }
     }
     export function makeCustomView(doc: Doc, custom: boolean, layout: string) {
         Doc.setNativeView(doc);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1d603e18f..fb50ecb4a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -176,12 +176,13 @@ export class DocumentManager {
             });  // if the target is hidden, un-hide it here.
         }
         const focusAndFinish = (didFocus: boolean) => {
+            const finalTargetDoc = docView?.Document ?? targetDoc;
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
-                    targetDoc.hidden = !targetDoc.hidden;
+                    finalTargetDoc.hidden = !finalTargetDoc.hidden;
                 }
             } else {
-                targetDoc.hidden && (targetDoc.hidden = undefined);
+                finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined);
                 !noSelect && docView?.select(false);
             }
             finished?.();
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 062ca1181..a8d91e0a2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -453,6 +453,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
             return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
         });
     }
+    @computed get hasIcons() {
+        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
+    }
 
     render() {
         const bounds = this.Bounds;
@@ -516,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", "times", undefined, this.onCloseClick, "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 9803f9782..dba290b46 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -3,10 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { action, runInAction } from 'mobx';
+import { extname } from 'path';
 import { Doc, Opt, StrListCast } from "../../fields/Doc";
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
-import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, ImageCast, NumCast, StrCast } from "../../fields/Types";
 import { DashColor, lightOrDark } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -88,7 +89,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
     const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + ((Math.abs(x * y) * 9301 + 49297) % 233280 / 233280) * (max - min);
     switch (property.split(":")[0]) {
-        case StyleProp.TreeViewIcon: return Doc.toIcon(doc, isOpen);
+        case StyleProp.TreeViewIcon:
+            const icon = Cast(doc?.layout_icon, Doc, null) ? Doc.expandTemplateLayout(Cast(doc?.layout_icon, Doc, null), doc) : undefined;
+            if (icon && ImageCast(doc?.icon, ImageCast(doc?.data))) {
+                const img = ImageCast(doc?.icon, ImageCast(doc?.data));
+                const ext = extname(img.url.href);
+                const url = doc?.icon ? img.url.href : img.url.href.replace(ext, "_s" + ext);
+                return <img src={url} width={20} height={15} style={{ margin: "auto", display: "block", objectFit: "contain" }} />;
+            }
+            return Doc.toIcon(doc, isOpen);
         case StyleProp.DocContents: return undefined;
         case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? "lightgrey" : "dimgrey";
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 2e33d3564..7c75810d3 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -47,7 +47,7 @@
         width: $TREE_BULLET_WIDTH;
         color: $medium-gray;
         margin-top: 3px;
-        transform: scale(1.3, 1.3);
+       // transform: scale(1.3, 1.3); // bcz: why was this here?  It makes displaying images in the treeView for documents that have icons harder.
         border: #80808030 1px solid;
         border-radius: 4px;
     }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ff5c4bab1..c2d0983da 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -31,6 +31,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -471,7 +472,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             onClick={this.bulletClick}
             style={this.props.treeView.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : {
                 color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
-                opacity: checked === "unchecked" ? undefined : 0.4
+                opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4
             }}>
             {this.props.treeView.outlineMode ?
                 !(this.doc.text as RichTextField)?.Text ? (null) :
@@ -484,7 +485,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                     checked === "unchecked" ? "square" :
                                         !this.treeViewOpen ? "caret-right" : "caret-down"} />
                     </div>
-                    {this.onCheckedClick ? (null) : <FontAwesomeIcon icon={iconType} />}
+                    {this.onCheckedClick ? (null) : typeof iconType === "string" ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
                 </div>
             }
         </div>;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b8f687489..5405ab500 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -519,13 +519,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
-        const portal = Doc.MakeAlias(summary);
-        Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected);
-        Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations");
-        summary._backgroundColor = "#e2ad32";
-        portal.layoutKey = "layout_portal";
-        portal.title = "document collection";
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const portal = Docs.Create.FreeformDocument(selected, { isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", "");
 
         this.props.addLiveTextDocument(summary);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 195f6c9e5..8bf9348c7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -594,8 +594,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch @action
     toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => {
         this.Document.ignoreClick = false;
-        this.Document._isLinkButton = !this.Document._isLinkButton;
-        setPushpin && (this.Document.isPushpin = this.Document._isLinkButton);
+        if (setPushpin) {
+            this.Document.isPushpin = !this.Document.isPushpin;
+            this.Document._isLinkButton = this.Document.isPushpin || this.Document._isLinkButton;
+        } else {
+            this.Document._isLinkButton = !this.Document._isLinkButton;
+        }
         if (this.Document._isLinkButton && !this.onClickHandler) {
             this.Document.followLinkZoom = zoom;
             this.Document.followLinkLocation = location;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d5f3f3b4e..eca2e3cfd 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -15,6 +15,7 @@ import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
 import './LinkDocPreview.scss';
 import React = require("react");
 import { DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
 
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
@@ -137,7 +138,13 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     @computed get previewHeader() {
         return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
             <div className="linkDocPreview-info" ref={this._infoRef}>
-                <div className="linkDocPreview-title">
+                <div className="linkDocPreview-title" style={{ pointerEvents: "all" }}
+                    onPointerDown={e => {
+                        DragManager.StartDocumentDrag([this._infoRef.current!],
+                            new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+                        e.stopPropagation();
+                        LinkDocPreview.Clear();
+                    }}>
                     {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}
                     <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From 85fca7ecd26236407e266d6aec051d1f5fb5feb9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Apr 2022 09:30:37 -0400
Subject: gave summary documents pushpin behaviors.  fixed following link to
 doc that is iconified to work.  fixed pushin visibility toggling for docs
 that are iconified.

---
 src/client/documents/Documents.ts                                   | 1 +
 src/client/views/StyleProvider.tsx                                  | 2 +-
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++---
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx     | 6 ++++--
 src/client/views/nodes/DocumentView.tsx                             | 5 ++++-
 5 files changed, 13 insertions(+), 7 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 465cdf3ec..69ea21541 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -117,6 +117,7 @@ export class DocumentOptions {
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _freeformLOD?: boolean; // whether to use LOD to render a freeform document
+    _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
     _showTitle?: string; // field name to display in header (:hover is an optional suffix)
     _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
     _scrollTop?: number; // scroll location for pdfs
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index dba290b46..ba5aaf36a 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -117,7 +117,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             const backColor = backgroundCol();
             if (!backColor) return undefined;
             return lightOrDark(backColor);
-        case StyleProp.Hidden: return BoolCast(doc?._hidden);
+        case StyleProp.Hidden: return BoolCast(doc?.hidden);
         case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], StrCast(doc?.borderRounding, doc?._viewType === CollectionViewType.Pile ? "50%" : ""));
         case StyleProp.TitleHeight: return 15;
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9b0acffdf..99a6508a9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1061,11 +1061,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             this.props.focus(doc, options);
         } else {
             const xfToCollection = options?.docTransform ?? Transform.Identity();
-            const layoutdoc = Doc.Layout(doc);
             const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: this.Document[this.scaleFieldKey] };
             const newState = HistoryUtil.getState();
             const cantTransform = this.props.isAnnotationOverlay || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
-            const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(layoutdoc, xfToCollection, options?.willZoom ? options?.scale || .75 : undefined);
+            const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || .75 : undefined);
             if (!cantTransform) {   // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
                 newState.initializers![this.Document[Id]] = { panX: panX, panY: panY };
                 HistoryUtil.pushState(newState);
@@ -1112,10 +1111,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
+        const layoutdoc = Doc.Layout(doc);
         const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1);
         const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1);
         const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
-        const pt2 = xf.transformPoint(NumCast(doc.x) + doc[WidthSym](), NumCast(doc.y) + doc[HeightSym]());
+        const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[WidthSym](), NumCast(doc.y) + layoutdoc[HeightSym]());
         const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1] };
         const cx = NumCast(this.layoutDoc._panX);
         const cy = NumCast(this.layoutDoc._panY);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5405ab500..2174aaf10 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -519,10 +519,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
-        const portal = Docs.Create.FreeformDocument(selected, { isGroup: true, backgroundColor: "transparent" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", "");
 
+        portal.hidden = true;
+        this.props.addDocument?.(portal);
         this.props.addLiveTextDocument(summary);
         MarqueeOptionsMenu.Instance.fadeOut(true);
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8bf9348c7..0e22b1b3f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -211,7 +211,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
     @computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
     @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
-    @computed get hidden() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden); }
+    @computed get hidden() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); }
     @computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
     @computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
     @computed get borderRounding() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); }
@@ -1081,6 +1081,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
         const isButton = this.props.Document.type === DocumentType.FONTICON;
+        if (this.props.Document.title === "myicon") {
+            console.log(this.hidden);
+        }
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
-- 
cgit v1.2.3-70-g09d2


From 74ef5f7a8c60dc68a1262077f7502d0535377b88 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Apr 2022 11:49:28 -0400
Subject: fixed following links to documents in iconified collections/text to
 deiconify.  fixed ctrl-x to cut (not iconify).  changed lightboxview to add
 to future docs when stepping into.  added placeholder for indicating how many
 documents are left in lightbox future

---
 src/client/util/DocumentManager.ts                        | 14 +++++++++++++-
 src/client/views/DocumentDecorations.tsx                  |  8 ++++----
 src/client/views/GlobalKeyHandler.ts                      |  2 +-
 src/client/views/LightboxView.tsx                         | 11 ++++-------
 src/client/views/nodes/DocumentView.tsx                   | 15 +++++++++------
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  4 ++--
 6 files changed, 33 insertions(+), 21 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb50ecb4a..607a3d6bf 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -197,7 +197,14 @@ export class DocumentManager {
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (!docView && annoContainerView) {
-            annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+            if (annoContainerView.props.Document.layoutKey === "layout_icon") {
+                annoContainerView.iconify(() => this.jumpToDocument(
+                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                    finished, originalTarget, noSelect, presZoom));
+                return;
+            } else {
+                annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+            }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
@@ -217,6 +224,11 @@ export class DocumentManager {
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
                         willZoom, afterFocus: async () => {
                             targetDocContext._viewTransition = undefined;
+                            if (targetDocContext.layoutKey === "layout_icon") {
+                                targetDocContextView.iconify(() => this.jumpToDocument(
+                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                                    finished, originalTarget, noSelect, presZoom));
+                            }
                             return ViewAdjustment.doNothing;
                         }
                     });
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a8d91e0a2..78a8a4c7e 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -145,11 +145,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
     }
 
     _iconifyTimeout: NodeJS.Timeout | undefined;
-    onCloseClick = () => {
+    onCloseClick = (force = false) => {
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
-            const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
-            const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
+            const icons = this._iconifyTimeout || force ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon");
+            const others = this._iconifyTimeout || force ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon");
             icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
             others.forEach(dv => dv.iconify());
             others.length && (this._iconifyTimeout = setTimeout(() => {
@@ -519,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(), "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 91e0503b3..e3031d2c5 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -275,7 +275,7 @@ export class KeyManager {
                     const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
                     const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
-                    DocumentDecorations.Instance.onCloseClick();
+                    DocumentDecorations.Instance.onCloseClick(true);
                     stopPropagation = false;
                     preventDefault = false;
                 }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index b39b5020c..59ed0dc92 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -63,7 +63,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             }
         }
         if (future) {
-            this._future = future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length);
+            this._future = [...(this._future ?? []), ...(this.LightboxDoc ? [this.LightboxDoc] : []), ...future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),];
         }
         this._doc = doc;
         this._layoutTemplate = layoutTemplate;
@@ -118,7 +118,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     addDocTab = LightboxView.AddDocTab;
     @action public static Next() {
         const doc = LightboxView._doc!;
-        const target = LightboxView._docTarget = LightboxView._future?.pop();
+        const target = LightboxView._docTarget = this._future?.pop();
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
@@ -163,11 +163,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc);
         if (docView) {
             LightboxView._docTarget = target;
-            const focusSpeed = 1000;
-            doc._viewTransition = `transform ${focusSpeed}ms`;
             if (!target) docView.ComponentView?.shrinkWrap?.();
             else docView.focus(target, { willZoom: true, scale: 0.9 });
-            setTimeout(() => doc._viewTransition = undefined, focusSpeed);
         }
         else {
             LightboxView.SetLightboxDoc(doc, target);
@@ -232,7 +229,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                             const doc = LightboxView._doc;
                             const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
                             if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
-                            else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true });
+                            //else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
                         }));
                     })}
                         Document={LightboxView.LightboxDoc}
@@ -272,7 +269,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => {
                         e.stopPropagation();
                         LightboxView.Next();
-                    })}
+                    }, this.future()?.length.toString())}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
                 <div className="lightboxView-tabBtn" title={"open in tab"}
                     onClick={e => {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 0e22b1b3f..4380cb0bc 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -452,7 +452,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
@@ -1243,15 +1243,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
     }
 
-    public iconify() {
+    public iconify(finished?: () => void) {
         this.ComponentView?.updateIcon?.();
         const layoutKey = Cast(this.Document.layoutKey, "string", null);
         if (layoutKey !== "layout_icon") {
-            this.switchViews(true, "icon");
+            this.switchViews(true, "icon", finished);
             if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", "");
         } else {
             const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
-            this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
+            this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
             this.Document.deiconifyLayout = undefined;
             this.props.bringToFront(this.rootDoc);
         }
@@ -1262,12 +1262,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         Doc.setNativeView(this.props.Document);
         custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
     }
-    switchViews = action((custom: boolean, view: string) => {
+    switchViews = action((custom: boolean, view: string, finished?: () => void) => {
         this.docView && (this.docView._animateScalingTo = 0.1);  // shrink doc
         setTimeout(action(() => {
             this.setCustomView(custom, view);
             this.docView && (this.docView._animateScalingTo = 1); // expand it
-            setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), this.docView!._animateScaleTime - 10);
+            setTimeout(action(() => {
+                this.docView && (this.docView._animateScalingTo = 0);
+                finished?.();
+            }), this.docView!._animateScaleTime - 10);
         }), this.docView!._animateScaleTime - 10);
     });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b6713d878..ac291801b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -758,7 +758,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             if (sel.from !== sel.to) {
-                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true });
+                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
                 const href = targetHref ?? Doc.localServerPath(anchor);
                 if (anchor !== anchorDoc) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
@@ -859,7 +859,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
                 autoHeight && this.props.setHeight?.(marginsHeight + Math.max(sidebarHeight, textHeight));
             }, { fireImmediately: true });
-        this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
+        this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
             newLinks => {
                 this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
                 this._cachedLinks = newLinks;
-- 
cgit v1.2.3-70-g09d2


From 0664b26916f656e2b898af4edab13c7b6d5a1dcc Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 21 Apr 2022 15:00:31 -0400
Subject: fixed issues with pile view - dragging, burst size, rendering within
 grid/non-freeform views,  fixed timeView filtering to not keep making
 redundant filters,

---
 src/client/views/collections/CollectionPileView.tsx          |  8 ++------
 src/client/views/collections/CollectionTimeView.tsx          |  9 +++++++++
 .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx   |  7 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx            |  8 +++++---
 .../views/collections/collectionGrid/CollectionGridView.scss |  4 ++++
 .../views/collections/collectionGrid/CollectionGridView.tsx  |  5 +++--
 src/client/views/nodes/DocumentView.tsx                      |  3 ---
 src/client/views/nodes/ImageBox.tsx                          | 12 +-----------
 8 files changed, 28 insertions(+), 28 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 3d5552f81..8832b0f4a 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
@@ -55,12 +55,8 @@ export class CollectionPileView extends CollectionSubView() {
     // returns the contents of the pileup in a CollectionFreeFormView
     @computed get contents() {
         const isStarburst = this.layoutEngine() === "starburst";
-        const draggingSelf = this.props.isSelected();
         return <div className="collectionPileView-innards"
-            style={{
-                pointerEvents: isStarburst || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none",
-                zIndex: isStarburst && !SnappingManager.GetIsDragging() ? -10 : "auto"
-            }} >
+            style={{ pointerEvents: isStarburst || SnappingManager.GetIsDragging() ? undefined : "none" }} >
             <CollectionFreeFormView {...this.props}
                 layoutEngine={this.layoutEngine}
                 childDocumentsActive={isStarburst ? returnTrue : undefined}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index d6398fda5..e63ea7151 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -235,6 +235,7 @@ export class CollectionTimeView extends CollectionSubView() {
 
 ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
     let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
+    const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField));
     pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
     pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
     pivotDoc["_prevPivotFields" + prevFilterIndex] = pivotDoc._pivotField;
@@ -249,5 +250,13 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
                 pivotDoc._pivotField = filterVals[0];
             }
         }
+        const newFilters = StrListCast(pivotDoc._docFilters);
+        if (newFilters.length && originalFilter.length &&
+            newFilters[newFilters.length - 1] === originalFilter[originalFilter.length - 1]) {
+            pivotDoc._prevFilterIndex = --prevFilterIndex;
+            pivotDoc["_prevDocFilter" + prevFilterIndex] = undefined;
+            pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = undefined;
+            pivotDoc["_prevPivotFields" + prevFilterIndex] = undefined;
+        }
     });
 });
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index f9345e33f..598960af7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -111,12 +111,13 @@ export function computerStarburstLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
     engineProps: any
 ) {
+    const mustFit = pivotDoc[WidthSym]() !== panelDim[0];
     const docMap = new Map<string, PoolData>();
-    const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
-    const docSize = 75; // assume a icon sized at 100
+    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
+    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume a icon sized at 75
     const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? 75 : 400; // assume a icon sized at 100
+        const docSize = layout.layoutKey === "layout_icon" ? mustFit ? panelDim[0] * .33 : 75 : 400; // assume a icon sized at 10750
         const deg = i / childPairs.length * Math.PI * 2;
         docMap.set(layout[Id], {
             x: Math.cos(deg) * (burstRadius[0] / 3) - docSize / 2,
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 99a6508a9..eaef1b49c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -454,9 +454,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         e.preventDefault();
                         break;
                     case InkTool.None:
-                        this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
-                        document.addEventListener("pointermove", this.onPointerMove);
-                        document.addEventListener("pointerup", this.onPointerUp);
+                        if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
+                            this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
+                            document.addEventListener("pointermove", this.onPointerMove);
+                            document.addEventListener("pointerup", this.onPointerUp);
+                        }
                         break;
                 }
             }
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index a6171af51..845b07c51 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -13,6 +13,10 @@
         display: flex;
         flex-direction: row;
 
+        .document-wrapper:hover {
+            z-index: 2000;
+        }
+
         .react-grid-layout {
             width: 100%;
         }
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 58ea7410d..da102fe18 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -166,7 +166,7 @@ export class CollectionGridView extends CollectionSubView() {
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
-            dontCenter={"y"}
+            dontCenter={this.props.Document.centerY ? "" : "y"}
         />;
     }
 
@@ -283,6 +283,7 @@ export class CollectionGridView extends CollectionSubView() {
     onContextMenu = () => {
         const displayOptionsMenu: ContextMenuProps[] = [];
         displayOptionsMenu.push({ description: "Toggle Content Display Style", event: () => this.props.Document.display = this.props.Document.display ? undefined : "contents", icon: "copy" });
+        displayOptionsMenu.push({ description: "Toggle Vertical Centering", event: () => this.props.Document.centerY = !this.props.Document.centerY, icon: "copy" });
         ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" });
     }
 
@@ -293,7 +294,7 @@ export class CollectionGridView extends CollectionSubView() {
         if (this.props.isContentActive(true)) {
             setupMoveUpEvents(this, e, returnFalse, returnFalse,
                 (e: PointerEvent, doubleTap?: boolean) => {
-                    if (doubleTap) {
+                    if (doubleTap && !e.button) {
                         undoBatch(action(() => {
                             const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
                             FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4380cb0bc..1eaff3c1c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1081,9 +1081,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
         const isButton = this.props.Document.type === DocumentType.FONTICON;
-        if (this.props.Document.title === "myicon") {
-            console.log(this.hidden);
-        }
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index fb9e86487..a1de944d8 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -300,17 +300,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         </div>;
     }
 
-    // adjust y position to center image in panel aspect is bigger than image aspect.
-    // bcz :note, this is broken for rotated images
-    get ycenter() {
-        const { nativeWidth, nativeHeight } = this.nativeSize;
-        const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
-        const aspect = (rotation % 180) ? nativeWidth / nativeHeight : nativeHeight / nativeWidth;
-        return this.props.PanelHeight() / this.props.PanelWidth() > aspect ?
-            (this.props.PanelHeight() - this.props.PanelWidth() * aspect) / 2 : 0;
-    }
-
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter);
+    screenToLocalTransform = this.props.ScreenToLocalTransform;
     contentFunc = () => [this.content];
 
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-- 
cgit v1.2.3-70-g09d2


From 18d21c0616adbdc7e0d0619c3c2d8bdf9b34cc01 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 22 Apr 2022 21:39:08 -0400
Subject: changes to allow PresBox to render using a Tree view

---
 src/client/documents/Documents.ts                 |  7 +--
 src/client/views/collections/TabDocView.tsx       |  9 ++++
 src/client/views/collections/TreeView.tsx         | 54 ++++++++++++-----------
 src/client/views/nodes/DocumentView.tsx           |  3 +-
 src/client/views/nodes/trails/PresBox.tsx         | 13 +++---
 src/client/views/nodes/trails/PresElementBox.scss |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx  |  4 +-
 7 files changed, 54 insertions(+), 38 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 69ea21541..49e53a214 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -284,6 +284,7 @@ export class DocumentOptions {
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
+    treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
 
     // Action Button
     buttonMenu?: boolean; // whether a action button should be displayed 
@@ -365,7 +366,7 @@ export namespace Docs {
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
                 options: {
-                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true,
+                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
                     links: "@links(self)"
                 }
             }],
@@ -460,8 +461,8 @@ export namespace Docs {
                 options: { links: "@links(self)" }
             }],
             [DocumentType.SLIDER, {
-                layout: { view: SliderBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
+                layout: { view: SliderBox, dataField: defaultDataKey, },
+                options: { links: "@links(self)", treeViewGrowsHorizontally: true }
             }],
             [DocumentType.PRES, {
                 layout: { view: PresBox, dataField: defaultDataKey },
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index bd3e810c9..136486f47 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -33,6 +33,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
+import { List } from '../../../fields/List';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -209,9 +210,17 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
+            pinDoc.data = new List<Doc>();  // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = PresMovement.Zoom;
             pinDoc.groupWithUp = false;
             pinDoc.context = curPres;
+            // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
+            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+            pinDoc.treeViewGrowsHorizontally = true;// the document can expand horizontally when displayed as a tree view header  
+            pinDoc.treeViewHideHeader = true;  // this will force the document to render itself as the tree view header
             const presArray: Doc[] = PresBox.Instance?.sortArray();
             const size: number = PresBox.Instance?._selectedArray.size;
             const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index c2d0983da..a35304787 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -112,7 +112,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); }
+    @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
     @computed get childDocs() { return this.childDocList(this.fieldKey); }
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAliases() { return this.childDocList("aliases"); }
@@ -156,18 +156,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             docView.select(false);
         }
     }
-    @action
-    openLevel = (docView: DocumentView) => {
-        if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
-            this.treeViewOpen = !this.treeViewOpen;
-        } else {
-            // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            // this.props.addDocTab(CurrentUserUtils.ActiveDashboard.isShared ? Doc.MakeAlias(this.props.document) : this.props.document, "add:right");
-            const bestAlias = DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? Doc.MakeAlias(this.props.document), "add:right");
 
-        }
-    }
     constructor(props: any) {
         super(props);
         if (!TreeView._openLevelScript) {
@@ -379,8 +368,14 @@ export class TreeView extends React.Component<TreeViewProps> {
         return rows;
     }
 
-    rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
-    rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    rtfWidth = () => {
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        return Math.min(layout[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
+    }
+    rtfHeight = () => {
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    }
     rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
     expandPanelHeight = () => {
         if (this.layoutDoc._fitWidth) return this.docHeight();
@@ -580,13 +575,20 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     onKeyDown = (e: React.KeyboardEvent) => {
         if (this.doc.treeViewHideHeader || this.props.treeView.outlineMode) {
-            e.stopPropagation();
-            e.preventDefault();
             switch (e.key) {
-                case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+                case "Tab":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
                     return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
-                case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
-                case "Enter": return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+                case "Backspace":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
+                case "Enter":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
             }
         }
     }
@@ -691,7 +693,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
         return <>
             <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
-                style={{ width: "max-content" }}
+                style={{ width: StrCast(this.doc.treeViewHeaderWidth, "max-content") }}
                 ref={this._header}
                 onClick={this.ignoreEvent}
                 onPointerDown={this.ignoreEvent}
@@ -705,8 +707,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
 
     renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
-        const layout = StrCast(Doc.LayoutField(this.layoutDoc));
-        const isExpandable = layout.includes(FormattedTextBox.name) || layout.includes(SliderBox.name);
+        const isExpandable = this.doc._treeViewGrowsHorizontally;
         const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
         const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
         return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)}
@@ -717,6 +718,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
             NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
             LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
+            LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
             isContentActive={isActive}
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
@@ -759,10 +761,10 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     // renders the document in the header field instead of a text proxy.
-    @computed get renderDocumentAsHeader() {
+    renderDocumentAsHeader = (asText: boolean) => {
         return <>
             {this.renderBullet}
-            {this.renderEmbeddedDocument(true, this.props.isContentActive)}
+            {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
         </>;
     }
 
@@ -794,9 +796,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
                 onKeyDown={this.onKeyDown}>
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF ?
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
                         this.renderEmbeddedDocument(false, returnFalse) :
-                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader, this._editTitle)}
+                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
             </div>;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1eaff3c1c..bcf00e88d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1308,6 +1308,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         TraceMobx();
         const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+        const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
         const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
         return (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth() ? (null) : (
@@ -1316,7 +1317,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         transition: this.props.dataTransition,
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
+                        width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
                         height: (!this.props.ignoreAutoHeight && this.layoutDoc.autoHeight && this.layoutDoc.type === DocumentType.RTF) || isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 2e312ee51..c9c74eca4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -122,7 +122,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
         if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", type: DocumentType.PRESELEMENT, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+                title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
             }));
             // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
             // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
@@ -308,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (!group) this._selectedArray.clear();
             this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
-            if (this.layoutDoc._viewType === "stacking" && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
+            if ([CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.layoutDoc._viewType as any) && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
             this.onHideDocument(); //Handles hide after/before
         }
     });
@@ -620,9 +620,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
-        viewType === CollectionViewType.Stacking && (this.rootDoc._pivotField = undefined);
+        [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
-        if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 0;
+        if ([CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType)) this.layoutDoc._gridGap = 0;
     });
 
     /**
@@ -696,7 +696,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
+    childLayoutTemplate = () => ![CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.rootDoc._viewType as any) ? undefined : this.presElement;
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
@@ -2262,6 +2262,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     onChange={this.viewChanged}
                     value={mode}>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
+                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
@@ -2459,7 +2460,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 }
 ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
     if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
-    if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31;
+    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(container._viewType as any) ? 35 : 31;
     if (field === 'presStatus') return container.presStatus;
     if (field === '_itemIndex') return container._itemIndex;
     if (field === 'presBox') return container;
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 1ad4b820e..a178be910 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -42,7 +42,7 @@ $slide-active: #5B9FDD;
     background-color: #d5dce2;
     border-radius: 5px;
     height: calc(100% - 7px);
-    width: calc(100% - 15px);
+    width: 100%;
     display: grid;
     grid-template-rows: 16px 10px auto;
     grid-template-columns: max-content max-content max-content max-content auto;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index a4ec559f5..5c16d743a 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -23,6 +23,7 @@ import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
 import { PresMovement } from "./PresEnums";
 import React = require("react");
+import { CollectionViewType } from "../../collections/CollectionView";
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -87,6 +88,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
+                    fitContentsToDoc={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
@@ -321,7 +323,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
                         boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
                     }}>
-                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 195 : 220)) : toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 105 : 145), cursor: isSelected ? 'text' : 'grab' }}>
                         <EditableView
                             ref={this._titleRef}
                             editing={!isSelected ? false : undefined}
-- 
cgit v1.2.3-70-g09d2


From 239ff2c0423333bb7e154158c1f52ef688875ee7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 24 Apr 2022 14:50:21 -0400
Subject: fixed treeViews to vertically span their containers.  fixed
 foreground coloring of nodes with transparent backgrounds to consider parent
 collection background color

---
 src/client/views/StyleProvider.tsx                 |  5 ++--
 src/client/views/collections/CollectionSubView.tsx | 34 +++++++++++-----------
 .../views/collections/CollectionTreeView.scss      |  6 ++++
 .../views/collections/CollectionTreeView.tsx       |  3 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  6 ++++
 .../views/nodes/formattedText/FormattedTextBox.tsx | 16 +++++-----
 7 files changed, 41 insertions(+), 31 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index ba5aaf36a..5c10c2344 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -81,7 +81,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isAnchor = property.includes(":anchor");
     const isAnnotated = property.includes(":annotated");
     const isOpen = property.includes(":open");
-    const fieldKey = (props as any)?.fieldKey ? (props as any).fieldKey + "-" : isCaption ? "caption-" : "";
+    const fieldKey = props?.fieldKey ? props.fieldKey + "-" : isCaption ? "caption-" : "";
     const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic";
     const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
@@ -114,7 +114,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
             if (docColor) return docColor;
-            const backColor = backgroundCol();
+            const docView = props?.DocumentView?.();
+            const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, "backgroundColor");
             if (!backColor) return undefined;
             return lightOrDark(backColor);
         case StyleProp.Hidden: return BoolCast(doc?.hidden);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d8f1287cd..0eb94c394 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,23 +1,32 @@
-import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
+import { action, computed, observable } from "mobx";
+import ReactLoading from 'react-loading';
+import * as rp from 'request-promise';
 import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc";
-import { Id, ToString } from "../../../fields/FieldSymbols";
+import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc";
+import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
 import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
-import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
+import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
+import { returnFalse, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
+import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { Networking } from "../../Network";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DragManager, dropActionType } from "../../util/DragManager";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { InteractionUtils } from "../../util/InteractionUtils";
+import { SelectionManager } from "../../util/SelectionManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { OverlayView } from "../OverlayView";
+import { CollectionView, CollectionViewProps, CollectionViewType } from "./CollectionView";
 import React = require("react");
-import ReactLoading from 'react-loading';
-import * as rp from 'request-promise';
-import { Networking } from "../../Network";
 
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -472,13 +481,4 @@ export function CollectionSubView<X>(moreProps?: X) {
     return CollectionSubView;
 }
 
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
-import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { OverlayView } from "../OverlayView";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
 
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index b664d9d82..a8eee9c19 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -2,6 +2,7 @@
 
 .collectionTreeView-container {
     transform-origin: top left;
+    height: 100%;
 }
 .collectionTreeView-dropTarget {
     border-width: $COLLECTION_BORDER_WIDTH;
@@ -71,6 +72,11 @@
     display: none;
 }
 
+.collectionTreeView-contents {
+    display: flex;
+    flex-direction: column;
+}
+
 .collectionTreeView-titleBar {
     display: inline-block;
     width: 100%;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e84517f40..76da96174 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -341,14 +341,13 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             <div className="collectionTreeView-contents" key="tree" style={{
                 ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
                 overflow: "auto",
-                height: this.layoutDoc._autoHeight ? "max-content" : "100%"
+                height: "100%"//this.layoutDoc._autoHeight ? "max-content" : "100%"
             }} >
                 {titleBar}
                 <div className="collectionTreeView-container"
                     style={{
                         transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
                         paddingLeft: `${this.marginX()}px`,
-                        height: "max-content",
                         width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
                     }}
                     onContextMenu={this.onContextMenu}>
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 342424d41..61c7ff78e 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -225,7 +225,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             title: "-title-",
             treeViewExpandedViewLock: true, treeViewExpandedView: "data",
             _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline",
-            x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, backgroundColor: "transparent", _width: 1000, _height: 10
+            x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bcf00e88d..504a3afb6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -104,7 +104,10 @@ export interface DocComponentView {
     snapPt?: (pt: { X: number, Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number, Y: number }, distance: number };
     search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
 }
+// These props are passed to both FieldViews and DocumentViews 
 export interface DocumentViewSharedProps {
+    fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps.  In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews
+    DocumentView?: () => DocumentView;
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
@@ -149,6 +152,8 @@ export interface DocumentViewSharedProps {
     createNewFilterDoc?: () => void;
     updateFilterDoc?: (doc: Doc) => void;
 }
+
+// these props are specific to DocuentViews
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
     freezeDimensions?: boolean;
@@ -173,6 +178,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     onPointerUp?: () => ScriptField;
 }
 
+// these props are only available in DocumentViewIntenral
 export interface DocumentViewInternalProps extends DocumentViewProps {
     NativeWidth: () => number;
     NativeHeight: () => number;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ac291801b..dc1c67bea 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,23 +1,23 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { isEqual } from "lodash";
-import { action, computed, IReactionDisposer, reaction, runInAction, observable, trace } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { baseKeymap, selectAll } from "prosemirror-commands";
 import { history } from "prosemirror-history";
 import { inputRules } from 'prosemirror-inputrules';
 import { keymap } from "prosemirror-keymap";
 import { Fragment, Mark, Node, Slice } from "prosemirror-model";
-import { ReplaceStep } from 'prosemirror-transform';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym, AclAugment } from "../../../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from "../../../../fields/Doc";
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { Cast, DateCast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
@@ -29,6 +29,7 @@ import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from "../../../util/DragManager";
 import { makeTemplate } from '../../../util/DropConverter';
+import { LinkManager } from '../../../util/LinkManager';
 import { SelectionManager } from "../../../util/SelectionManager";
 import { SnappingManager } from '../../../util/SnappingManager';
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
@@ -38,10 +39,11 @@ import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { ViewBoxAnnotatableComponent } from "../../DocComponent";
 import { DocumentButtonBar } from '../../DocumentButtonBar';
+import { Colors } from '../../global/globalEnums';
 import { LightboxView } from '../../LightboxView';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
+import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
-import { AudioBox } from '../AudioBox';
 import { FieldView, FieldViewProps } from "../FieldView";
 import { LinkDocPreview } from '../LinkDocPreview';
 import { DashDocCommentView } from "./DashDocCommentView";
@@ -60,10 +62,6 @@ import { schema } from "./schema_rts";
 import { SummaryView } from "./SummaryView";
 import applyDevTools = require("prosemirror-dev-tools");
 import React = require("react");
-import { SidebarAnnos } from '../../SidebarAnnos';
-import { Colors } from '../../global/globalEnums';
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { LinkManager } from '../../../util/LinkManager';
 const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
-- 
cgit v1.2.3-70-g09d2


From cb59842fcb79af7ef72443e08c6a479c3f0af343 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 25 Apr 2022 20:20:48 -0400
Subject:  changed freeformview's with engines to not allow children to be
 edited using setHeight prop since engine layouts are temporary and shouldn't
 edit the doc.  fixed tags to work with pivot view. cleaned up pile views to
 be less hacky..

---
 src/client/documents/Documents.ts                  | 27 +++++++++++++---------
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/SidebarAnnos.tsx                  |  2 +-
 .../views/collections/CollectionPileView.tsx       |  4 ++--
 .../views/collections/CollectionStackingView.tsx   |  6 ++---
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/TreeView.tsx          |  1 +
 .../CollectionFreeFormLayoutEngines.tsx            | 26 +++++++++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  5 +---
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 17 +++++++-------
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  1 +
 src/client/views/nodes/WebBox.tsx                  |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 19 files changed, 62 insertions(+), 48 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 49e53a214..a19530c92 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -14,7 +14,7 @@ import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Upload } from "../../server/SharedMediaTypes";
-import { OmitKeys, Utils } from "../../Utils";
+import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
 import { YoutubeBox } from "../apis/youtube/YoutubeBox";
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
@@ -1352,26 +1352,31 @@ export namespace DocUtils {
         if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
     }
 
-    export function pileup(docList: Doc[], x?: number, y?: number, create: boolean = true) {
+    export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
         let w = 0, h = 0;
         runInAction(() => {
             docList.forEach(d => {
                 DocUtils.iconify(d);
-                w = Math.max(d[WidthSym](), w);
-                h = Math.max(d[HeightSym](), h);
+                w = Math.max(NumCast(d._width), w);
+                h = Math.max(NumCast(d._height), h);
             });
-            h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away.  so this is a fallback hack to try to do something reasonable
             docList.forEach((d, i) => {
-                d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2;
-                d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2;
+                d.x = Math.cos(Math.PI * 2 * i / docList.length) * size;
+                d.y = Math.sin(Math.PI * 2 * i / docList.length) * size;
+                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+            });
+            const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0);
+            docList.forEach((d, i) => {
+                d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2);
+                d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2);
                 d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - 55, y: (y || 0) - 55, _width: 110, _height: 100, });
-            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
-            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
-            newCollection._width = newCollection._height = 110;
+            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, });
+            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
+            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
+            newCollection._width = newCollection._height = size * 2;
             newCollection._jitterRotation = 10;
             return newCollection;
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a67cb3014..68b4710ed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -297,6 +297,7 @@ export class MainView extends React.Component {
             searchFilterDocs={returnEmptyDoclist}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
+            suppressSetHeight={true}
             renderDepth={-1}
         />;
     }
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index fae385660..43a02d029 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -88,7 +88,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
     showTitle = () => "title";
-    setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
+    setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 8832b0f4a..4489601db 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -74,13 +74,13 @@ export class CollectionPileView extends CollectionSubView() {
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
             this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
-            DocUtils.pileup(this.childDocs, undefined, undefined, false);
+            DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
             this.layoutDoc._panX = 0;
             this.layoutDoc._panY = -10;
             this.props.Document._pileLayoutEngine = 'pass';
         } else {
             const defaultSize = 25;
-            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
+            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 250);
             !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
             if (this.layoutEngine() === 'pass') {
                 this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8634ea139..509005b45 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -141,7 +141,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             () => this.layoutDoc._columnHeaders = new List()
         );
         this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                 this.headerMargin + (this.isStackingView ?
                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
                     this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
@@ -408,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                                 Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                             if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
-                                this.props.setHeight(height);
+                                this.props.setHeight?.(height);
                             }
                         }
                     }));
@@ -458,7 +458,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            this.props.setHeight(this.headerMargin + height);
+                            this.props.setHeight?.(this.headerMargin + height);
                         }
                     }));
                     this.observer.observe(ref);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5bdd0c0ac..30e0adf24 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -453,7 +453,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!,);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 76da96174..41970eb96 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -88,7 +88,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
             const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
-            this.props.setHeight(bodyHeight + titleHeight);
+            this.props.setHeight?.(bodyHeight + titleHeight);
         }
     }
     unobserveHeight = (ref: any) => {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6213850d8..b9710b3f5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -30,6 +30,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { KeyValueBox } from '../nodes/KeyValueBox';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 598960af7..16c7df311 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -111,17 +111,17 @@ export function computerStarburstLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
     engineProps: any
 ) {
-    const mustFit = pivotDoc[WidthSym]() !== panelDim[0];
+    const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
-    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume a icon sized at 75
-    const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
+    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
+    const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? mustFit ? panelDim[0] * .33 : 75 : 400; // assume a icon sized at 10750
+        const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
         const deg = i / childPairs.length * Math.PI * 2;
         docMap.set(layout[Id], {
-            x: Math.cos(deg) * (burstRadius[0] / 3) - docSize / 2,
-            y: Math.sin(deg) * (burstRadius[1] / 3) - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
+            x: Math.cos(deg) * burstRadius[0] - docSize / 2,
+            y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
             width: docSize,//layout[WidthSym](),
             height: docSize * layout[HeightSym]() / layout[WidthSym](),
             zIndex: NumCast(layout.zIndex),
@@ -129,7 +129,7 @@ export function computerStarburstLayout(
             replica: ""
         });
     });
-    const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+    const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
     return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
@@ -267,7 +267,13 @@ export function computePivotLayout(
     });
 
     const dividers = sortedPivotKeys.map((key, i) =>
-        ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
+    ({
+        type: "div", color: "lightGray",
+        x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
+        y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+        height: maxColHeight,
+        payload: pivotColumnGroups.get(key)!.filters
+    }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
@@ -404,7 +410,7 @@ function normalizeResults(
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
     const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
-    aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
+    aggBounds.r = aggBounds.x + 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;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index eaef1b49c..a14405a0b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1158,6 +1158,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             replica={entry.replica}
             renderIndex={renderIndex}
             dataTransition={entry.transition}
+            suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
             ContainingCollectionDoc={this.props.Document}
@@ -1300,10 +1301,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return [] as ViewDefResult[];
     }
 
+    @computed get layoutEngine() { return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); }
     @computed get doInternalLayoutComputation() {
         TraceMobx();
         const newPool = new Map<string, PoolData>();
-        switch (this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine)) {
+        switch (this.layoutEngine) {
             case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
             case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
             case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3e35039e3..6097425e1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,9 +17,6 @@ import { StyleProp } from "../StyleProvider";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { DocumentType } from "../../documents/DocumentTypes";
-import { collectionSchema } from "../../../fields/documentSchemas";
-import { CollectionViewType } from "../collections/CollectionView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -174,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
                 width: this.panelWidth(),
-                height: !this.ShowTitle && this.layoutDoc.autoHeight && this.rootDoc.type === DocumentType.RTF ? undefined : this.panelHeight(),
+                height: this.panelHeight(),
                 transform: this.transform,
                 transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 005133eb0..d4e8ffc7f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -112,7 +112,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
     isSelected: (outsideReaction: boolean) => boolean,
     select: (ctrl: boolean) => void,
     scaling?: () => number,
-    setHeight: (height: number) => void,
+    setHeight?: (height: number) => void,
     layoutKey: string,
 }> {
     @computed get layout(): string {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 504a3afb6..20eabe6a9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,6 +114,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     isHovering?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -842,11 +843,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    setHeight = (height: number) => {
-        if (this.props.renderDepth !== -1) {
-            this.layoutDoc._height = height;
-        }
-    }
+    setHeight = (height: number) => this.layoutDoc._height = height;
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
@@ -890,7 +887,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 setContentView={this.setContentView}
                 scaling={this.contentScaling}
                 PanelHeight={this.panelHeight}
-                setHeight={this.setHeight}
+                setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                 isContentActive={this.isContentActive}
                 ScreenToLocalTransform={this.screenToLocal}
                 rootSelected={this.rootSelected}
@@ -1207,7 +1204,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
             returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
     }
-    @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any); }
+    @computed get shouldNotScale() {
+        return (this.fitWidth && !this.nativeWidth) ||
+            this.props.ContainingCollectionView?.collectionViewType === CollectionViewType.Time ||
+            this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+    }
     @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
     @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
@@ -1324,7 +1325,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
                         width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: (!this.props.ignoreAutoHeight && this.layoutDoc.autoHeight && this.layoutDoc.type === DocumentType.RTF) || isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 943b9f153..ae9acfe3f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -22,7 +22,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
-    setHeight: (height: number) => void;
+    setHeight?: (height: number) => void;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: string;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index eca2e3cfd..55f6d6059 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -195,6 +195,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             ContainingCollectionDoc={undefined}
                             ContainingCollectionView={undefined}
                             renderDepth={-1}
+                            suppressSetHeight={true}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
                             focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0fd193977..eeb7b925b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             autoHeight => {
                 if (autoHeight) {
                     this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index dc1c67bea..710270be4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1524,7 +1524,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (children) {
             const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-            if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+            if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                 const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
                 if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9aaa6e90f..92a69c02b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -123,7 +123,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             autoHeight => {
                 if (autoHeight) {
                     this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 15df673f3..63af8401c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1108,7 +1108,7 @@ export namespace Doc {
         if (typeof value === "string") {
             value = value.replace(`,${Utils.noRecursionHack}`, "");
         }
-        const fieldVal = doc[key];
+        const fieldVal = key === "#" ? (StrCast(doc.tags).includes(":#" + value + ":") ? StrCast(doc.tags) : undefined) : doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
-- 
cgit v1.2.3-70-g09d2


From 08217336445cf2b3cd3efbe97e3c83525f02bf1b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 22:34:21 -0400
Subject: added image cropping. made treeView icons show up for file system and
 dashboard, and made them persist invisibly to fix triggering on hover. 
 changed tree view filesystem and dashboard to show expanded icons on hover.

---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/MarqueeAnnotator.tsx              | 27 ++++++++++++-
 src/client/views/PropertiesView.tsx                |  4 +-
 src/client/views/StyleProvider.scss                |  2 +-
 src/client/views/collections/TreeView.scss         | 23 +++++++++--
 src/client/views/collections/TreeView.tsx          | 16 ++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 +++++----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/ImageBox.tsx                | 45 ++++++++++++++++++++++
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 src/client/views/pdf/AnchorMenu.tsx                | 16 +++++++-
 11 files changed, 132 insertions(+), 27 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 68b4710ed..b73074899 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -362,7 +362,7 @@ export class MainView extends React.Component {
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
                         layerProvider={undefined}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 563261dec..a6b012bd6 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -5,7 +5,7 @@ import { Id } from "../../fields/FieldSymbols";
 import { List } from "../../fields/List";
 import { NumCast } from "../../fields/Types";
 import { GetEffectiveAcl } from "../../fields/util";
-import { Utils } from "../../Utils";
+import { unimplementedFunction, Utils } from "../../Utils";
 import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DragManager } from "../util/DragManager";
@@ -33,6 +33,7 @@ export interface MarqueeAnnotatorProps {
     getPageFromScroll?: (top: number) => number;
     finishMarquee: (x?: number, y?: number, PointerEvent?: PointerEvent) => void;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
+    anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
 }
 @observer
 export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
@@ -63,6 +64,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         doc.addEventListener("pointermove", this.onSelectMove);
         doc.addEventListener("pointerup", this.onSelectEnd);
 
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight("rgba(173, 216, 230, 0.75)", true), true);
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
         AnchorMenu.Instance.Highlight = this.highlight;
         AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations);
@@ -93,6 +95,29 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 }
             });
         });
+        /**
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * It also initiates a Drag/Drop interaction to place the text annotation.
+         */
+        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop ? unimplementedFunction : action((e: PointerEvent, ele: HTMLElement) => {
+            e.preventDefault();
+            e.stopPropagation();
+            var cropRegion: Doc | undefined;
+            const sourceAnchorCreator = () => {
+                cropRegion = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
+                cropRegion && this.props.addDocument(cropRegion);
+                return cropRegion;
+            };
+            const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+                dragComplete: e => {
+                    if (!e.aborted && e.linkDocument) {
+                        Doc.GetProto(e.linkDocument).linkRelationship = "cropped image";
+                        Doc.GetProto(e.linkDocument).title = "crop: " + this.props.docView.rootDoc.title;
+                    }
+                }
+            });
+        });
     }
     componentWillUnmount() {
         const doc = (this.props.iframe?.()?.contentDocument ?? document);
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 91cf83619..b63395c76 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1360,12 +1360,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
                     {this.optionsSubMenu}
 
+                    {this.fieldsSubMenu}
+
                     {this.sharingSubMenu}
 
                     {isNovice ? null : this.filtersSubMenu}
 
-                    {isNovice ? null : this.fieldsSubMenu}
-
                     {isNovice ? null : this.layoutSubMenu}
                 </div>;
             }
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index f26ed1f2d..8929954c8 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -25,5 +25,5 @@
 }
 
 .styleProvider-treeView-icon {
-    display: none;
+    opacity: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 7c75810d3..4707ebb80 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -21,7 +21,9 @@
     }
 
     .treeView-bulletIcons {
-        width: $TREE_BULLET_WIDTH;
+       // width: $TREE_BULLET_WIDTH; 
+        width: 100%;
+        height: 100%;
 
         .treeView-expandIcon {
             display: none;
@@ -37,10 +39,17 @@
 
         &:hover {
             .treeView-expandIcon {
-                display: unset;
+                display: unset; 
             }
         }
     }
+    .treeView-bulletIcons:hover img {
+        left: 14px;
+        position: absolute;
+        transform-origin: center left;
+        transform: scale(6);
+        pointer-events:none;
+    }
 
     .bullet {
         position: relative;
@@ -60,6 +69,10 @@
     pointer-events: all;
 }
 
+.bullet:hover {
+    z-index: 100;
+}
+
 .treeView-openRight {
     display: none;
     height: 17px;
@@ -123,7 +136,9 @@
         }
 
         >svg {
-            display: none;
+            //display: none;
+            opacity: 0;
+            pointer-events: none;
         }
     }
 }
@@ -150,6 +165,8 @@
         >svg,
         .styleProvider-treeView-icon {
             display: inherit;
+            opacity: unset;
+            pointer-events: unset;
         }
     }
 }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index b9710b3f5..9108acfcf 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -101,8 +101,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
     get defaultExpandedView() {
         return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
-            this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "layout") :
-                this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+            this.props.treeView.dashboardMode ? this.fieldKey :
+                this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") :
+                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
     }
 
     @computed get doc() { return this.props.document; }
@@ -491,15 +492,12 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get validExpandViewTypes() {
-        if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) {
-            return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"];
-        }
-        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : "";
-        const links = () => DocListCast(this.doc.links).length ? "links" : "";
-        const data = () => this.childDocs ? this.fieldKey : "";
+        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : "";
+        const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
+        const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
         const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const layout = (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bdae5a4f8..f0b3d70a0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -936,8 +936,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
-        if (deltaScale * invTransform.Scale < 1 && this.isAnnotationOverlay) {
-            deltaScale = 1 / invTransform.Scale;
+        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+            deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
         }
 
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
@@ -989,8 +989,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
-            const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
-            const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY));
+            const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
+            const minPanX = NumCast(this.rootDoc._panXMin, 0);
+            const minPanY = NumCast(this.rootDoc._panYMin, 0);
+            const newPanX = Math.min(
+                minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+            const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) :
+                minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight)), Math.max(minPanY, panY));
             !this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX);
             !this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY);
         }
@@ -1056,9 +1061,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 HistoryUtil.pushState(state);
             }
         }
-        if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
-            SelectionManager.DeselectAll();
-        }
+        // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
+        //     SelectionManager.DeselectAll();
+        // }
         if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined) {
             this.props.focus(doc, options);
         } else {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 20eabe6a9..9d738180b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1084,7 +1084,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
         const isButton = this.props.Document.type === DocumentType.FONTICON;
-        if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
+        if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a1de944d8..aadad5ffa 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,6 +14,8 @@ import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from '../../util/DragManager';
@@ -127,6 +129,48 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.layoutDoc._height = w;
     });
 
+    crop = (region: Doc | undefined, addCrop?: boolean) => {
+        if (!region) return;
+        const cropping = Doc.MakeCopy(region, true);
+        Doc.GetProto(region).lockedPosition = true;
+        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).isPushpin = true;
+        this.addDocument(region);
+        const anchx = NumCast(cropping.x);
+        const anchy = NumCast(cropping.y);
+        const anchw = NumCast(cropping._width);
+        const anchh = NumCast(cropping._height);
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]) / anchw;
+        cropping.title = "crop: " + this.rootDoc.title;
+        cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
+        cropping.y = NumCast(this.rootDoc.y);
+        cropping._width = anchw * (this.props.scaling?.() || 1);
+        cropping._height = anchh * (this.props.scaling?.() || 1);
+        cropping.isLinkButton = undefined;
+        const croppingProto = Doc.GetProto(cropping);
+        croppingProto.annotationOn = undefined;
+        croppingProto.isPrototype = true;
+        croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+        croppingProto.type = DocumentType.IMG;
+        croppingProto.layout = ImageBox.LayoutString("data")
+        croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
+        croppingProto["data-nativeWidth"] = anchw;
+        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto.viewScale = viewScale;
+        croppingProto.viewScaleMin = viewScale;
+        croppingProto.panX = anchx / viewScale;
+        croppingProto.panY = anchy / viewScale;
+        croppingProto.panXMin = (anchx) / viewScale;
+        croppingProto.panXMax = (anchw) / viewScale;
+        croppingProto.panYMin = (anchy) / viewScale;
+        croppingProto.panYMax = (anchh) / viewScale;
+        if (addCrop) {
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+        }
+        this.props.bringToFront(cropping);
+        return cropping;
+    }
+
     specificContextMenu = (e: React.MouseEvent): void => {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
@@ -367,6 +411,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     savedAnnotations={this._savedAnnotations}
                     annotationLayer={this._annotationLayer.current}
                     mainCont={this._mainCont.current}
+                    anchorMenuCrop={this.crop}
                 />}
         </div >);
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 710270be4..41e2fc266 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -19,7 +19,7 @@ import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -219,6 +219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             return undefined;
         });
         AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
+        AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
          * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.  
          * It also initiates a Drag/Drop interaction to place the text annotation.
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index ad3afb775..29d068817 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -46,8 +46,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
 
+    public OnCrop: (e: PointerEvent) => void = unimplementedFunction;
     public OnClick: (e: PointerEvent) => void = unimplementedFunction;
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+    public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
     public GetAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     public Delete: () => void = unimplementedFunction;
@@ -79,6 +81,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         }, returnFalse, e => this.OnClick?.(e));
     }
 
+    cropDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, (e: PointerEvent) => {
+            this.StartCropDrag(e, this._commentCont.current!);
+            return true;
+        }, returnFalse, e => this.OnCrop?.(e));
+    }
+
     @action
     highlightClicked = (e: React.MouseEvent) => {
         if (!this.Highlight(this.highlightColor, false) && this.Pinned) {
@@ -161,7 +170,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         <FontAwesomeIcon style={{ position: "absolute", transform: "scale(0.5)", transformOrigin: "top left", top: 12, left: 12 }} icon={"link"} size="lg" />
                     </button>
                 </Tooltip>,
-                <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} />
+                <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} />,
+                AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? <></> : <Tooltip key="crop" title={<div className="dash-tooltip">{"Click/Drag to create cropped image"}</div>}>
+                    <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: "grab" }}>
+                        <FontAwesomeIcon icon="image" size="lg" />
+                    </button>
+                </Tooltip>,
             ] : [
                 <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
                     <button className="antimodeMenu-button" onPointerDown={this.Delete}>
-- 
cgit v1.2.3-70-g09d2


From 40ccb702d1dddf0ce9f15e047feb321353f01580 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 27 Apr 2022 14:55:04 -0400
Subject: fixed following pushpin links to text selections to toggle the text
 box (since markers don't have a hidden flag for toggling).  fixed pdf
 next/prev page and # pages.  fixed pdf selections to not include <br>'s which
 apparently have the wrong bounds.

---
 src/client/util/DocumentManager.ts      | 11 ++++++-----
 src/client/views/nodes/DocumentView.tsx |  2 +-
 src/client/views/nodes/PDFBox.tsx       | 10 ++++++++--
 src/client/views/pdf/PDFViewer.tsx      |  8 +++-----
 4 files changed, 18 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 607a3d6bf..cc3196baa 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -168,15 +168,17 @@ export class DocumentManager {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
         const docView = getFirstDocView(targetDoc, originatingDoc);
-        const wasHidden = targetDoc.hidden;  // 
+        const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
+        const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field
+        const wasHidden = resolvedTarget.hidden;
         if (wasHidden) {
             runInAction(() => {
-                targetDoc.hidden = false;
-                docView?.props.bringToFront(targetDoc);
+                resolvedTarget.hidden = false;
+                docView?.props.bringToFront(resolvedTarget);
             });  // if the target is hidden, un-hide it here.
         }
         const focusAndFinish = (didFocus: boolean) => {
-            const finalTargetDoc = docView?.Document ?? targetDoc;
+            const finalTargetDoc = docView?.Document ?? resolvedTarget;
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
                     finalTargetDoc.hidden = !finalTargetDoc.hidden;
@@ -188,7 +190,6 @@ export class DocumentManager {
             finished?.();
             return false;
         };
-        const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
         const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc);
         const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9d738180b..88000ecbd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -460,7 +460,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
-        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
+        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus) : ViewAdjustment.doNothing), focusSpeed ?? 0))
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 23749d479..b556b0ef1 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -157,8 +157,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     });
     public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
     public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
-    public backPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) - 1; return true; };
-    public forwardPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) + 1; return true; };
+    public backPage = () => {
+        this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
+        return true;
+    };
+    public forwardPage = () => {
+        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1);
+        return true;
+    };
     public gotoPage = (p: number) => this.Document._curPage = p;
 
     @undoBatch
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 92a69c02b..b58966173 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -167,7 +167,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     });
                     if (i === this.props.pdf.numPages - 1) {
                         this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1],
-                            page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], i);
+                            page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages);
                     }
                 }))));
             this.props.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72;
@@ -299,9 +299,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
 
     @action
     gotoPage = (p: number) => {
-        if (this._pdfViewer?._setDocumentViewerElement?.offsetParent) {
-            this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
-        }
+        this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
     }
 
     @action
@@ -419,7 +417,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
-                if (rect && rect.width !== this._mainCont.current.clientWidth) {
+                if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) {
                     const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
                     const annoBox = document.createElement("div");
                     annoBox.className = "marqueeAnnotator-annotationBox";
-- 
cgit v1.2.3-70-g09d2


From 2192b6cd994e71812d335a9902ef4e4ae296314b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 27 Apr 2022 23:33:19 -0400
Subject: fixed following links (jumpToDocument) to work properly with markers
 and hidden flags in nested arrangements.  pushpin behaviors had been broken
 for scrolling to a location within a text document and hiding the document if
 no scroll was needed.  unhiding hidden images/pdfs wasn't working when
 following a pushpin to an annotation on them.

---
 src/client/util/DocumentManager.ts                 | 26 +++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  7 ++++--
 3 files changed, 19 insertions(+), 16 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index cc3196baa..9febd4302 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -167,15 +167,15 @@ export class DocumentManager {
     ): Promise<void> => {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-        const docView = getFirstDocView(targetDoc, originatingDoc);
+        var docView = getFirstDocView(targetDoc, originatingDoc);
         const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
         const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field
-        const wasHidden = resolvedTarget.hidden;
+        var wasHidden = resolvedTarget.hidden;
         if (wasHidden) {
             runInAction(() => {
-                resolvedTarget.hidden = false;
+                resolvedTarget.hidden = false;  // if the target is hidden, un-hide it here.
                 docView?.props.bringToFront(resolvedTarget);
-            });  // if the target is hidden, un-hide it here.
+            });
         }
         const focusAndFinish = (didFocus: boolean) => {
             const finalTargetDoc = docView?.Document ?? resolvedTarget;
@@ -188,16 +188,15 @@ export class DocumentManager {
                 !noSelect && docView?.select(false);
             }
             finished?.();
-            return false;
         };
-        const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc);
+        const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
         const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
-        if (!docView && annoContainerView) {
+        if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === "layout_icon") {
                 annoContainerView.iconify(() => this.jumpToDocument(
                     targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
@@ -221,6 +220,8 @@ export class DocumentManager {
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
             } else {  // otherwise try to get a view of the context of the target
                 if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
+                    wasHidden = wasHidden || targetDocContextView.rootDoc.hidden;
+                    targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden
                     targetDocContext._viewTransition = "transform 500ms";
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
                         willZoom, afterFocus: async () => {
@@ -240,24 +241,24 @@ export class DocumentManager {
                         finished?.();
                     } else { // no timecode means we need to find the context view and focus on our target
                         const findView = (delay: number) => {
-                            const retryDocView = getFirstDocView(targetDoc);  // test again for the target view snce we presumably created the context above by focusing on it
-                            if (retryDocView) {   // we found the target in the context
+                            const retryDocView = getFirstDocView(resolvedTarget);  // test again for the target view snce we presumably created the context above by focusing on it
+                            if (retryDocView) {   // we found the target in the context. 
                                 Doc.linkFollowHighlight(retryDocView.rootDoc);
-                                retryDocView.props.focus(targetDoc, {
+                                retryDocView.focus(targetDoc, {
                                     willZoom, afterFocus: (didFocus: boolean) =>
                                         new Promise<ViewAdjustment>(res => {
                                             !noSelect && focusAndFinish(didFocus);
                                             res(ViewAdjustment.doNothing);
                                         })
                                 }); // focus on the target in the context
-                            } else if (delay > 1500) {
+                            } else if (delay > 1000) {
                                 // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.
                                 if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);
                                 if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document
                                     createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target
                                 }
                             } else {
-                                setTimeout(() => findView(delay + 250), 250);
+                                setTimeout(() => findView(delay + 200), 200);
                             }
                         };
                         findView(0);
@@ -270,7 +271,6 @@ export class DocumentManager {
             }
         }
     }
-
 }
 export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) {
     const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 88000ecbd..9d738180b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -460,7 +460,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
-        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing;
+        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus) : ViewAdjustment.doNothing), focusSpeed ?? 0))
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 41e2fc266..756d6c2d6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -813,6 +813,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         };
 
         let start = 0;
+        this._didScroll = false; // assume we don't need to scroll.  if we do, this will get set to true in handleScrollToSelextion when we dispatch the setSelection below
         if (this._editorView && textAnchorId) {
             const editor = this._editorView;
             const ret = findAnchorFrag(editor.state.doc.content, editor);
@@ -832,7 +833,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
 
-        return this._focusSpeed;
+        return this._didScroll ? this._focusSpeed : undefined; // if we actually scrolled, then return some focusSpeed
     }
 
     // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
@@ -1128,6 +1129,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         });
     }
+    _didScroll = false;
     setupEditor(config: any, fieldKey: string) {
         const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.props.fieldKey]);
         const rtfField = Cast((!curText && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
@@ -1142,7 +1144,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     const scrollRef = self._scrollRef.current;
                     const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
                     const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
-                    if ((topOff || botOff) && scrollRef) {
+                    if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
                         const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
                         const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale;
                         if (this._focusSpeed !== undefined) {
@@ -1150,6 +1152,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         } else {
                             scrollRef.scrollTo({ top: scrollPos });
                         }
+                        this._didScroll = true;
                     }
                     return true;
                 },
-- 
cgit v1.2.3-70-g09d2


From 59a22f3a007b201e68ac2f3e1d62e0ca5e66488a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Apr 2022 12:13:44 -0400
Subject: fixed feedback dragging link button. fixed warnings.  added some
 assymetric linkRelationships.

---
 src/client/documents/Documents.ts                              |  3 ++-
 src/client/util/DocumentManager.ts                             |  2 +-
 src/client/util/LinkManager.ts                                 |  2 +-
 src/client/views/SidebarAnnos.tsx                              |  2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx       |  2 +-
 src/client/views/linking/LinkMenu.tsx                          |  4 +---
 src/client/views/nodes/DocumentLinksButton.scss                |  2 ++
 src/client/views/nodes/DocumentLinksButton.tsx                 | 10 +++++-----
 src/client/views/nodes/DocumentView.tsx                        |  4 ++--
 src/client/views/nodes/ImageBox.tsx                            |  2 +-
 src/client/views/nodes/PDFBox.tsx                              |  4 ++--
 src/client/views/nodes/formattedText/RichTextRules.ts          |  2 +-
 src/client/views/search/SearchBox.tsx                          |  2 +-
 src/server/ApiManagers/UploadManager.ts                        |  4 ++--
 15 files changed, 24 insertions(+), 23 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0bb873667..087d32a60 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1094,10 +1094,11 @@ export namespace DocUtils {
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
         broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
         return DocUtils.ActiveRecordings.map(audio =>
-            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording link", "recording timeline"));
+            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline"));
     }
 
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link";
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === Doc.UserDoc()) return undefined;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 412d5a169..b6c28d2fe 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -167,7 +167,7 @@ export class DocumentManager {
     ): Promise<void> => {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-        var docView = getFirstDocView(targetDoc, originatingDoc);
+        const docView = getFirstDocView(targetDoc, originatingDoc);
         const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
         const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field
         var wasHidden = resolvedTarget.hidden;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 7ba7a1d4c..9445533dc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -152,7 +152,7 @@ export class LinkManager {
         this.relatedLinker(anchor).forEach(link => {
             if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") {
                 const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(":") ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+                const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 43a02d029..04c0565ea 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -61,7 +61,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
         this.allMetadata.map(tag => target[tag] = tag);
-        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup");
+        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline comment:comment on");
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f0b3d70a0..3f72052ae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -278,7 +278,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) { // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
                 const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
                 this.props.addDocument?.(source);
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, "annotated by:annotation of", ""); // TODODO this is where in text links get passed
             }
             e.stopPropagation();  //  do nothing if link is dropped into any freeform view parent of dragged document
             return true;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2174aaf10..5f303b428 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -521,7 +521,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         });
         const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
-        DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", "");
+        DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index a564c59d3..17d28e886 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,9 +1,7 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
 import { DocumentView } from "../nodes/DocumentView";
 import { LinkDocPreview } from "../nodes/LinkDocPreview";
 import { LinkEditor } from "./LinkEditor";
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 9ab3171d3..0f3eb14bc 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -2,7 +2,9 @@
 
 .documentLinksButton-wrapper {
     transform-origin: top left;
+    width: 100%;
 }
+
 .documentLinksButton-menu {
     width: 100%;
     height: 100%;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index d2e6e8579..7f69adf6c 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -160,8 +160,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
             startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
             const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink },
-                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" :
-                    endLink.type === DocumentType.RTF ? "Commentary:Comments On" : "link", undefined, undefined, true);
+                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : undefined, undefined, undefined, true);
 
             LinkManager.currentLink = linkDoc;
 
@@ -245,9 +244,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 </div>
             </div>
             :
-            <div className="documentLinksButton-menu" ref={this._linkButton}>
+            <div className="documentLinksButton-menu" >
                 {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node
-                    <div className={"documentLinksButton-endLink"}
+                    <div className={"documentLinksButton-endLink"} ref={this._linkButton}
                         onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
                         onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
@@ -256,7 +255,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 }
                 {
                     this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
-                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton}
+                            onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
                             <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                         </div>
                         :
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9d738180b..8570f6fff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                 const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y]);
             }
         }
     }
@@ -674,7 +674,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
             const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
-            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
+            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from");
         }
         this.Document.followLinkLocation = "inPlace";
         this.Document.followLinkZoom = true;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index aadad5ffa..5982d4d66 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -152,7 +152,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data")
+        croppingProto.layout = ImageBox.LayoutString("data");
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
         croppingProto["data-nativeWidth"] = anchw;
         croppingProto["data-nativeHeight"] = anchh;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b556b0ef1..668c6c8fc 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -160,11 +160,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public backPage = () => {
         this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
         return true;
-    };
+    }
     public forwardPage = () => {
         this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1);
         return true;
-    };
+    }
     public gotoPage = (p: number) => this.Document._curPage = p;
 
     @undoBatch
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 00c03875b..427e05edb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -275,7 +275,7 @@ export class RichTextRules {
                                     this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                                 }
                                 const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: rawdocid.replace(/^:/, ""), _width: 500, _height: 500, }, docid);
-                                DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to", undefined);
+                                DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to:portal from", undefined);
 
                                 const fstate = this.TextBox.EditorView?.state;
                                 if (fstate && selection) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 5fe2a5ab1..9257cb75e 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -112,7 +112,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         if (this.props.linkFrom) {
             const linkFrom = this.props.linkFrom();
             if (linkFrom) {
-                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }, "Link");
+                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
             }
         }
     });
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 5e0cd67cc..83a2bc79b 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -14,7 +14,7 @@ import { normalize } from "path";
 import RouteSubscriber from "../RouteSubscriber";
 const imageDataUri = require('image-data-uri');
 import { SolrManager } from "./SearchManager";
-let fs = require('fs');
+const fs = require('fs');
 
 export enum Directory {
     parsed_files = "parsed_files",
@@ -265,7 +265,7 @@ export default class UploadManager extends ApiManager {
                 }
                 if (deleteFiles) {
                     const path = serverPathToFile(Directory.images, "");
-                    let regex = new RegExp(`${deleteFiles}.*`);
+                    const regex = new RegExp(`${deleteFiles}.*`);
                     fs.readdirSync(path).filter((f: any) => regex.test(f)).map((f: any) => fs.unlinkSync(path + f));
                 }
                 return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
-- 
cgit v1.2.3-70-g09d2


From 99ae2ccde9dbcf6bae75edea231d4b10c736a692 Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Thu, 28 Apr 2022 17:09:13 -0400
Subject: feat: recording box turning into video + only one recording box

---
 package-lock.json                                  | 44 ++++++++++++++--------
 src/client/views/OverlayView.tsx                   |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  4 +-
 src/client/views/nodes/trails/PresElementBox.tsx   | 16 +++++---
 5 files changed, 44 insertions(+), 26 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 684caa92e..691f56160 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4775,16 +4775,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -5812,8 +5802,8 @@
             "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
         },
         "equation-editor-react": {
-            "version": "github:bobzel/equation-editor-react#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
-            "from": "github:bobzel/equation-editor-react#useLocally",
+            "version": "git+ssh://git@github.com/bobzel/equation-editor-react.git#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
+            "from": "equation-editor-react@github:bobzel/equation-editor-react#useLocally",
             "requires": {
                 "jquery": "^3.4.1",
                 "mathquill": "^0.10.1-a"
@@ -5912,6 +5902,18 @@
                 "d": "1",
                 "es5-ext": "^0.10.35",
                 "es6-symbol": "^3.1.1"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
         "es6-promise": {
@@ -5927,6 +5929,18 @@
             "requires": {
                 "d": "^1.0.1",
                 "ext": "^1.1.2"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
         "escalade": {
@@ -8002,14 +8016,14 @@
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
             "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
             "requires": {
-                "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                "image-size": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
                 "tryit": "^1.0.1"
             },
             "dependencies": {
                 "image-size": {
-                    "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
-                    "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
+                    "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                    "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
                 },
                 "isarray": {
                     "version": "0.0.1",
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 806abb990..f86406997 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -187,8 +187,8 @@ export class OverlayView extends React.Component {
                               bringToFront={emptyFunction}
                               addDocument={undefined}
                               removeDocument={undefined}
-                              PanelWidth={returnOne}
-                              PanelHeight={returnOne}
+                              PanelWidth={() => NumCast(d._width)}
+                              PanelHeight={() => NumCast(d._height)}
                               ScreenToLocalTransform={Transform.Identity}
                               renderDepth={1}
                               isDocumentActive={returnTrue}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 78c6c9776..f48173975 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1317,7 +1317,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
             const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
             const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
             const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
-            const isOverlay: boolean = CurrentUserUtils.OverlayDocs.includes(this.props.Document);
+            // const isOverlay: boolean = CurrentUserUtils.OverlayDocs.includes(this.props.Document);
             return (<div className="contentFittingDocumentView">
                   {!this.props.Document || !this.props.PanelWidth() ? (null) : (
                         <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 6f9451925..86358e838 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -39,8 +39,8 @@ export class RecordingBox extends ViewBoxBaseComponent() {
             this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
 
             this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
-            this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-            this.layoutDoc._fitWidth = undefined;
+            // this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
+            // this.layoutDoc._fitWidth = undefined;
             this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client);
       }
 
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 05f1b0365..f4f33482e 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
@@ -275,23 +275,27 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       @action
       startRecording = (targetDoc: Doc, activeItem: Doc) => {
 
-            // TODO: Remove everything that already exists
+            // Remove every recording that already exists in overlay view
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides !== null) {
+                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                  }
+            })
+
             if (activeItem.recording) {
                   // if we already have an existing recording
                   Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
 
             } else {
                   // if we dont have any recording
-                  console.log(targetDoc.title, activeItem.title);
                   const recording = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
+                  // attach the recording to the slide, and attach the slide to the recording
+                  recording.slides = activeItem
                   activeItem.recording = recording
                   // TODO: Figure out exactly where we want the video to appear
                   const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-                  console.log("x: ", pt[0], "y: ", pt[1]);
                   recording.x = pt[0];
                   recording.y = pt[1];
-                  console.log(Doc.UserDoc().myOverlayDocs)
-                  // Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
                   Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
             }
 
-- 
cgit v1.2.3-70-g09d2

