Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-grid-layout

Package Overview
Dependencies
Maintainers
1
Versions
105
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-grid-layout - npm Package Compare versions

Comparing version 0.0.2 to 0.1.0

examples/1-basic.html

141

lib/GridItem.jsx
'use strict';
var React = require('react/addons');
var utils = require('./utils');
var Draggable = require('react-draggable');
var Resizable = require('react-resizable').Resizable;

@@ -32,2 +34,4 @@ var GridItem = module.exports = React.createClass({

onDrag: React.PropTypes.func,
onResizeStop: React.PropTypes.func,
onResizeStart: React.PropTypes.func,
onResize: React.PropTypes.func,

@@ -53,2 +57,9 @@

getInitialState() {
return {
resizing: false,
className: ''
};
},
/**

@@ -66,5 +77,9 @@ * Return position on the page given an x, y, w, h.

top: p.rowHeight * p.y + p.margin[1],
width: (width * p.w / p.cols) - ((p.w - 1) * p.margin[0]),
width: width * (p.w / p.cols) - p.margin[0],
height: p.h * p.rowHeight - p.margin[1]
};
if (this.state.resizing) {
out.width = this.state.resizing.width;
out.height = this.state.resizing.height;
}
return out;

@@ -89,11 +104,40 @@ },

/**
* Given a resize handle, figure out a new w and h for this element.
* @param {DOMElement} element DOM Element (resize handle)
* @return {Object} w and h.
*/
calcWH(element) {
calcWH({height, width}) {
var w = Math.round((width / this.props.containerWidth) * this.props.cols);
var h = Math.round(height / this.props.rowHeight);
w = Math.max(Math.min(w, this.props.cols - this.props.x), 0);
h = Math.max(h, 0);
return {w, h};
},
mixinDraggable(child, position) {
return (
<Draggable
start={{x: position.left, y: position.top}}
moveOnStartChange={this.props.moveOnStartChange}
onStop={this.onDragHandler('onDragStop')}
onStart={this.onDragHandler('onDragStart')}
onDrag={this.onDragHandler('onDrag')}
handle={this.props.handle}
cancel=".react-resizable-handle"
>
{child}
</Draggable>
);
},
mixinResizable(child, position) {
return (
<Resizable
width={position.width}
height={position.height}
onResizeStop={this.onResizeHandler('onResizeStop')}
onResizeStart={this.onResizeHandler('onResizeStart')}
onResize={this.onResizeHandler('onResize')}
>
{child}
</Resizable>
);
},
/**

@@ -107,3 +151,3 @@ * Wrapper around drag events to provide more useful data.

*/
dragHandler(handlerName) {
onDragHandler(handlerName) {
var me = this;

@@ -125,4 +169,4 @@ return function(e, {element, position}) {

/**
* Wrapper around resize events to provide more useful data.
* All resize events call the function with the given handler name,
* Wrapper around drag events to provide more useful data.
* All drag events call the function with the given handler name,
* with the signature (index, x, y).

@@ -133,9 +177,10 @@ *

*/
resizeHandler(handlerName, element) {
onResizeHandler(handlerName) {
var me = this;
return function(e, {element, position}) {
return function(e, {element, size}) {
if (!me.props[handlerName]) return;
// Get new WH
var {w, h} = me.calcWH(element);
// Get new XY
var {w, h} = me.calcWH(size);
// Cap w at numCols

@@ -146,2 +191,4 @@ if (w + me.props.x > me.props.cols) {

me.setState({resizing: handlerName === 'onResizeStop' ? null : size});
me.props[handlerName](me.props.i, w, h);

@@ -152,12 +199,12 @@ };

render() {
var child = React.Children.only(this.props.children);
var p = this.props, pos = this.calcPosition();
var p = this.props;
var {left, top, width, height} = this.calcPosition();
child = React.addons.cloneWithProps(child, {
var child = React.addons.cloneWithProps(React.Children.only(this.props.children), {
// Munge a classname. Use passed in classnames, child classnames, and resizing.
className: ['react-grid-item', this.props.children.props.className || '', this.props.className,
this.state.resizing ? 'resizing' : ''].join(' '),
// We can set the width and height on the child, but unfortunately we can't set the position.
style: {
width: width + 'px',
height: height + 'px',
width: pos.width + 'px',
height: pos.height + 'px',
position: 'absolute'

@@ -170,54 +217,22 @@ }

if (!this.isMounted()) {
child.props.style.left = perc(this.props.x / p.containerWidth);
child.props.style.width = perc(this.props.width / p.containerWidth);
pos.left = utils.perc(pos.left / p.containerWidth);
child.props.style.width = utils.perc(pos.width / p.containerWidth);
}
child.props.className = 'react-grid-item ' + (child.props.className || "") + " " + this.props.className;
// Resizable support. This is usually on but the user can toggle it off.
if (this.props.isResizable && this.isMounted()) {
child.props.children = [
child.props.children,
<Draggable
start={{x: width - 20 + 'px', y: height - 20 + 'px'}}
moveOnStartChange={true}
onStop={this.resizeHandler('onResizeStop')}
onStart={this.resizeHandler('onResizeStart')}
onDrag={this.resizeHandler('onResize')}
>
<span className="react-grid-resize-handle">⌟</span>
</Draggable>
];
child = this.mixinResizable(child, pos);
}
// Draggable support. This is always on, except for with placeholders.
if (this.props.isDraggable) {
return (
<Draggable
start={{x: left, y: top}}
moveOnStartChange={this.props.moveOnStartChange}
onStop={this.dragHandler('onDragStop')}
onStart={this.dragHandler('onDragStart')}
onDrag={this.dragHandler('onDrag')}
handle={this.props.handle}
cancel=".react-grid-resize-handle"
>
{child}
</Draggable>
);
} else {
child.props.style.left = left, child.props.style.top = top;
return child;
child = this.mixinDraggable(child, pos);
}
// Place the element directly if draggability is turned off.
else {
child.props.style.left = pos.left, child.props.style.top = pos.top;
}
return child;
}
});
/**
* Helper to convert a number to a percentage string.
* @param {Number} num Any number
* @return {String} That number as a percentage.
*/
function perc(num) {
return num * 100 + '%';
}

@@ -5,2 +5,3 @@ 'use strict';

var GridItem = require('./GridItem.jsx');
var utils = require('./utils');

@@ -12,3 +13,3 @@ var ReactGridLayout = module.exports = React.createClass({

propTypes: {
// If true, the container swells and contracts to fit contents
// If true, the container height swells and contracts to fit contents
autoSize: React.PropTypes.bool,

@@ -29,3 +30,10 @@ // {name: pxVal}, e.g. {lg: 1200, md: 996, sm: 768, xs: 480}

// Rows have a static height, but you can change this based on breakpoints if you like
rowHeight: React.PropTypes.number
rowHeight: React.PropTypes.number,
// Flags
isDraggable: React.PropTypes.bool,
isResizable: React.PropTypes.bool,
// Callback so you can save the layout
onLayoutChange: React.PropTypes.func
},

@@ -40,3 +48,6 @@

initialWidth: 1280,
margin: [10, 10]
margin: [10, 10],
isDraggable: true,
isResizable: true,
onLayoutChange: function(){}
};

@@ -55,10 +66,17 @@ },

componentDidMount() {
window.addEventListener('resize', this.onResize);
this.onResize();
window.addEventListener('resize', this.onWindowResize);
this.onWindowResize();
},
componentWillUnmount() {
window.removeEventListener('resize', this.onResize);
window.removeEventListener('resize', this.onWindowResize);
},
componentDidUpdate(prevProps, prevState) {
// Call back so we can store the layout
if (this.state.layout !== prevState.layout) {
this.props.onLayoutChange(this.state.layout);
}
},
/**

@@ -92,3 +110,5 @@ * Calculates a pixel value for the container.

}
return compact(layout);
layout = utils.correctBounds(layout, {w: this.props.cols});
return utils.compact(layout);
},

@@ -106,3 +126,3 @@

*/
onResize() {
onWindowResize() {
// Set breakpoint

@@ -119,3 +139,3 @@ var width = this.getDOMNode().offsetWidth;

var layout = this.state.layout;
var l = getLayoutItem(layout, i);
var l = utils.getLayoutItem(layout, i);

@@ -128,6 +148,6 @@ // Create drag element (display only)

// Move the element to the dragged location.
layout = moveElement(layout, l, x, y);
layout = utils.moveElement(layout, l, x, y);
this.setState({
layout: compact(layout),
layout: utils.compact(layout),
activeDrag: activeDrag

@@ -144,10 +164,30 @@ });

var layout = this.state.layout;
var l = getLayoutItem(layout, i);
var l = utils.getLayoutItem(layout, i);
// Move the element here
layout = moveElement(layout, l, x, y);
layout = utils.moveElement(layout, l, x, y);
// Set state
this.setState({layout: compact(layout), activeDrag: null});
this.setState({layout: utils.compact(layout), activeDrag: null});
},
onResize(i, w, h) {
var layout = this.state.layout;
var l = utils.getLayoutItem(layout, i);
// Create drag element (display only)
var activeDrag = {
w: w, h: h, x: l.x, y: l.y, placeholder: true, i: i
};
l.w = w;
l.h = h;
// Move the element to the dragged location.
// layout = utils.moveElement(layout, l, x, y);
this.setState({layout: utils.compact(layout), activeDrag: activeDrag});
},
onResizeStop(e, {element, position}) {
this.setState({activeDrag: null});
},
/**

@@ -158,3 +198,3 @@ * Create a placeholder object.

placeholder() {
if (!this.state.activeDrag) return null;
if (!this.state.activeDrag) return '';

@@ -184,3 +224,3 @@ return (

processGridItem(child, i) {
var l = getLayoutItem(this.state.layout, i);
var l = utils.getLayoutItem(this.state.layout, i);

@@ -202,3 +242,8 @@ // watchStart property tells Draggable to react to changes in the start param

onDragStart={this.onDragStart}
onDrag={this.onDrag}>
onDrag={this.onDrag}
onResize={this.onResize}
onResizeStop={this.onResizeStop}
isDraggable={this.props.isDraggable}
isResizable={this.props.isResizable}
>
{child}

@@ -212,3 +257,3 @@ </GridItem>

var {className, initialLayout, ...props} = this.props;
className = 'react-grid-layout ' + (className || '');
className = 'react-grid-layout ' + (className || '') + ' ' + this.state.className;

@@ -224,139 +269,1 @@ return (

/**
* Given two layouts, check if they collide.
* @param {Object} l1 Layout object.
* @param {Object} l2 Layout object.
* @return {Boolean} True if colliding.
*/
function collides(l1, l2) {
if (l1 === l2) return false; // same element
if (l1.x + l1.w <= l2.x) return false; // l1 is left of l2
if (l1.x >= l2.x + l2.w) return false; // l1 is right of l2
if (l1.y + l1.h <= l2.y) return false; // l1 is above l2
if (l1.y >= l2.y + l2.h) return false; // l1 is below l2
return true; // boxes overlap
}
/**
* Given a layout, compact it. This involves going down each y coordinate and removing gaps
* between items.
* @param {Array} layout Layout.
* @return {Array} Compacted Layout.
*/
function compact(layout) {
// We go through the items by row and column.
var sorted = getLayoutItemsByRowCol(layout);
var out = _.map(getLayoutItemsByRowCol(layout), function(l, i) {
// Only collide with elements before this one.
var ls = sorted.slice(0, i);
// Move the element up as far as it can go without colliding.
do {
l.y--;
}
while (l.y > -1 && !layoutItemCollidesWith(ls, l).length);
// Move it down, and keep moving it down if it's colliding.
do {
l.y++;
} while(layoutItemCollidesWith(ls, l).length);
delete l.moved;
return l;
});
return _.sortBy(out, 'i');
}
/**
* Get a layout item by index. Used so we can override later on if necessary.
*
* @param {Array} layout Layout array.
* @param {Number} i Index
* @return {LayoutItem} Item at index.
*/
function getLayoutItem(layout, i) {
return layout[i];
}
/**
* Get layout items sorted from top left to right and down.
* @return {Array} Array of layout objects.
*/
function getLayoutItemsByRowCol(layout) {
return [].concat(layout).sort(function(a, b) {
if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
return 1;
}
return -1;
});
}
/**
* Get layout items sorted from top left to down.
* @return {Array} Array of layout objects.
*/
function getLayoutItemsByColRow(layout) {
return [].concat(layout).sort(function(a, b) {
if (a.x > b.x || a.x === b.x && a.y > b.y) {
return 1;
}
return -1;
});
}
/**
* Returns an array of items this layout item collides with.
* @param {Object} layoutItem Layout item.
* @return {Array} Array of colliding layout objects.
*/
function layoutItemCollidesWith(layout, layoutItem) {
return _.filter(layout, collides.bind(null, layoutItem));
}
/**
* Move / resize an element. Responsible for doing cascading movements of other elements.
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} l element to move.
* @param {Number} [x] X position in grid units.
* @param {Number} [y] Y position in grid units.
* @param {Number} [w] Width in grid units.
* @param {Number} [h] Height in grid units.
*/
function moveElement(layout, l, x, y, w, h) {
// _.pick trickery removes undefined values from the object so we don't overwrite
// the object with attrs we didn't pass
_.extend(l, _.pick({x: x, y: y, w: w, h: h, moved: 1}, _.isNumber));
// Get all items this box collides with.
var collisions = layoutItemCollidesWith(layout, l);
// Move each item that collides away from this element.
_.each(collisions, function(coll) {
if (coll.moved) return; // short circuit so we don't re-move items
layout = moveElementAwayFromCollision(layout, l, coll);
});
return layout;
}
/**
* This is where the magic needs to happen - given a collision, move an element away from the collision.
* It's okay to cascade movements here, but be careful to not have a move b move c move a.
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} collidesWith Layout item we're colliding with.
* @param {LayoutItem} itemToMove Layout item we're moving.
*/
function moveElementAwayFromCollision(layout, collidesWith, itemToMove) {
var fakeItem = _.extend({}, itemToMove, {y: 0});
var sorted = getLayoutItemsByRowCol(layout);
var itemsBefore = sorted.slice(0, sorted.indexOf(itemToMove)).concat(collidesWith);
// While the item collides with any of the items before it, move it down.
var collisions;
do {
collisions = layoutItemCollidesWith(itemsBefore, fakeItem);
if (collisions.length) fakeItem.y = collisions[0].y + collisions[0].h;
} while(collisions.length);
return moveElement(layout, itemToMove, undefined, fakeItem.y);
}
{
"name": "react-grid-layout",
"version": "0.0.2",
"version": "0.1.0",
"description": "A draggable and resizable grid layout with responsive breakpoints, for React.",

@@ -9,3 +9,3 @@ "main": "index.js",

"build": "./node_modules/.bin/webpack",
"dev-server": "echo 'Open http://localhost:4002/examples/1.html'; webpack-dev-server --config webpack-dev-server.config.js --hot --progress --colors --port 4002 --content-base ."
"dev-server": "echo 'Open http://localhost:4002'; webpack-dev-server --config webpack-dev-server.config.js --hot --progress --colors --port 4002 --content-base ."
},

@@ -32,2 +32,7 @@ "repository": {

"homepage": "https://github.com/STRML/react-grid-layout",
"dependencies": {
"lodash": "^2.4.1",
"react-draggable": "strml/react-draggable",
"react-resizable": "^0.0.3"
},
"devDependencies": {

@@ -34,0 +39,0 @@ "css-loader": "^0.9.0",

### React-Grid-Layout
[View the Demo](https://strml.github.io/react-grid-layout/examples/1.html)
[View the Demo](https://strml.github.io/react-grid-layout/examples/1-basic.html)

@@ -19,2 +19,7 @@ React-Grid-Layout is a grid layout system much like [Packery](http://packery.metafizzy.co/) or

#### Demos
[1. Basic](https://strml.github.io/react-grid-layout/examples/1-basic.html)
[1. No Dragging/Resizing (Layout Only)](https://strml.github.io/react-grid-layout/examples/2-no-dragging.html)
----

@@ -29,3 +34,3 @@

- [x] Live grid packing while dragging
- [x] Resizable grid items
- [ ] Define grid attributes on children themselves (`_grid` key)
- [ ] Resizable grid items

@@ -6,3 +6,4 @@ 'use strict';

var contentDiv = document.getElementById('content');
React.render(React.createElement(Layout), contentDiv);
var gridProps = window.gridProps || {};
React.render(React.createElement(Layout, gridProps), contentDiv);
});

@@ -8,2 +8,4 @@ 'use strict';

require('style!css!../css/styles.css');
require('style!css!../examples/example-styles.css');
require('style!css!../node_modules/react-resizable/css/styles.css');

@@ -13,4 +15,18 @@ var TestLayout = module.exports = React.createClass({

generate() {
return _.map(_.range(12), function(i) {
getDefaultProps() {
return {
items: 12
};
},
getInitialState() {
var layout = this.props.layout || this.generateLayout();
return {
layout: layout,
initialLayout: layout
};
},
generateDOM() {
return _.map(_.range(this.props.items), function(i) {
return (<div key={i}><span className="text">{i}</span></div>);

@@ -20,14 +36,39 @@ });

generateLayout() {
var p = this.props;
return _.map(new Array(p.items), function(item, i) {
var w = _.result(p, 'w') || Math.ceil(Math.random() * 4);
var y = _.result(p, 'y') || Math.ceil(Math.random() * 4) + 1;
return {x: i * 2 % 12, y: Math.floor(i / 6) * y, w: w, h: y, i: i};
});
},
onLayoutChange: function(layout) {
console.log(layout);
this.setState({layout: layout});
},
stringifyLayout() {
return _.map(this.state.layout, function(l) {
return <div className="layoutItem"><b>{l.i}</b>: [{l.x}, {l.y}, {l.w}, {l.h}]</div>;
});
},
render() {
var items = this.generate();
var layout = _.map(items, function(item, i) {
var y = Math.ceil(Math.random() * 8) + 1;
return {x: i * 2 % 12, y: Math.floor(i / 6) * y, w: 2, h: y};
});
var {layout, ...gridProps} = this.props;
return (
<ReactGridLayout className="layout" initialLayout={layout} cols={12} rowHeight={30}>
{this.generate()}
</ReactGridLayout>
<div>
<div className="layoutJSON">
Displayed as <code>[x, y, w, h]</code>:
<div className="columns">
{this.stringifyLayout()}
</div>
</div>
<ReactGridLayout className="layout" initialLayout={this.state.initialLayout} cols={12} onLayoutChange={this.onLayoutChange}
rowHeight={30} {...gridProps}>
{this.generateDOM()}
</ReactGridLayout>
</div>
);
}
});

@@ -21,2 +21,3 @@ module.exports = {

devtool: "#inline-source-map",
publicPath: '/examples/',
resolve: {

@@ -23,0 +24,0 @@ extensions: ["", ".webpack.js", ".web.js", ".js", ".jsx"]

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc