Skip to content

Commit

Permalink
more fix gridstack#404
Browse files Browse the repository at this point in the history
* changing column size and manually resizing a widget will now call resizeToContent()
* added resizeToContentCB for app to override how to resize.
* Added `resizeToContentParent` to make generic code more robust.
* fixed some 1 column loading Nan issues
  • Loading branch information
adumesny committed Aug 23, 2023
1 parent aa585d4 commit b92f840
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 21 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,15 @@ grid.printCount();

## Extend Engine

You can now (5.1+) easily create your own layout engine to further customize you usage. Here is a typescript example
You can now (5.1+) easily create your own layout engine to further customize your usage. Here is a typescript example

```ts
import { GridStack, GridStackEngine, GridStackNod, GridStackMoveOpts } from 'gridstack';

class CustomEngine extends GridStackEngine {

/** refined this to move the node to the given new location */
public moveNode(node: GridStackNode, o: GridStackMoveOpts): boolean {
public override moveNode(node: GridStackNode, o: GridStackMoveOpts): boolean {
// keep the same original X and Width and let base do it all...
o.x = node.x;
o.w = node.w;
Expand Down
10 changes: 10 additions & 0 deletions demo/fitToContent.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<title>FitToContent demo</title>

<link rel="stylesheet" href="demo.css"/>
<link rel="stylesheet" href="../dist/gridstack-extra.css"/>
<script src="../dist/gridstack-all.js"></script>
<style type="text/css">
.grid-stack-item-content {
Expand All @@ -18,6 +19,11 @@
<div class="container">
<h1>Cell FitToContent options demo</h1>
<p>new 9.x feature that size the items to fit their content height as to not have scroll bars (unless `fitToContent:false` in C: case) </p>
<div>
column:
<a onClick="column(8)" class="btn btn-primary" href="#">8</a>
<a onClick="column(12)" class="btn btn-primary" href="#">12</a>
</div>
<br>
<div class="grid-stack"></div>
</div>
Expand All @@ -38,6 +44,10 @@ <h1>Cell FitToContent options demo</h1>
{x:0, y:1, w:3, content: `<div>D: ${text} ${text}</div>`},
];
grid.load(items);

function column(n) {
grid.column(n, 'none');
}
</script>
</body>
</html>
1 change: 1 addition & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Change log

## 8.4.0-dev (TBD)
- feat [#404](https://github.com/gridstack/gridstack.js/issues/404) added `GridStackOptions.fitToContent` and `GridStackWidget.fitToContent` to make gridItems size themselves to their content (no scroll bar), calling `GridStack.resizeToContent(el)` whenever the grid or item is resized.
- also added new `'resizecontent'` event, and `resizeToContentCB` and `resizeToContentParent` vars.
- fix [#2406](https://github.com/gridstack/gridstack.js/issues/2406) inf loop when autoPosition after loading into 1 column, then 2.

## 8.4.0 (2023-07-20)
Expand Down
6 changes: 3 additions & 3 deletions src/gridstack-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,10 @@ export class GridStackEngine {
if (node.minW && node.minW <= this.column) { node.w = Math.max(node.w, node.minW); }
if (node.minH) { node.h = Math.max(node.h, node.minH); }

// if user loaded a larger than allowed widget for current # of columns (or force 1 column mode),
// if user loaded a larger than allowed widget for current # of columns,
// remember it's position & width so we can restore back (1 -> 12 column) #1655 #1985
// IFF we're not in the middle of column resizing!
const saveOrig = this.column === 1 || node.x + node.w > this.column;
const saveOrig = (node.x || 0) + (node.w || 1) > this.column;
if (saveOrig && this.column < 12 && !this._inColumnResize && node._id && this.findCacheLayout(node, 12) === -1) {
let copy = {...node}; // need _id + positions
if (copy.autoPosition) { delete copy.x; delete copy.y; }
Expand Down Expand Up @@ -771,7 +771,7 @@ export class GridStackEngine {
if (!n) return; // no cache for new nodes. Will use those values.
// Y changed, push down same amount
// TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode)
if (node.y !== node._orig.y) {
if (n.y >= 0 && node.y !== node._orig.y) {
n.y += (node.y - node._orig.y);
}
// X changed, scale from new position
Expand Down
61 changes: 45 additions & 16 deletions src/gridstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { GridStackEngine } from './gridstack-engine';
import { Utils, HeightData, obsolete } from './utils';
import { gridDefaults, ColumnOptions, GridItemHTMLElement, GridStackElement, GridStackEventHandlerCallback,
GridStackNode, GridStackWidget, numberOrString, DDUIData, DDDragInOpt, GridStackPosition, GridStackOptions,
dragInDefaultOptions, GridStackEventHandler, GridStackNodesHandler, AddRemoveFcn, SaveFcn, CompactOptions, GridStackMoveOpts } from './types';
dragInDefaultOptions, GridStackEventHandler, GridStackNodesHandler, AddRemoveFcn, SaveFcn, CompactOptions, GridStackMoveOpts, ResizeToContentFcn } from './types';

/*
* and include D&D by default
Expand All @@ -35,7 +35,7 @@ export interface GridHTMLElement extends HTMLElement {
}
/** list of possible events, or space separated list of them */
export type GridStackEvent = 'added' | 'change' | 'disable' | 'drag' | 'dragstart' | 'dragstop' | 'dropped' |
'enable' | 'removed' | 'resize' | 'resizestart' | 'resizestop' | string;
'enable' | 'removed' | 'resize' | 'resizestart' | 'resizestop' | 'resizecontent' | string;

/** Defines the coordinates of an object */
export interface MousePosition {
Expand Down Expand Up @@ -184,6 +184,11 @@ export class GridStack {
*/
public static saveCB?: SaveFcn;

/** callback to use for resizeToContent instead of the built in one */
public static resizeToContentCB?: ResizeToContentFcn;
/** parent class for sizing content. defaults to '.grid-stack-item-content' */
public static resizeToContentParent = '.grid-stack-item-content';

/** scoping so users can call GridStack.Utils.sort() for example */
public static Utils = Utils;

Expand Down Expand Up @@ -859,9 +864,8 @@ export class GridStack {
}
this.engine.columnChanged(oldColumn, column, domNodes, layout);
if (this._isAutoCellHeight) this.cellHeight();
// this.engine.nodes.forEach(n => {
// if (Utils.shouldFitToContent(n)) this.resizeToContent(n.el);
// });

this.doContentResize();

// and trigger our event last...
this._ignoreLayoutsNodeChange = true; // skip layout update
Expand Down Expand Up @@ -1045,7 +1049,8 @@ export class GridStack {
this._gsEventHandler[name] = (event: CustomEvent) => (callback as GridStackNodesHandler)(event, event.detail);
}
this.el.addEventListener(name, this._gsEventHandler[name]);
} else if (name === 'drag' || name === 'dragstart' || name === 'dragstop' || name === 'resizestart' || name === 'resize' || name === 'resizestop' || name === 'dropped') {
} else if (name === 'drag' || name === 'dragstart' || name === 'dragstop' || name === 'resizestart' || name === 'resize'
|| name === 'resizestop' || name === 'dropped' || name === 'resizecontent') {
// drag&drop stop events NEED to be call them AFTER we update node attributes so handle them ourself.
// do same for start event to make it easier...
this._gsEventHandler[name] = callback;
Expand Down Expand Up @@ -1258,21 +1263,32 @@ export class GridStack {
GridStack.getElements(els).forEach(el => {
let n = el?.gridstackNode;
if (!n) return;
let height = el.clientHeight;
if (el.parentElement !== n.grid.el) return; // skip if we are not inside a grid
let height = el.clientHeight; // getBoundingClientRect().height seem to flicker back and forth
if (!height) return; // 0 when hidden, skip
const item = el.querySelector('.grid-stack-item-content');
const item = el.querySelector(GridStack.resizeToContentParent);
if (!item) return;
const itemH = item.clientHeight;
const wantedH = (item.firstChild as Element)?.clientHeight || itemH; // NOTE: clientHeight & getBoundingClientRect() is undefined for text and other leaf nodes. use <div> container!
const itemH = item.clientHeight; // available height to our child (minus border, padding...)
const wantedH = (item.firstChild as Element)?.getBoundingClientRect().height || itemH; // NOTE: clientHeight & getBoundingClientRect() is undefined for text and other leaf nodes. use <div> container!
if (itemH === wantedH) return;
height += wantedH - itemH;
const cell = this.getCellHeight();
if (!cell) return;
let h = Math.ceil(height / cell);
if (n.maxH && h > n.maxH) h = n.maxH;
if (h !== n.h) this.moveNode(n, {h});
if (h !== n.h) {
this._ignoreLayoutsNodeChange = true;
this.moveNode(n, {h});
delete this._ignoreLayoutsNodeChange;
}
});
}

/** call the user resize (so we can do extra work) else our build in version */
protected resizeToContentCheck(el: GridItemHTMLElement) {
if (GridStack.resizeToContentCB) GridStack.resizeToContentCB(el);
else this.resizeToContent(el);
}

/**
* Updates the margins which will set all 4 sides at once - see `GridStackOptions.margin` for format options (CSS string format of 1,2,4 values or single number).
Expand Down Expand Up @@ -1608,18 +1624,29 @@ export class GridStack {
// update any nested grids, or items size
this.engine.nodes.forEach(n => {
if (n.subGrid) n.subGrid.onResize()
// update any gridItem height with fitToContent, but wait for DOM $animation_speed to settle if we changed column count
// TODO: is there a way to know what the final (post animation) size of the content will be so we can animate the column width and height together rather than sequentially ?
if (Utils.shouldFitToContent(n)) {
columnChanged ? setTimeout(() => this.resizeToContent(n.el), 300 + 10) : this.resizeToContent(n.el);
}
});
this.doContentResize(columnChanged);

this.batchUpdate(false);

return this;
}

private doContentResize(delay = true, n: GridStackNode = undefined) {
// update any gridItem height with fitToContent, but wait for DOM $animation_speed to settle if we changed column count
// TODO: is there a way to know what the final (post animation) size of the content will be so we can animate the column width and height together rather than sequentially ?
setTimeout(() => {
if (n) {
if (Utils.shouldFitToContent(n)) this.resizeToContentCheck(n.el);
} else {
this.engine.nodes.forEach(n => {
if (Utils.shouldFitToContent(n)) this.resizeToContentCheck(n.el);
});
}
if (this._gsEventHandler['resizeContent']) this._gsEventHandler['resizeContent'](null, n ? [n] : this.engine.nodes);
}, delay ? 300 + 10 : 0);
}

/** add or remove the grid element size event handler */
protected _updateResizeEvent(forceRemove = false): GridStack {
// only add event if we're not nested (parent will call us) and we're auto sizing cells or supporting oneColumn (i.e. doing work)
Expand Down Expand Up @@ -2192,6 +2219,8 @@ export class GridStack {
this._triggerChangeEvent();

this.engine.endUpdate();

if (event.type === 'resizestop') this.doContentResize(false, node);
}

dd.draggable(el, {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type AddRemoveFcn = (parent: HTMLElement, w: GridStackWidget, add: boolea
/** optional function called during save() to let the caller add additional custom data to the GridStackWidget structure that will get returned */
export type SaveFcn = (node: GridStackNode, w: GridStackWidget) => void;

export type ResizeToContentFcn = (els: GridItemHTMLElement) => void;

/**
* Defines the options for a Grid
Expand Down

0 comments on commit b92f840

Please sign in to comment.