Can we have both horizontal and vertical compacting together?
We can achieve that but the grid packing will be very weird.
Set the props
compactType = "horizontal"
verticalCompact = true
FYI Check all possible arrangements to see whether it suits your requirement.
And they stated that verticalCompact is going be deprecated( said by them 2 years ago )
@Hari-Shankar-3296 Yes, I tried setting the verticalCompact prop and it is super weird.
But my goal is to always move tiles from right to left. If i drag a tile from the top row to the bottom row. Then the spot vacated by this tile should be occupied by tile to its right and the tile from the beginning of next row should come up and so on. Like text in paragraph, if we delete letters then next line character will come up.
@nitesh619 I got your point now. The thing you are expecting ( even I need the same behaviour ) is not possible with react-grid-layout currently. But there are other library in which you can achieve this.
Here is the link.
https://ourcodeworld.com/articles/read/663/top-5-best-draggable-droppable-and-resizable-grid-layout-designer-jquery-and-javascript-plugins
and up to me Muuri is the best.
Note :
In Murri we can't resize grid items ( Can be done by adding React-Resizable ).
Some libraries are done using jQuery.
Up to my knowledge Muuri doesn't use jQuery
If you find any other good libraries, Please do let me know. :+1:
Thanks @Hari-Shankar-3296, Murri is really good. But I am not sure how it will work with react and also I can't afford to revert my changes and start with murri, I guess I have passed that stage. So I have decided to modify react-grid-layout codebase for the behavior I need. Wish me luck, looks like a tough task ahead. I will keep you posted.
@nitesh619 Hey, Did you end up getting that working? I've seen react-wrappers for muuri, not sure if they're still up to date.
Hey,
Yes, actually I was able achieve this behavior by easily overriding few util methods of the grid.
@nitesh619 May you share your solution with us ?
@nitesh619 Little reminder if you would please share your solution. Much needed here as well :)
Hey @IvoBeneStudio @amrsalama,
Please find the solution below to achieve "Horizontal + Vertical" alignment.
Create a gridUtil.js or gridUtil.ts file in your project with following imports:
import * as RGL_UTILS from 'react-grid-layout/build/utils'
import {
sortLayoutItems,
getLayoutItem,
cloneLayoutItem,
validateLayout,
correctBounds,
getAllCollisions,
bottom
} from 'react-grid-layout/build/utils
RGL_UTILS.compact = function compact(
layout: Layout,
compactType: CompactType,
cols: number
): Layout {
// We go through the items by row and column.
const sorted = sortLayoutItems(layout, 'vertical');
// Holding for new items.
const out = Array(layout.length);
for (let i = 0, len = sorted.length; i < len; i++) {
let l = cloneLayoutItem(sorted[i]);
// Don't move static elements
if (!l.static) {
l.y = Math.floor(i / cols)
l.x = i % cols
}
// Add to output array to make sure they still come out in the right order.
out[i] = l;
// Clear moved flag, if it exists.
l.moved = false;
}
return out;
}
_Logic:_
Note: After overriding the above method, also override the 'synchronizeLayoutWithChildren' like below:
/* No code is added here. It's just copied from library otherwise grid wont call our overrided compact method */
RGL_UTILS.synchronizeLayoutWithChildren = function synchronizeLayoutWithChildren(initilaLayout, children, cols, compactType) {
....
// copy the exact code from the utils.js 'synchronizeLayoutWithChildren' method
.....
}
/* All code is re-used from the original method except the 'isLeftShift' related code. */
RGL_UTILS.moveElement = (
layout: Layout,
l: LayoutItem,
x: ?number,
y: ?number,
isUserAction: ?boolean,
preventCollision: ?boolean,
compactType: CompactType,
cols: number,
isLeftShift: boolean // overriden - nitesh
): Layout => {
// If this is static and not explicitly enabled as draggable,
// no move is possible, so we can short-circuit this immediately.
if (l.static && l.isDraggable !== true) return layout;
// Short-circuit if nothing to do.
if (l.y === y && l.x === x) return layout;
log(
`Moving element ${l.i} to [${String(x)},${String(y)}] from [${l.x},${l.y}]`
);
const oldX = l.x;
const oldY = l.y;
// This is quite a bit faster than extending the object
if (typeof x === "number") l.x = x;
if (typeof y === "number") l.y = y;
l.moved = true;
// If this collides with anything, move it.
// When doing this comparison, we have to sort the items we compare with
// to ensure, in the case of multiple collisions, that we're getting the
// nearest collision.
let sorted = sortLayoutItems(layout, compactType);
const movingUp =
compactType === "vertical" && typeof y === "number"
? oldY >= y
: compactType === "horizontal" && typeof x === "number"
? oldX >= x
: false;
if (movingUp) sorted = sorted.reverse();
const collisions = getAllCollisions(sorted, l);
// There was a collision; abort
if (preventCollision && collisions.length) {
log(`Collision prevented on ${l.i}, reverting.`);
l.x = oldX;
l.y = oldY;
l.moved = false;
return layout;
}
// overriden - nitesh
if(isUserAction) {
isUserAction = false;
if(oldX === x) isLeftShift = oldY - y <= 0;
if(oldY === y) isLeftShift = oldX - x <= 0;
}
// Move each item that collides away from this element.
for (let i = 0, len = collisions.length; i < len; i++) {
const collision = collisions[i];
log(
`Resolving collision between ${l.i} at [${l.x},${l.y}] and ${collision.i} at [${collision.x},${collision.y}]`
);
// Short circuit so we can't infinite loop
if (collision.moved) continue;
// Don't move static items - we have to move *this* element away
if (collision.static) {
layout = RGL_UTILS.moveElementAwayFromCollision(
layout,
collision,
l,
isUserAction,
compactType,
cols,
isLeftShift // overriden - nitesh
);
} else {
layout = RGL_UTILS.moveElementAwayFromCollision(
layout,
l,
collision,
isUserAction,
compactType,
cols,
isLeftShift // overriden - nitesh
);
}
}
return layout;
}
RGL_UTILS.moveElementAwayFromCollision = function moveElementAwayFromCollision(
layout: Layout,
collidesWith: LayoutItem,
itemToMove: LayoutItem,
isUserAction: ?boolean,
compactType: CompactType,
cols: number,
isLeftShift,
): Layout {
const compactH = compactType === "horizontal";
const preventCollision = collidesWith.static; // we're already colliding (not for static items)
const isTileWrapping = isLeftShift
? itemToMove.x - 1 < 0
: itemToMove.x + 1 >= cols;
const deltaShift = isLeftShift ? -1 : 1
const x = isTileWrapping ? (isLeftShift ? cols - 1 : 0) : itemToMove.x + deltaShift
const y = isTileWrapping ? itemToMove.y + deltaShift : itemToMove.y
return moveElement(
layout,
itemToMove,
compactH ? x : undefined,
y,
isUserAction,
preventCollision,
compactType,
cols,
isLeftShift
);
}
_Logic:_
Please let me know, if you face any problems while implementing it. Also, If anyone wants to volunteer to create a pull request with this feature then I would be happy to guide.
Closing this issue, as above solution will provide the needed feature. Also tested this and fond OK with all test cases. @STRML please review my solution.
I made some changes based on the above, but making the new behavior conditional on a new compactType "wrap". The full PR is at https://github.com/STRML/react-grid-layout/pull/1188.
@nitesh619 Do you mind checking out this CodeSandbox? I'm missing something with your implementation, I just can't figure it out. Thanks!
https://codesandbox.io/s/react-grid-layout-double-compaction-z1kwo?file=/src/App.js
I took a quick look. It's not doing what I need yet.
For example, starting with
abc
def
ghi
and dragging d to where h started out, I want
abc
efg
hdi
but I get
abc
gef
hdi
That's not to say what you have may not be useful to someone. I'm not really clear what you were aiming at.
@JohnThomson I probably should have been more specific. What I'm trying to accomplish is the ability to re-arrange the gird without it creating more rows. Currently, when I drag A to I's spot, F jumps down to a new row.
I don't think you're there yet. When I tried dragging A to I's spot as directly as I could, things jumped all over the place during the drag, and I ended up with
f b c
e f
d g a
h
Trying again along a slightly different path, I got
b c
d e
g h a
f i
I tend to think that the result of dragging a card to a particular destination should be independent of the route
@JohnThomson I agree, I'm not there yet. That's why I posted the CodeSandbox. It reads as if @nitesh619 solved this problem, so I tried implementing his solution up top, but I'm not getting the results.
What you got is my issue. I want a grid of 3 columns and 3 rows at all times. No matter what gets dragged where. Currently, there are times (when dragging any item to the lower right), that a new row is created, opposed to just shuffling the items around the 3x3 grid.
Hey Guys,
Its almost a year since I worked on it. I will try to recall and look at your sandbox solution. Give me sometime.
Best Regards
Nitesh Jain
Looking forward to the solution, I'm encountering the same issue for weeks now and can't find a proper solution.
Cheers!
Most helpful comment
How to do horizontal + vertical compaction combined, also maintain ordering while dragging tiles on the grid
Hey @IvoBeneStudio @amrsalama,
Please find the solution below to achieve "Horizontal + Vertical" alignment.
Create a gridUtil.js or gridUtil.ts file in your project with following imports:
_Logic:_
Note: After overriding the above method, also override the 'synchronizeLayoutWithChildren' like below:
_Logic:_
Please let me know, if you face any problems while implementing it. Also, If anyone wants to volunteer to create a pull request with this feature then I would be happy to guide.