From b1eb1da93da4f1d34a5ded19abfcf16523ef703a Mon Sep 17 00:00:00 2001 From: Alain Dumesny Date: Thu, 5 Nov 2020 12:31:30 -0800 Subject: [PATCH] margin support CSS '5px 10px 0 20px' format * can now specify multiple margin values like CSS for all sides --- doc/CHANGES.md | 1 + doc/README.md | 10 +++++--- spec/gridstack-spec.ts | 42 +++++++++++++++++++++++++++++++- src/gridstack.ts | 55 ++++++++++++++++++++++++++++-------------- src/types.ts | 8 +++--- src/utils.ts | 6 ++--- 6 files changed, 93 insertions(+), 29 deletions(-) diff --git a/doc/CHANGES.md b/doc/CHANGES.md index 5200a0f2a..9f59b5bb6 100644 --- a/doc/CHANGES.md +++ b/doc/CHANGES.md @@ -46,6 +46,7 @@ Change log - add `data-gs-static-grid` attribute - fix getting DOM element by id with number works (api that uses `GridStackElement` handle more string formats) - fix setting `marginTop` (or any 4 sides) to cause resize to break. Thanks [@deadivan](https://github.com/deadivan) for suggested fix. +- add `margin` support multi CSS format `'5px 10px 0 20px'` or `'5em 10em'` ## 2.1.0 (2020-10-28) diff --git a/doc/README.md b/doc/README.md index 94b87fa91..9d9e5c7e7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -384,7 +384,7 @@ Return list of GridItem HTML dom elements (excluding temporary placeholder) ### getMargin() -returns current margin value. +returns current margin value (undefined if all 4 sides don't match). ### isAreaEmpty(x, y, width, height) @@ -423,9 +423,11 @@ grid.makeWidget('#gsi-1'); ### margin(value: numberOrString) -set the top/right/bottom/left margin between grid item and content. Parameters: -- `value` - new margin value. see `cellHeight` for possible value formats. -Note: you can instead use `marginTop | marginBottom | marginLeft | marginRight` so set the sides separately. +gap between grid item and content (default?: 10). This will set all 4 sides and support the CSS formats below + - an `integer` (px) + - a string with possible units (ex: `'5'`, `'2em'`, `'20px'`, `'2rem'`) + - string with space separated values (ex: `'5px 10px 0 20px'` for all 4 sides, or `'5em 10em'` for top/bottom and left/right pairs like CSS). + - Note: all sides must have same units (last one wins, default px) ### maxHeight(el, val) diff --git a/spec/gridstack-spec.ts b/spec/gridstack-spec.ts index bd609b713..a34881272 100644 --- a/spec/gridstack-spec.ts +++ b/spec/gridstack-spec.ts @@ -1204,7 +1204,7 @@ describe('gridstack', function() { let grid: any = GridStack.init(options); expect(grid.getMargin()).toBe(5); spyOn(grid, '_updateStyles'); - grid.margin('5px', false); + grid.margin('5px'); expect(grid._updateStyles).not.toHaveBeenCalled(); expect(grid.getMargin()).toBe(5); }); @@ -1237,6 +1237,46 @@ describe('gridstack', function() { expect(grid.opts.marginLeft).toBe(5); expect(grid.opts.marginRight).toBe(5); }); + it('init 2 values', function() { + let options = { + cellHeight: 80, + margin: '5px 10' + }; + let grid: any = GridStack.init(options); + expect(grid.getMargin()).toBe(undefined); + expect(grid.opts.marginTop).toBe(5); + expect(grid.opts.marginBottom).toBe(5); + expect(grid.opts.marginLeft).toBe(10); + expect(grid.opts.marginRight).toBe(10); + }); + it('init 4 values', function() { + let options = { + cellHeight: 80, + margin: '1 2 0em 3' + }; + let grid: any = GridStack.init(options); + expect(grid.getMargin()).toBe(undefined); + expect(grid.opts.marginTop).toBe(1); + expect(grid.opts.marginRight).toBe(2); + expect(grid.opts.marginBottom).toBe(0); + expect(grid.opts.marginLeft).toBe(3); + }); + it('set 2 values, should update style', function() { + let options = { + cellHeight: 80, + margin: 5 + }; + let grid = GridStack.init(options); + expect(grid.getMargin()).toBe(5); + spyOn(grid as any, '_updateStyles'); + grid.margin('1px 0'); + expect((grid as any)._updateStyles).toHaveBeenCalled(); + expect(grid.getMargin()).toBe(undefined); + expect(grid.opts.marginTop).toBe(1); + expect(grid.opts.marginBottom).toBe(1); + expect(grid.opts.marginLeft).toBe(0); + expect(grid.opts.marginRight).toBe(0); + }); }); describe('grid.opts.rtl', function() { diff --git a/src/gridstack.ts b/src/gridstack.ts index 860abecdf..1ff7542ca 100644 --- a/src/gridstack.ts +++ b/src/gridstack.ts @@ -7,7 +7,7 @@ */ import { GridStackEngine } from './gridstack-engine'; -import { obsoleteOpts, obsoleteOptsDel, obsoleteAttr, obsolete, Utils } from './utils'; +import { obsoleteOpts, obsoleteOptsDel, obsoleteAttr, obsolete, Utils, HeightData } from './utils'; import { GridItemHTMLElement, GridStackWidget, GridStackNode, GridStackOptions, numberOrString, ColumnOptions } from './types'; import { GridStackDD } from './gridstack-dd'; @@ -1021,27 +1021,27 @@ export class GridStack { } /** - * Updates the margins which will set all 4 sides at once - see `GridStackOptions.margin` for format options. - * @param value new vertical margin value - * Note: you can instead use `marginTop | marginBottom | marginLeft | marginRight` GridStackOptions to set the sides separately. + * Updates the margins which will set all 4 sides at once - see `GridStackOptions.margin` for format options (CSS string format of 1,2,4 values or single number). + * @param value margin value */ public margin(value: numberOrString): GridStack { - let data = Utils.parseHeight(value); - if (this.opts.marginUnit === data.unit && this.opts.margin === data.height) { - return; - } - this.opts.marginUnit = data.unit; - this.opts.marginTop = - this.opts.marginBottom = - this.opts.marginLeft = - this.opts.marginRight = - this.opts.margin = data.height; + let isMultiValue = (typeof value === 'string' && value.split(' ').length > 1); + // check if we can skip re-creating our CSS file... won't check if multi values (too much hassle) + if (!isMultiValue) { + let data = Utils.parseHeight(value); + if (this.opts.marginUnit === data.unit && this.opts.margin === data.height) return; + } + // re-use existing margin handling + this.opts.margin = value; + this.opts.marginTop = this.opts.marginBottom = this.opts.marginLeft = this.opts.marginRight = undefined; + this.initMargin(); + this._updateStyles(true); // true = force re-create return this; } - /** returns current margin value (undefined if all 4 sides don't match) */ + /** returns current margin number value (undefined if 4 sides don't match) */ public getMargin(): number { return this.opts.margin as number; } /** @@ -1818,9 +1818,28 @@ export class GridStack { /** @internal initialize margin top/bottom/left/right and units */ private initMargin(): GridStack { - let data = Utils.parseHeight(this.opts.margin); - this.opts.marginUnit = data.unit; - let margin = this.opts.margin = data.height; + + let data: HeightData; + let margin = 0; + + // support passing multiple values like CSS (ex: '5px 10px 0 20px') + let margins: string[] = []; + if (typeof this.opts.margin === 'string') { + margins = this.opts.margin.split(' ') + } + if (margins.length === 2) { // top/bot, left/right like CSS + this.opts.marginTop = this.opts.marginBottom = margins[0]; + this.opts.marginLeft = this.opts.marginRight = margins[1]; + } else if (margins.length === 4) { // Clockwise like CSS + this.opts.marginTop = margins[0]; + this.opts.marginRight = margins[1]; + this.opts.marginBottom = margins[2]; + this.opts.marginLeft = margins[3]; + } else { + data = Utils.parseHeight(this.opts.margin); + this.opts.marginUnit = data.unit; + margin = this.opts.margin = data.height; + } // see if top/bottom/left/right need to be set as well if (this.opts.marginTop === undefined) { diff --git a/src/types.ts b/src/types.ts index c004d6065..cc2a41fe0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -100,13 +100,15 @@ export interface GridStackOptions { itemClass?: string; /** - * gap size between grid item and content (default?: 10). see also marginTop, marginRight,... Can be: + * gap between grid item and content (default?: 10). This will set all 4 sides and support the CSS formats below * an integer (px) - * a string (ex: '2em', '20px', '2rem') + * a string with possible units (ex: '2em', '20px', '2rem') + * string with space separated values (ex: '5px 10px 0 20px' for all 4 sides, or '5em 10em' for top/bottom and left/right pairs like CSS). + * Note: all sides must have same units (last one wins, default px) */ margin?: numberOrString; - /** optional way to specify each individual margin side - default to margin */ + /** OLD way to optionally set each side - use margin: '5px 10px 0 20px' instead. Used internally to store each side. */ marginTop?: numberOrString; marginRight?: numberOrString; marginBottom?: numberOrString; diff --git a/src/utils.ts b/src/utils.ts index 1e9a2dc89..5d64313a5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -140,18 +140,18 @@ export class Utils { static parseHeight(val: numberOrString): HeightData { let height: number; - let heightUnit = 'px'; + let unit = 'px'; if (typeof val === 'string') { let match = val.match(/^(-[0-9]+\.[0-9]+|[0-9]*\.[0-9]+|-[0-9]+|[0-9]+)(px|em|rem|vh|vw|%)?$/); if (!match) { throw new Error('Invalid height'); } - heightUnit = match[2] || 'px'; + unit = match[2] || 'px'; height = parseFloat(match[1]); } else { height = val; } - return { height, unit: heightUnit } + return { height, unit }; } /** copies unset fields in target to use the given default sources values */