angular-gridster2
angular-gridster2 copied to clipboard
Grid responsiveness for different device sizes (xl, lg, md, sm, xs)
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.
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
- When the window is resized calculate the column width;
- Give the widgets that violate the column width a tmpPosition (the gridster auto calculated position)
- 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
};
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 @smoore2386 don't suppose either of you have a demo of what you managed to create?
Hi @parliament718 how could you use autoPositionItem function? I don't know how instance this "this.gridster"
Thanks!
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?
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,