From cc354114e686c16a0699c2eca00d0b1ad16f0352 Mon Sep 17 00:00:00 2001 From: nexiumbiz-debug <265948173+nexiumbiz-debug@users.noreply.github.com> Date: Sun, 14 Jun 2026 05:10:23 -0400 Subject: [PATCH] fix: clamp loaded item dimensions --- .../angular-gridster2/src/lib/gridster.ts | 19 +++ .../src/lib/tests/gridster.spec.ts | 115 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 projects/angular-gridster2/src/lib/tests/gridster.spec.ts diff --git a/projects/angular-gridster2/src/lib/gridster.ts b/projects/angular-gridster2/src/lib/gridster.ts index 5f199490..f59d4ca1 100644 --- a/projects/angular-gridster2/src/lib/gridster.ts +++ b/projects/angular-gridster2/src/lib/gridster.ts @@ -365,6 +365,9 @@ export class Gridster implements OnInit, OnDestroy { item.rows = $item.rows; itemComponent.itemChanged(); } + if (this.clampItemSizeToLimits($item, item)) { + itemComponent.itemChanged(); + } if ($item.x === -1 || $item.y === -1) { this.autoPositionItem(itemComponent); } else if (this.checkCollision($item)) { @@ -401,6 +404,22 @@ export class Gridster implements OnInit, OnDestroy { } } + private clampItemSizeToLimits($item: GridsterItemConfig, item: GridsterItemConfig): boolean { + const $options = this.$options(); + const minItemCols = $item.minItemCols === undefined ? $options.minItemCols : $item.minItemCols; + const maxItemCols = $item.maxItemCols === undefined ? $options.maxItemCols : $item.maxItemCols; + const minItemRows = $item.minItemRows === undefined ? $options.minItemRows : $item.minItemRows; + const maxItemRows = $item.maxItemRows === undefined ? $options.maxItemRows : $item.maxItemRows; + const cols = Math.min(Math.max($item.cols, minItemCols), maxItemCols); + const rows = Math.min(Math.max($item.rows, minItemRows), maxItemRows); + if ($item.cols === cols && $item.rows === rows) { + return false; + } + $item.cols = item.cols = cols; + $item.rows = item.rows = rows; + return true; + } + checkCollision(item: GridsterItemConfig, checkRatio?: boolean): GridsterItem | boolean { let collision: GridsterItem | boolean = false; const options = this.options(); diff --git a/projects/angular-gridster2/src/lib/tests/gridster.spec.ts b/projects/angular-gridster2/src/lib/tests/gridster.spec.ts new file mode 100644 index 00000000..0cd2478b --- /dev/null +++ b/projects/angular-gridster2/src/lib/tests/gridster.spec.ts @@ -0,0 +1,115 @@ +import { NO_ERRORS_SCHEMA, provideZonelessChangeDetection, signal } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Gridster } from '../gridster'; +import { CompactType, DisplayGrid, GridType } from '../gridsterConfig'; +import { GridsterItem } from '../gridsterItem'; +import { GridsterItemConfig } from '../gridsterItemConfig'; +import { GridsterPreview } from '../gridsterPreview'; + +function createItemComponent(item: GridsterItemConfig) { + const $item = { ...item }; + const itemChanged = vi.fn(); + const itemComponent = { + $item: () => $item, + item: () => item, + itemChanged, + setSize: vi.fn(), + drag: { + toggle: vi.fn() + }, + resize: { + toggle: vi.fn() + }, + notPlaced: false + } as unknown as GridsterItem; + + return { $item, itemChanged, itemComponent }; +} + +describe('gridster component', () => { + let fixture: ComponentFixture; + let gridsterComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + imports: [Gridster, GridsterItem, GridsterPreview], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + + fixture = TestBed.createComponent(Gridster); + gridsterComponent = fixture.componentInstance; + gridsterComponent.options = signal({ + gridType: GridType.Fixed, + compactType: CompactType.None, + margin: 10, + outerMargin: true, + outerMarginTop: null, + outerMarginRight: null, + outerMarginBottom: null, + outerMarginLeft: null, + useTransformPositioning: true, + mobileBreakpoint: 640, + minCols: 1, + maxCols: 100, + minRows: 1, + maxRows: 100, + maxItemCols: 100, + minItemCols: 1, + maxItemRows: 100, + minItemRows: 1, + maxItemArea: 2500, + minItemArea: 1, + defaultItemCols: 1, + defaultItemRows: 1, + fixedColWidth: 105, + fixedRowHeight: 105, + keepFixedHeightInMobile: false, + keepFixedWidthInMobile: false, + scrollSensitivity: 10, + scrollSpeed: 20, + + enableEmptyCellClick: false, + enableEmptyCellContextMenu: false, + enableEmptyCellDrop: true, + enableEmptyCellDrag: false, + enableOccupiedCellDrop: false, + + emptyCellDragMaxCols: 50, + emptyCellDragMaxRows: 50, + + ignoreMarginInRow: false, + draggable: { + enabled: true + }, + resizable: { + enabled: true + }, + swap: true, + pushItems: true, + disablePushOnDrag: false, + disablePushOnResize: false, + pushDirections: { north: true, east: true, south: true, west: true }, + pushResizeItems: false, + displayGrid: DisplayGrid.OnDragAndResize, + disableWindowResize: false, + disableWarnings: true, + scrollToNewItems: false + }); + }); + + it('clamps loaded item dimensions to item min and max limits', () => { + const item = { x: 0, y: 0, cols: 1, rows: 9, minItemCols: 3, maxItemRows: 4 }; + const { $item, itemChanged, itemComponent } = createItemComponent(item); + + gridsterComponent.addItem(itemComponent); + + expect(item.cols).toBe(3); + expect(item.rows).toBe(4); + expect($item.cols).toBe(3); + expect($item.rows).toBe(4); + expect(itemChanged).toHaveBeenCalledTimes(1); + expect(itemComponent.notPlaced).toBe(false); + }); +});