From 436b3def89ea955e2ff30fe8c2e9d15092e8c9a9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 5 Mar 2025 15:38:03 -0500
Subject: fixed gridView to have margins.  fixed gridView rowslider.

---
 .../collectionGrid/CollectionGridView.tsx          | 135 +++++++++++----------
 1 file changed, 69 insertions(+), 66 deletions(-)

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

diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 6dffb80f1..80bf4bd12 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -9,7 +9,7 @@ import { emptyFunction } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoable } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -22,9 +22,9 @@ export class CollectionGridView extends CollectionSubView() {
     private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
     private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
+    private _dropLocation: object = {}; // sets the drop location for external drops
     @observable private _rowHeight: Opt<number> = undefined; // temporary store of row height to make change undoable
     @observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
-    private dropLocation: object = {}; // sets the drop location for external drops
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -48,14 +48,20 @@ export class CollectionGridView extends CollectionSubView() {
     }
 
     @computed get colWidthPlusGap() {
-        return (this._props.PanelWidth() - this.margin) / this.numCols;
+        return (this._props.PanelWidth() - 2 * this.xMargin - this.gridGap) / this.numCols;
     }
     @computed get rowHeightPlusGap() {
-        return this.rowHeight + this.margin;
+        return this.rowHeight + this.gridGap;
     }
 
-    @computed get margin() {
-        return NumCast(this.Document.margin, 10);
+    @computed get xMargin() {
+        return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
+    }
+    @computed get yMargin() {
+        return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+    }
+    @computed get gridGap() {
+        return NumCast(this.Document._gridGap, 10);
     } // sets the margin between grid nodes
 
     @computed get flexGrid() {
@@ -77,10 +83,10 @@ export class CollectionGridView extends CollectionSubView() {
                 pairs.forEach((pair, i) => {
                     const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
                     if (existing) newLayouts.push(existing);
-                    else if (Object.keys(this.dropLocation).length) {
+                    else if (Object.keys(this._dropLocation).length) {
                         // external drop
-                        this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
-                        this.dropLocation = {};
+                        this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this._dropLocation as { x: number; y: number }, !this.flexGrid));
+                        this._dropLocation = {};
                     } else {
                         // internal drop
                         this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
@@ -115,30 +121,29 @@ export class CollectionGridView extends CollectionSubView() {
      * @returns the default location of the grid node (i.e. when the grid is static)
      * @param index
      */
-    unflexedPosition(index: number): Omit<Layout, 'i'> {
-        return {
-            x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
-            y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
-            w: this.defaultW,
-            h: this.defaultH,
-            static: true,
-        };
-    }
+    unflexedPosition = (index: number): Omit<Layout, 'i'> => ({
+        x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
+        y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
+        w: this.defaultW,
+        h: this.defaultH,
+        static: true,
+    });
 
     /**
      * Maps the x- and y- coordinates of the event to a grid cell.
      */
-    screenToCell(sx: number, sy: number) {
-        const pt = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
-        const x = Math.floor(pt[0] / this.colWidthPlusGap);
-        const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
+    screenToCell = (sx: number, sy: number) => {
+        const [ptx, pty] = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
+        const x = Math.floor((ptx + this.xMargin) / this.colWidthPlusGap);
+        const y = Math.floor((pty + this.yMargin + this._scroll) / this.rowHeight);
         return { x, y };
-    }
+    };
 
     /**
      * Creates a layout object for a grid item
      */
-    makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
+    makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => 
+        ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }); // prettier-ignore
 
     /**
      * Adds a layout to the list of layouts.
@@ -152,9 +157,9 @@ export class CollectionGridView extends CollectionSubView() {
     /**
      * @returns the transform that will correctly place the document decorations box.
      */
-    private lookupIndividualTransform = (layout: Layout) => {
+    lookupIndividualTransform = (layout: Layout) => {
         const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
-        const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
+        const pos = { x: xypos.x * this.colWidthPlusGap + this.gridGap + this.xMargin, y: xypos.y * this.rowHeightPlusGap + this.gridGap - this._scroll + this.yMargin };
 
         return this.ScreenToLocalBoxXf().translate(-pos.x, -pos.y);
     };
@@ -169,9 +174,9 @@ export class CollectionGridView extends CollectionSubView() {
     /**
      * Stores the layout list on the Document as JSON
      */
-    setLayoutList(layouts: Layout[]) {
+    setLayoutList = (layouts: Layout[]) => {
         this.Document.gridLayoutString = JSON.stringify(layouts);
-    }
+    };
 
     isContentActive = () => this._props.isSelected() || this._props.isContentActive();
     isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
@@ -183,29 +188,27 @@ export class CollectionGridView extends CollectionSubView() {
      * @param height
      * @returns the `ContentFittingDocumentView` of the node
      */
-    getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
-        return (
-            <DocumentView
-                {...this._props}
-                Document={layout}
-                TemplateDataDocument={layout.resolvedDataDoc as Doc}
-                NativeWidth={returnZero}
-                NativeHeight={returnZero}
-                fitWidth={this._props.childLayoutFitWidth}
-                containerViewPath={this.childContainerViewPath}
-                renderDepth={this._props.renderDepth + 1}
-                isContentActive={this.isChildContentActive}
-                PanelWidth={width}
-                PanelHeight={height}
-                ScreenToLocalTransform={dxf}
-                setContentViewBox={emptyFunction}
-                whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
-                onClickScript={this.onChildClickHandler}
-                dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
-                showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
-            />
-        );
-    }
+    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
+        <DocumentView
+            {...this._props}
+            Document={layout}
+            TemplateDataDocument={layout.resolvedDataDoc as Doc}
+            NativeWidth={returnZero}
+            NativeHeight={returnZero}
+            fitWidth={this._props.childLayoutFitWidth}
+            containerViewPath={this.childContainerViewPath}
+            renderDepth={this._props.renderDepth + 1}
+            isContentActive={this.isChildContentActive}
+            PanelWidth={width}
+            PanelHeight={height}
+            ScreenToLocalTransform={dxf}
+            setContentViewBox={emptyFunction}
+            whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+            onClickScript={this.onChildClickHandler}
+            dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+            showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
+        />
+    );
 
     /**
      * Saves the layouts received from the Grid to the Document.
@@ -238,15 +241,14 @@ export class CollectionGridView extends CollectionSubView() {
      * @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
      * The key of the wrapper div must be the same as the `i` value of the corresponding layout.
      */
-    @computed
-    private get contents(): JSX.Element[] {
+    @computed get contents(): JSX.Element[] {
         const collector: JSX.Element[] = [];
         if (this.renderedLayoutList.length === this.childLayoutPairs.length) {
             this.renderedLayoutList.forEach(l => {
                 const child = this.childLayoutPairs.find(c => c.layout[Id] === l.i);
                 const dxf = () => this.lookupIndividualTransform(l);
-                const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.margin;
-                const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
+                const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.gridGap;
+                const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.gridGap;
                 child &&
                     collector.push(
                         <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this._props.isSelected() ? '' : ' static')}>
@@ -295,7 +297,7 @@ export class CollectionGridView extends CollectionSubView() {
      * Handles external drop of images/PDFs etc from outside Dash.
      */
     onExternalDrop = async (e: React.DragEvent): Promise<void> => {
-        this.dropLocation = this.screenToCell(e.clientX, e.clientY);
+        this._dropLocation = this.screenToCell(e.clientX, e.clientY);
         super.onExternalDrop(e, {});
     };
 
@@ -316,12 +318,13 @@ export class CollectionGridView extends CollectionSubView() {
             this,
             e,
             returnFalse,
-            action(() => {
-                undoable(() => {
+            undoable(
+                action(() => {
                     this.Document.gridRowHeight = this._rowHeight;
-                }, 'changing row height')();
-                this._rowHeight = undefined;
-            }),
+                    this._rowHeight = undefined;
+                }),
+                'changing row height'
+            ),
             emptyFunction,
             false,
             false
@@ -391,14 +394,14 @@ export class CollectionGridView extends CollectionSubView() {
                 <div
                     className="collectionGridView-gridContainer"
                     ref={this._containerRef}
-                    style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
+                    style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white'), padding: `${this.yMargin} ${this.xMargin}` }}
                     onWheel={e => e.stopPropagation()}
                     onScroll={action(e => {
                         if (!this._props.isSelected()) e.currentTarget.scrollTop = this._scroll;
                         else this._scroll = e.currentTarget.scrollTop;
                     })}>
                     <Grid
-                        width={this._props.PanelWidth()}
+                        width={this._props.PanelWidth() - 2 * this.xMargin}
                         nodeList={this.contents.length ? this.contents : null}
                         layout={this.contents.length ? this.renderedLayoutList : undefined}
                         childrenDraggable={!!this._props.isSelected()}
@@ -408,15 +411,15 @@ export class CollectionGridView extends CollectionSubView() {
                         transformScale={this.ScreenToLocalBoxXf().Scale}
                         compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
                         preventCollision={BoolCast(this.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
-                        margin={this.margin}
+                        margin={this.gridGap}
                     />
                     <input
                         className="rowHeightSlider"
                         type="range"
-                        style={{ width: this._props.PanelHeight() - 30 }}
+                        style={{ width: this._props.PanelHeight() - 2 * this.yMargin }}
                         min={1}
                         value={this.rowHeight}
-                        max={this._props.PanelHeight() - 30}
+                        max={this._props.PanelHeight() - 2 * this.yMargin}
                         onPointerDown={this.onSliderDown}
                         onChange={this.onSliderChange}
                     />
-- 
cgit v1.2.3-70-g09d2

