gridstack / gridstack.js

Build interactive dashboards in minutes.
https://gridstackjs.com
MIT License
6.38k stars 1.27k forks source link

Responsive grid coordinates system is inconsistent #2678

Closed rory-orennia closed 2 months ago

rory-orennia commented 2 months ago

Subject of the issue

The x, w, y, h, minW, minH properties of items inside the grid is inconsistent if you add items at different screen/grid sizes. I think there should be a mode (maybe default?) where you lock the coordinates system to your initial column layout, and then the display of those items will change based on what size the grid is now. Currently if you add an item its size and location could be different based on what size the browser was at the time: image It seems to me that you'd want a mode where adding a "4x1" widget would result in the same sized widget as all the other 4x1 widgets regardless of when they were added to the grid.

My use case is that I have a dashboarding application and we save that dashboard to a database with each widget pulling it's coordinates from gridstack's item's values. in full 1080p we have a grid of 30 columns, but if you go down to something like 720p we drop to 10 columns and finally if you go down to phone size we do the 1 column mode. If you create a dashboard in 1080p and save, then open it in 720p everything looks great. Now if you save the dashboard in 720p and then open it in 1080p, everything is shrunk and in the wrong positions. This is because in 1080p a widget might be {x: 10, y: 0, w: 15, h: 10} then in 720p it gets the layout recalculated to {x:3, y:0, w:5, h:10}. Now if you save that dashboard and reload the page in 1080p instead of being a widget that was half the screen wide, 1/3rd from the left, you get a widget that's almost touching the left side and is now 1/6th the width of the screen

Your environment

Steps to reproduce

https://jsfiddle.net/qjb3faLk/1/

Expected behavior

There should be a mode where your coordinates system and display sizes/positions are disconnected so that adding a 4x1 widget will result in the same widget size no matter what responsive breakpoint your grid is currently at

adumesny commented 2 months ago

| It seems to me that you'd want a mode where adding a "4x1" widget would result in the same sized widget as all the other 4x1 widgets regardless of when they were added to the grid.

adding a 4x1 widget will always (if you have least 4 column) be that size. the issue you are having is that then moving to 8 column might scale those items up, but a newly added 4x1 will still be 4 width. the problem can be solved using 'move' (or other repacking what I actually use) and not scale modes to keep those 4x1 at same size.

don't expect teh lib to say insert a 4x1 and get 8x1 because you happen to be i 8 column mode. either you adjust the drag in to be nx1 OR don't scale items as column change if you want that button to insert widgets the same size... simple.

closing as designed. use different refow mode.

rory-orennia commented 2 months ago

As I described above, there's no way to not ruin your grid layout if you try to save or load the grid in a different breakpoint than before. I think anyone who uses these breakpoints in an application where they save the grid state is going to hit the same issue. There should be some way where opening the grid in a smaller screen and hitting save doesn't break everything when you re-open the grid in a higher breakpoint

I was able to work around this by manually correcting the coordinates system back to the largest breakpoint before saving the grid state to the database:

          const { x, y, w, h, minH, minW } = item;
          const baseColumns = 30;
          const currColumns = grid.getColumn();
          // If we're saving while in a smaller grid, we need to convert the positions back to
          // a 30 column grid coordinate system for the database
          if (currColumns !== baseColumns) {
            const xRatio = baseColumns / currColumns;
            dbItem.gridPosition = {
              x: x ? Math.round(x * xRatio) : undefined,
              y,
              w: w ? Math.round(w * xRatio) : undefined,
              h,
              minH,
              minW,
            };
          } else {
            dbItem.gridPosition = { x, y, w, h, minH, minW };
          }
adumesny commented 2 months ago

The code should already save the grid to the highest resolution possible (not the one you are necessarly seeing) for that reason, and some people complain the layout didn't match what they were seeing then... can't please everyone.

Unless I missunderstood your issue (I didn't read the second part).

personally for responsice I use 'compact' (or 'list') as they others don't make that much sense to me...

rory-orennia commented 1 month ago

@adumesny I'm still having trouble getting this to behave in a consistent way. Doesn't matter what layout mode I select. It seems like maybe it doesn't take the layout mode into account when "loading" the widgets at startup (batch update) vs when you make changes to the window/grid size after.

The easiest reproduction is to open https://gridstackjs.com/demo/responsive.html in full screen, shrink it down, notice the grid layout. Then hit F5 and watch the grid refresh in a different layout. I tried using my jsFiddle above and setting the layout at init and that didn't help either.

shrink after load: image

refresh while already shrunk: image

The result of this is that if a user shrinks their browser, lays out their widgets, then saves, then comes back, their dashboard doesn't look anything like how it did when they first saved it.

Am I maybe missing something as to how this is supposed to work?

rory-orennia commented 1 month ago

I made a new JSFIddle to show the issue a bit better.
https://jsfiddle.net/fep5v8mq/

Here's the steps to reproduce:

  1. scroll down to box 2 image
  2. shrink that box down and see it saved to localStorage image
  3. hit Run again to restart the app. See how box 2 is changed after init image