Resize widgets feature. Closes #15.

By popular demand, now gridster allows to change the size of widgets dynamically.

Usage.
- gridster_api.resize_wiget($widget, [size_x], [size_y]);

E.g. (try in gridster.net):
    - gridster.resize_widget($('.gs_w:eq(3)'), 6);
    - gridster.resize_widget($('.gs_w:eq(3)'), 1, 4);

To do this, two new methods were added to gridster:

    - empty_cells(col, row, size_x, size_y, $exclude)
      Move down widgets in cells represented by the arguments col, row, size_x,
      size_y.

    - remove_empty_cells(col, row, size_x, size_y, $exclude)
      Move up widgets below cells represented by the arguments col, row, size_x,
      size_y.

Also, add_widget method supports specifying a row and column. This was easy
through empty_cells method. Related to #24. Thanks to @parkerd for the help!

The code should be improved and make it more reusable when the feature of
"move widgets dynamically" is added (related to #20).
This commit is contained in:
vieron
2012-08-20 01:31:34 +02:00
parent 010444f57c
commit dbf98cc9bd
+179 -83
View File
@@ -16,13 +16,14 @@
min_cols: 1,
min_rows: 15,
max_size_x: 6,
max_size_y: 6,
autogenerate_stylesheet: true,
avoid_overlapped_widgets: true,
serialize_params: function($w, wgd) {
return {
col: wgd.col,
row: wgd.row
row: wgd.row,
size_x: wgd.size_x,
size_y: wgd.size_y
};
},
collision: {},
@@ -56,8 +57,6 @@
* @param {Number} [options.min_rows] The minimum required rows.
* @param {Number} [options.max_size_x] The maximum number of columns
* that a widget can span.
* @param {Number} [options.max_size_y] The maximum number of rows
* that a widget can span.
* @param {Boolean} [options.autogenerate_stylesheet] If true, all the
* CSS required to position all widgets in their respective columns
* and rows will be generated automatically and injected to the
@@ -142,17 +141,22 @@
* @method add_widget
* @param {String|HTMLElement} html The string representing the HTML of the widget
* or the HTMLElement.
* @param {Number} size_x The nº of rows the widget occupies horizontally.
* @param {Number} size_y The nº of columns the widget occupies vertically.
* @param {Number} [size_x] The nº of rows the widget occupies horizontally.
* @param {Number} [size_y] The nº of columns the widget occupies vertically.
* @param {Number} [col] The column the widget should start in.
* @param {Number} [row] The row the widget should start in.
* @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing.
* the widget that was just created.
*/
fn.add_widget = function(html, size_x, size_y, col, row) {
var pos;
size_x || (size_x = 1);
size_y || (size_y = 1);
if (!col & !row) {
var pos = this.next_position(size_x, size_y);
pos = this.next_position(size_x, size_y);
}else{
var pos = {
pos = {
col: col,
row: row
};
@@ -171,11 +175,7 @@
this.register_widget($w);
this.add_faux_rows(next_pos.size_y);
if (this.options.autogenerate_stylesheet) {
this.generate_stylesheet();
}
this.add_faux_rows(pos.size_y);
this.set_dom_grid_height();
@@ -198,79 +198,127 @@
var wgd = $widget.coords().grid;
size_x || (size_x = wgd.size_x);
size_y || (size_y = wgd.size_y);
if (size_x > this.cols) {
size_x = this.cols;
};
var old_cells_occupied = this.get_cells_occupied(wgd);
var old_size_x = wgd.size_x;
var old_size_y = wgd.size_y;
var old_col = wgd.col;
var new_col = old_col;
var wider = size_x > old_size_x;
var taller = size_y > old_size_y;
var $nexts = this.widgets_below({
col: wgd.col,
row: wgd.row,
size_x: size_x,
size_y: size_y
});
wgd.size_x = size_x;
wgd.size_y = size_y;
if (wgd.col+wgd.size_x > this.cols) {
var diff = wgd.col+wgd.size_x-this.cols;
var c = wgd.col - diff;
wgd.col = c > 1 ? c : 1;
if (old_col + size_x - 1 > this.cols) {
var diff = old_col + (size_x - 1) - this.cols;
var c = old_col - diff;
new_col = Math.max(1, c);
};
this.$player = this.$preview_holder = $widget;
this.player_grid_data = {
col: wgd.col,
row: wgd.row,
size_x: size_x,
size_y: size_y,
el: $widget
}
this.placeholder_grid_data = $.extend({}, this.player_grid_data);
$widget.attr({
'data-sizex': size_x,
'data-sizey': size_y
})
this.set_player(wgd.col, wgd.row, true);
this.placeholder_grid_data = this.player_grid_data = {};
this.remove_from_gridmap({
col: old_col,
row: wgd.row,
size_x: old_size_x,
size_y: old_size_y
});
this.add_to_gridmap({
col: wgd.col,
var new_grid_data = {
col: new_col,
row: wgd.row,
size_x: size_x,
size_y: size_y
}, $widget);
};
// if (!taller || !wider) {
$nexts.each($.proxy(function(i, widget) {
this.move_widget_up(
$(widget), old_size_y - size_y);
}, this));
// };
var new_cells_occupied = this.get_cells_occupied(new_grid_data);
this.set_dom_grid_height();
var empty_cols = [];
$.each(old_cells_occupied.cols, function(i, col) {
if ($.inArray(col, new_cells_occupied.cols) === -1) {
empty_cols.push(col);
};
});
var occupied_cols = [];
$.each(new_cells_occupied.cols, function(i, col) {
if ($.inArray(col, old_cells_occupied.cols) === -1) {
occupied_cols.push(col);
}
});
var empty_rows = [];
$.each(old_cells_occupied.rows, function(i, row) {
if ($.inArray(row, new_cells_occupied.rows) === -1) {
empty_rows.push(row);
};
});
var occupied_rows = [];
$.each(new_cells_occupied.rows, function(i, row) {
if ($.inArray(row, old_cells_occupied.rows) === -1) {
occupied_rows.push(row);
};
});
this.remove_from_gridmap(wgd);
if (occupied_cols.length) {
var cols_to_empty = [
new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget
];
this.empty_cells.apply(this, cols_to_empty);
};
if (occupied_rows.length) {
var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget];
this.empty_cells.apply(this, rows_to_empty);
};
wgd.col = new_col;
wgd.size_x = size_x;
wgd.size_y = size_y;
this.add_to_gridmap(new_grid_data, $widget);
if (size_y > old_size_y) {
this.add_faux_rows(size_y - old_size_y);
};
$widget.attr({
'data-col': new_col,
'data-sizex': size_x,
'data-sizey': size_y
});
if (empty_cols.length) {
var cols_to_remove_holes = [
empty_cols[0], wgd.row,
empty_cols.length,
Math.min(old_size_y, size_y),
$widget
];
this.remove_empty_cells.apply(this, cols_to_remove_holes);
}
if (empty_rows.length) {
var rows_to_remove_holes = [
new_col, wgd.row, size_x, size_y, $widget
];
this.remove_empty_cells.apply(this, rows_to_remove_holes);
}
return $widget;
};
fn.empty_cells = function(col, row, size_x, size_y) {
/**
* Move down widgets in cells represented by the arguments col, row, size_x,
* size_y
*
* @method empty_cells
* @param {Number} col The column where the group of cells begin.
* @param {Number} row The row where the group of cells begin.
* @param {Number} size_x The number of columns that the group of cells
* occupy.
* @param {Number} size_y The number of rows that the group of cells
* occupy.
* @param {HTMLElement} $exclude Exclude widgets from being moved.
* @return {Class} Returns the instance of the Gridster Class.
*/
fn.empty_cells = function(col, row, size_x, size_y, $exclude) {
var $nexts = this.widgets_below({
col: col,
row: row - size_y,
@@ -278,17 +326,51 @@
size_y: size_y
});
$nexts.filter(function() {
console.log($(this).coords().grid.row, row + size_y -1 );
return $(this).coords().grid.row <= (row + size_y - 1);
}).each($.proxy(function(i, w) {
this.move_widget_down($(w), size_y);
$nexts.not($exclude).each($.proxy(function(i, w) {
var wgd = $(w).coords().grid;
if (!(wgd.row <= (row + size_y - 1))) { return; };
var diff = (row + size_y) - wgd.row;
this.move_widget_down($(w), diff);
}, this));
this.set_dom_grid_height();
return this;
};
/**
* Move up widgets below cells represented by the arguments col, row, size_x,
* size_y.
*
* @method remove_empty_cells
* @param {Number} col The column where the group of cells begin.
* @param {Number} row The row where the group of cells begin.
* @param {Number} size_x The number of columns that the group of cells
* occupy.
* @param {Number} size_y The number of rows that the group of cells
* occupy.
* @param {HTMLElement} $exclude Exclude widgets from being moved.
* @return {Class} Returns the instance of the Gridster Class.
*/
fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) {
var $nexts = this.widgets_below({
col: col,
row: row,
size_x: size_x,
size_y: size_y
});
$nexts.not(exclude).each($.proxy(function(i, widget) {
this.move_widget_up( $(widget), size_y );
}, this));
this.set_dom_grid_height();
return this;
}
/**
* Get the most left column below to add a new widget.
*
@@ -680,7 +762,13 @@
}
this.$preview_holder.remove();
this.$player = null;
this.$helper = null;
this.placeholder_grid_data = {};
this.player_grid_data = {};
this.cells_occupied_by_placeholder = {};
this.cells_occupied_by_player = {};
this.set_dom_grid_height();
};
@@ -862,6 +950,11 @@
*/
fn.sort_by_row_asc = function(widgets) {
widgets = widgets.sort(function(a, b) {
if (!a.row) {
a = $(a).coords().grid;
b = $(b).coords().grid;
};
if (a.row > b.row) {
return 1;
}
@@ -1123,7 +1216,7 @@
* @return {HTMLElement} Returns a jQuery collection of HTMLElements
*/
fn.get_widgets_under_player = function() {
var cells = this.cells_occupied_by_player;
var cells = this.cells_occupied_by_player || {cols: [], rows: []};
var $widgets = $([]);
$.each(cells.cols, $.proxy(function(i, col) {
@@ -1712,14 +1805,12 @@
var $nexts = $([]);
this.for_each_column_occupied(el_grid_data, function(col) {
self.for_each_widget_below(col, next_row,
function(tcol, trow) {
if (!self.is_player(this) &&
$.inArray(this, $nexts) === -1) {
$nexts = $nexts.add(this);
return true; // break
}
});
self.for_each_widget_below(col, next_row, function(tcol, trow) {
if (!self.is_player(this) && $.inArray(this, $nexts) === -1) {
$nexts = $nexts.add(this);
return true; // break
}
});
});
return this.sort_by_row_asc($nexts);
@@ -1767,6 +1858,7 @@
this.for_each_column_occupied(el_grid_data, function(col) {
var $w = this.is_widget(col, prev_row);
if (this.is_occupied(col, prev_row) ||
this.is_player(col, prev_row) ||
this.is_placeholder_in(col, prev_row) ||
@@ -2108,7 +2200,6 @@
*/
fn.generate_stylesheet = function(opts) {
var styles = '';
var max_size_y = this.options.max_size_y;
var max_size_x = this.options.max_size_x;
var max_rows = 0;
var max_cols = 0;
@@ -2152,7 +2243,7 @@
((i + 1) * opts.widget_margins[1]) ) + 'px;} ');
}
for (var y = 1; y <= max_size_y; y++) {
for (var y = 1; y <= opts.rows; y++) {
styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' +
(y * opts.widget_base_dimensions[1] +
(y - 1) * (opts.widget_margins[1] * 2)) + 'px;}');
@@ -2218,7 +2309,7 @@
/**
* Add cell to the faux grid.
*
* @method add_faux_rows
* @method add_faux_cell
* @param {Number} row The row for the new faux cell.
* @param {Number} col The col for the new faux cell.
* @return {Object} Returns the instance of the Gridster class.
@@ -2260,6 +2351,11 @@
};
this.rows = max_rows;
if (this.options.autogenerate_stylesheet) {
this.generate_stylesheet();
}
return this;
};