angular-gridster2 icon indicating copy to clipboard operation
angular-gridster2 copied to clipboard

Grid responsiveness for different device sizes (xl, lg, md, sm, xs)

Open tiberiuzuld opened this issue 6 years ago • 6 comments

Making this issue so people stop asking about it every month or so.

Yes you can with CSS Grid and @media-queries You can increase the option mobileBreakpoint: 640 to what size you desire. You can override the css gridster.mobile with your own and you can define your own columns. You will not be able to drag and resize.

There is no way for the grid to work with drag and resize when you move around the widgets depending on the size of the grid.

If you don't need drag and resize then don't use this library use CSS Grid only.

tiberiuzuld avatar Jan 24 '18 11:01 tiberiuzuld

This is how I was able to achieve a dynamic columns solution in my dashboard component. If anyone needs more info let me know. I have pretty good results with this. sometimes I do still see collisions happen but not often. Maybe I need to change the algorithm but haven't touch it since it was working rather well.

Overview Dynamic cols

  1. When the window is resized calculate the column width;
  2. Give the widgets that violate the column width a tmpPosition (the gridster auto calculated position)
  3. When the user resizes the screen larger and the widget no longer violates column width. Then delete tmpPosition and use its original position.
angular-gridster2": "3.15.0",

Widget Structure

{
  gridItem: {
    cols: 2,
    id: '-L6Nl9-e1CxJQzIXbl2O',
    position: {
      desktop: {
        x: 6,
        y: 0
      },
      phone: {
        x: -1,
        y: -1
      },
      tablet: {
        x: 0,
        y: 6
      },
      widescreen: {
        x: -1,
        y: -1
      }
    },
    rows: 2,
    x: 6,
    y: 0
  }
}
  constructor(private store: Store<AppStore>) {
    // the window Observable is throttled, maybe we should use debounce or delay
    // could play around with this some more.
    this.windowSub = Observable.fromEvent(window, 'resize')
      .throttleTime(200)
      // .delay(200)
      // .debounceTime(700)
      .subscribe(event => {
        this.computeColWidth();
      });
  }

  /** computeColWidth - compute gridsters columns based on minColWidth
   *  This function will utilized the fixed columnWidth and compare it against gridsters
   *  it will detect which screensize based on a mobile browser check
   *  then autoposition the values if need be without collision
   **/
  private computeColWidth() {
    // check the inner width compute the columns based on our minimum col width
    const width = window.innerWidth;
    const minColWidth = this.options.fixedColWidth;
    const gridColWidth = this.gridster.curColWidth;
    const cols = this.gridster.columns;
    const columns = Math.floor(width / minColWidth);

    // enter a mobile check. Tablet users may want a separate orientation
    // maybe set mobile breakpoint to number | function
    if (width >= 1024 && !this.mobileCheck()) {
      this.screenSize = 'desktop';
    }
    if (width < 1024 && this.mobileCheck()) {
      this.screenSize = 'tablet';
    }

    // now we override gridsters columns
    if (columns !== this.gridster.columns) {
      this.options.maxCols = columns;
      this.options.minCols = columns;
      if (this.options.api) this.options.api.optionsChanged();

      // Now go through our dashboard and delete any tmpPositions
      // original positions is first detected to preserve anti collision
      this.dashboard.map((item: DashterItem) => {
        if (
          item.gridItem.position['tmpPos'] && // delete tmp position if real pos fits
          !(
            item.gridItem.cols + item.gridItem.position[this.screenSize].x >=
            columns
          )
        ) {
          delete item.gridItem.position['tmpPos'];
        }
      });

      // when the user changes the column sizes we will manually add a tmpPosition
      // tmpPosition is based upon gridster's getNextPossiblePosition
      this.gridster.grid.forEach((item: GridsterItemComponent, i) => {
        const idx = findIndex(this.dashboard, ['id', item.item.id]);
        if (item.$item.cols + item.item.position[this.screenSize].x >=columns) {
          // this is meant to correctly auto position item. Not
          this.options.api.getNextPossiblePosition(item.$item);
          this.dashboard[idx].gridItem.position['tmpPos'] = item.$item;
        }
      });
    }
  }

Gridster config

  // This will be moved to ngrx store for initial state
  options: GridsterConfig = {
    gridType: 'verticalFixed',
    compactType: 'none',
    mobileBreakpoint: 750,
    margin: 8,
    outerMargin: true,
    scrollSpeed: 10,
    itemInitCallback: (i, c) => this.itemInitCallback(i, c),
    defaultItemCols: 2,
    defaultItemRows: 2,
    fixedColWidth: 180,
    fixedRowHeight: 180,
    displayGrid: 'onDrag&Resize',
    keepFixedWidthInMobile: false,
    keepFixedHeightInMobile: true,
    disablePushOnResize: false,
    draggable: {
      enabled: true,
      ignoreContent: true, // if true drag will start only from elements from `dragHandleClass`
      dragHandleClass: 'drag-handle', // drag event only from this class. If `ignoreContent` is true.
      stop: (p, q, r) => this.updateGridItems() // callback when dragging an item stops.  Accepts Promise return to cancel/approve drag.
    },
    resizable: {
      enabled: false
    },
    pushItems: true,
    swap: true, // allow items to switch position if drop on top of another
    disablePushOnDrag: false, // disable push on drag
    pushDirections: { north: true, east: false, south: false, west: false }, // control the directions items are pushed
    pushResizeItems: false, // on resize of item will shrink adjacent items
    disableWindowResize: true, // disable the window on resize listener. This will stop grid to recalculate on window resize.
    disableWarnings: true,
    scrollToNewItems: true // scroll to new items placed in a scrollable view
  };

smoore2386 avatar Mar 15 '18 22:03 smoore2386

The code above doesnt work for me because my widgets are added dynamically and I cannot hard-code the x/y coordinates like above.

A simpler way I've found for responsive tiles without hard-coded positions:

 private computeColWidth() {
         const width = this.gridster.el.clientWidth;
         const columns = Math.floor(width / this.options.fixedColWidth);

         if (columns !== this.options.maxCols) {
            this.options.maxCols = columns;
            this.options.minCols = columns;

            this.options.api.optionsChanged();

            this.gridster.grid.forEach((comp: GridsterItemComponent) => {
              this.gridster.autoPositionItem(comp);
          });
       }
}

parliament718 avatar Sep 01 '20 18:09 parliament718

@parliament718 @smoore2386 don't suppose either of you have a demo of what you managed to create?

danwhite-ipc avatar Feb 10 '21 18:02 danwhite-ipc

Hi @parliament718 how could you use autoPositionItem function? I don't know how instance this "this.gridster"

Thanks!

ianninirojas avatar Sep 13 '22 20:09 ianninirojas

Hi @tiberiuzuld

I want to recalculate all the (x , y) of the grid items, after dynamically updating the grid columns, so that they adjust to the next available position, I try this.options.api?.getNextPossiblePosition but it places them all in the same position, it seems that it will not place one first and then calculate the position of the next, is it possible to do it?

ianninirojas avatar Sep 19 '22 12:09 ianninirojas

For completeness, this.gridster refers to the GridsterComponent.

An easy way to access the GridsterComponent is ViewChild.

In the parent component use:

export class MyComponent {
    @ViewChild('gridster') gridster: GridsterComponent

Then mark your gridster component with the template reference variable #gridster:

<gridster #gridster [options]="options">

Hope this helps a fellow angular beginner working with this repo,

rod7760 avatar Nov 17 '22 20:11 rod7760