mirror of
https://github.com/stylersnico/librenms.git
synced 2026-08-01 08:03:30 +02:00
feat(gridster): drag-and-drop widget resizing
This commit is contained in:
+58
-2
@@ -47,7 +47,8 @@
|
||||
transition: left .3s, top .3s!important;
|
||||
}
|
||||
|
||||
.gridster .dragging {
|
||||
.gridster .dragging,
|
||||
.gridster .resizing {
|
||||
z-index: 10!important;
|
||||
-webkit-transition: all 0s !important;
|
||||
-moz-transition: all 0s !important;
|
||||
@@ -55,7 +56,62 @@
|
||||
transition: all 0s !important;
|
||||
}
|
||||
|
||||
|
||||
.gs-resize-handle {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gs-resize-handle-both {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
bottom: -8px;
|
||||
right: -8px;
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=');
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
cursor: se-resize;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.gs-resize-handle-x {
|
||||
top: 0;
|
||||
bottom: 13px;
|
||||
right: -5px;
|
||||
width: 10px;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.gs-resize-handle-y {
|
||||
left: 0;
|
||||
right: 13px;
|
||||
bottom: -5px;
|
||||
height: 10px;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.gs-w:hover .gs-resize-handle,
|
||||
.resizing .gs-resize-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gs-resize-handle,
|
||||
.gs-w.dragging .gs-resize-handle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gs-resize-disabled .gs-resize-handle {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
[data-max-sizex="1"] .gs-resize-handle-x,
|
||||
[data-max-sizey="1"] .gs-resize-handle-y,
|
||||
[data-max-sizey="1"][data-max-sizex="1"] .gs-resize-handle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Uncomment this if you set helper : "clone" in draggable options */
|
||||
/*.gridster .player {
|
||||
opacity:0;
|
||||
}*/
|
||||
}
|
||||
*/
|
||||
+309
-11
@@ -32,6 +32,13 @@
|
||||
draggable: {
|
||||
items: '.gs-w',
|
||||
distance: 4
|
||||
},
|
||||
resize: {
|
||||
enabled: false,
|
||||
axes: ['x', 'y', 'both'],
|
||||
handle_append_to: '',
|
||||
handle_class: 'gs-resize-handle',
|
||||
max_size: [Infinity, Infinity]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,6 +87,19 @@
|
||||
* @param {Object} [options.draggable] An Object with all options for
|
||||
* Draggable class you want to overwrite. See Draggable docs for more
|
||||
* info.
|
||||
* @param {Object} [options.resize] An Object with resize config
|
||||
* options.
|
||||
* @param {Boolean} [options.resize.enabled] Set to true to enable
|
||||
* resizing.
|
||||
* @param {Array} [options.resize.axes] Axes in which widgets can be
|
||||
* resized. Possible values: ['x', 'y', 'both'].
|
||||
* @param {String} [options.resize.handle_append_to] Set a valid CSS
|
||||
* selector to append resize handles to.
|
||||
* @param {String} [options.resize.handle_class] CSS class name used
|
||||
* by resize handles.
|
||||
* @param {Array} [options.resize.max_size] Limit widget dimensions
|
||||
* when resizing. Array values should be integers:
|
||||
* `[max_cols_occupied, max_rows_occupied]`
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
@@ -87,7 +107,8 @@
|
||||
this.options = $.extend(true, defaults, options);
|
||||
this.$el = $(el);
|
||||
this.$wrapper = this.$el.parent();
|
||||
this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs-w');
|
||||
this.$widgets = this.$el.children(
|
||||
this.options.widget_selector).addClass('gs-w');
|
||||
this.widgets = [];
|
||||
this.$changed = $([]);
|
||||
this.wrapper_width = this.$wrapper.width();
|
||||
@@ -106,11 +127,13 @@
|
||||
var fn = Gridster.prototype;
|
||||
|
||||
fn.init = function() {
|
||||
this.options.resize.enabled && this.setup_resize();
|
||||
this.generate_grid_and_stylesheet();
|
||||
this.get_widgets_from_DOM();
|
||||
this.set_dom_grid_height();
|
||||
this.$wrapper.addClass('ready');
|
||||
this.draggable();
|
||||
this.options.resize.enabled && this.resizable();
|
||||
|
||||
$(window).bind('resize.gridster', throttle(
|
||||
$.proxy(this.recalculate_faux_grid, this), 200));
|
||||
@@ -142,6 +165,33 @@
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Disables drag-and-drop widget resizing.
|
||||
*
|
||||
* @method disable
|
||||
* @return {Class} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.disable_resize = function() {
|
||||
this.$el.addClass('gs-resize-disabled');
|
||||
this.resize_api.disable();
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables drag-and-drop widget resizing.
|
||||
*
|
||||
* @method enable
|
||||
* @return {Class} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.enable_resize = function() {
|
||||
this.$el.removeClass('gs-resize-disabled');
|
||||
this.resize_api.enable();
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add a new widget to the grid.
|
||||
*
|
||||
@@ -152,10 +202,11 @@
|
||||
* @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.
|
||||
* @param {Array} [max_size] max_size Maximun size (in units) for width and height.
|
||||
* @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) {
|
||||
fn.add_widget = function(html, size_x, size_y, col, row, max_size) {
|
||||
var pos;
|
||||
size_x || (size_x = 1);
|
||||
size_y || (size_y = 1);
|
||||
@@ -185,14 +236,56 @@
|
||||
this.add_faux_rows(pos.size_y);
|
||||
//this.add_faux_cols(pos.size_x);
|
||||
|
||||
if (max_size) {
|
||||
this.set_widget_max_size($w, max_size);
|
||||
}
|
||||
|
||||
this.set_dom_grid_height();
|
||||
|
||||
return $w.fadeIn();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Change widget size limits.
|
||||
*
|
||||
* @method set_widget_max_size
|
||||
* @param {HTMLElement|Number} $widget The jQuery wrapped HTMLElement
|
||||
* representing the widget or an index representing the desired widget.
|
||||
* @param {Array} max_size Maximun size (in units) for width and height.
|
||||
* @return {HTMLElement} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.set_widget_max_size = function($widget, max_size) {
|
||||
$widget = typeof $widget === 'number' ?
|
||||
this.$widgets.eq($widget) : $widget;
|
||||
|
||||
/**
|
||||
if (!$widget.length) { return this; }
|
||||
|
||||
var wgd = $widget.data('coords').grid;
|
||||
wgd.max_size_x = max_size[0];
|
||||
wgd.max_size_y = max_size[1];
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Append the resize handle into a widget.
|
||||
*
|
||||
* @method add_resize_handle
|
||||
* @param {HTMLElement} $widget The jQuery wrapped HTMLElement
|
||||
* representing the widget.
|
||||
* @return {HTMLElement} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.add_resize_handle = function($w) {
|
||||
var append_to = this.options.resize.handle_append_to;
|
||||
$(this.resize_handle_tpl).appendTo( append_to ? $(append_to, $w) : $w);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Change the size of a widget. Width is limited to the current grid width.
|
||||
*
|
||||
* @method resize_widget
|
||||
@@ -200,11 +293,16 @@
|
||||
* representing the widget.
|
||||
* @param {Number} size_x The number of columns that will occupy the widget.
|
||||
* @param {Number} size_y The number of rows that will occupy the widget.
|
||||
* @param {Function} callback Function executed when the widget is removed.
|
||||
* @param {Boolean} [reposition] Set to false to not move the widget to
|
||||
* the left if there is insufficient space on the right.
|
||||
* By default <code>size_x</code> is limited to the space available from
|
||||
* the column where the widget begins, until the last column to the right.
|
||||
* @param {Function} [callback] Function executed when the widget is removed.
|
||||
* @return {HTMLElement} Returns $widget.
|
||||
*/
|
||||
fn.resize_widget = function($widget, size_x, size_y, callback) {
|
||||
fn.resize_widget = function($widget, size_x, size_y, reposition, callback) {
|
||||
var wgd = $widget.coords().grid;
|
||||
reposition !== false && (reposition = true);
|
||||
size_x || (size_x = wgd.size_x);
|
||||
size_y || (size_y = wgd.size_y);
|
||||
|
||||
@@ -216,7 +314,7 @@
|
||||
var old_col = wgd.col;
|
||||
var new_col = old_col;
|
||||
|
||||
if (old_col + size_x - 1 > this.cols) {
|
||||
if (reposition && 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);
|
||||
@@ -245,8 +343,17 @@
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mutate widget dimensions and position in the grid map.
|
||||
*
|
||||
* @method mutate_widget_in_gridmap
|
||||
* @param {HTMLElement} $widget The jQuery wrapped HTMLElement
|
||||
* representing the widget to mutate.
|
||||
* @param {Object} wgd Current widget grid data (col, row, size_x, size_y).
|
||||
* @param {Object} new_wgd New widget grid data.
|
||||
* @return {HTMLElement} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.mutate_widget_in_gridmap = function($widget, wgd, new_wgd) {
|
||||
|
||||
var old_size_x = wgd.size_x;
|
||||
var old_size_y = wgd.size_y;
|
||||
|
||||
@@ -303,6 +410,8 @@
|
||||
|
||||
this.add_to_gridmap(new_wgd, $widget);
|
||||
|
||||
$widget.removeClass('player-revert');
|
||||
|
||||
//update coords instance attributes
|
||||
$widget.data('coords').update({
|
||||
width: (new_wgd.size_x * this.options.widget_base_dimensions[0] +
|
||||
@@ -558,12 +667,13 @@
|
||||
* @return {Array} Returns the instance of the Gridster class.
|
||||
*/
|
||||
fn.register_widget = function($el) {
|
||||
|
||||
var wgd = {
|
||||
'col': parseInt($el.attr('data-col'), 10),
|
||||
'row': parseInt($el.attr('data-row'), 10),
|
||||
'size_x': parseInt($el.attr('data-sizex'), 10),
|
||||
'size_y': parseInt($el.attr('data-sizey'), 10),
|
||||
'max_size_x': parseInt($el.attr('data-max-sizex'), 10) || false,
|
||||
'max_size_y': parseInt($el.attr('data-max-sizey'), 10) || false,
|
||||
'el': $el
|
||||
};
|
||||
|
||||
@@ -571,8 +681,7 @@
|
||||
!this.can_move_to(
|
||||
{size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row)
|
||||
) {
|
||||
wgd = this.next_position(wgd.size_x, wgd.size_y);
|
||||
wgd.el = $el;
|
||||
$.extend(wgd, this.next_position(wgd.size_x, wgd.size_y));
|
||||
$el.attr({
|
||||
'data-col': wgd.col,
|
||||
'data-row': wgd.row,
|
||||
@@ -583,12 +692,13 @@
|
||||
|
||||
// attach Coord object to player data-coord attribute
|
||||
$el.data('coords', $el.coords());
|
||||
|
||||
// Extend Coord object with grid position info
|
||||
$el.data('coords').grid = wgd;
|
||||
|
||||
this.add_to_gridmap(wgd, $el);
|
||||
|
||||
this.options.resize.enabled && this.add_resize_handle($el);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -661,6 +771,8 @@
|
||||
var draggable_options = $.extend(true, {}, this.options.draggable, {
|
||||
offset_left: this.options.widget_margins[0],
|
||||
container_width: this.container_width,
|
||||
ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON',
|
||||
'.' + this.options.resize.handle_class],
|
||||
start: function(event, ui) {
|
||||
self.$widgets.filter('.player-revert')
|
||||
.removeClass('player-revert');
|
||||
@@ -688,6 +800,50 @@
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bind resize events to get resize working.
|
||||
*
|
||||
* @method resizable
|
||||
* @return {Class} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.resizable = function() {
|
||||
this.resize_api = this.$el.drag({
|
||||
items: '.' + this.options.resize.handle_class,
|
||||
offset_left: this.options.widget_margins[0],
|
||||
container_width: this.container_width,
|
||||
move_element: false,
|
||||
start: $.proxy(this.on_start_resize, this),
|
||||
stop: $.proxy(function(event, ui) {
|
||||
delay($.proxy(function() {
|
||||
this.on_stop_resize(event, ui);
|
||||
}, this), 120);
|
||||
}, this),
|
||||
drag: throttle($.proxy(this.on_resize, this), 60)
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Setup things required for resizing. Like build templates for drag handles.
|
||||
*
|
||||
* @method setup_resize
|
||||
* @return {Class} Returns instance of gridster Class.
|
||||
*/
|
||||
fn.setup_resize = function() {
|
||||
this.resize_handle_class = this.options.resize.handle_class;
|
||||
var axes = this.options.resize.axes;
|
||||
var handle_tpl = '<span class="' + this.resize_handle_class + ' ' +
|
||||
this.resize_handle_class + '-{type}" />';
|
||||
|
||||
this.resize_handle_tpl = $.map(axes, function(type) {
|
||||
return handle_tpl.replace('{type}', type);
|
||||
}).join('');
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function is executed when the player begins to be dragged.
|
||||
*
|
||||
@@ -842,6 +998,148 @@
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This function is executed every time a widget starts to be resized.
|
||||
*
|
||||
* @method on_start_resize
|
||||
* @param {Event} event The original browser event
|
||||
* @param {Object} ui A prepared ui object with useful drag-related data
|
||||
*/
|
||||
fn.on_start_resize = function(event, ui) {
|
||||
this.$resized_widget = ui.$player.closest('.gs-w');
|
||||
this.resize_coords = this.$resized_widget.coords();
|
||||
this.resize_wgd = this.resize_coords.grid;
|
||||
this.resize_initial_width = this.resize_coords.coords.width;
|
||||
this.resize_initial_height = this.resize_coords.coords.height;
|
||||
this.resize_initial_sizex = this.resize_coords.grid.size_x;
|
||||
this.resize_initial_sizey = this.resize_coords.grid.size_y;
|
||||
this.resize_last_sizex = this.resize_initial_sizex;
|
||||
this.resize_last_sizey = this.resize_initial_sizey;
|
||||
this.resize_max_size_x = Math.min(this.resize_wgd.max_size_x ||
|
||||
this.options.resize.max_size[0], this.cols - this.resize_wgd.col + 1);
|
||||
this.resize_max_size_y = this.resize_wgd.max_size_y ||
|
||||
this.options.resize.max_size[1];
|
||||
|
||||
this.resize_dir = {
|
||||
right: ui.$player.is('.' + this.resize_handle_class + '-x'),
|
||||
bottom: ui.$player.is('.' + this.resize_handle_class + '-y')
|
||||
};
|
||||
|
||||
this.$resized_widget.css({
|
||||
'min-width': this.options.widget_base_dimensions[0],
|
||||
'min-height': this.options.widget_base_dimensions[1]
|
||||
});
|
||||
|
||||
var nodeName = this.$resized_widget.get(0).tagName;
|
||||
this.$resize_preview_holder = $('<' + nodeName + ' />', {
|
||||
'class': 'preview-holder resize-preview-holder',
|
||||
'data-row': this.$resized_widget.attr('data-row'),
|
||||
'data-col': this.$resized_widget.attr('data-col'),
|
||||
'css': {
|
||||
'width': this.resize_initial_width,
|
||||
'height': this.resize_initial_height
|
||||
}
|
||||
}).appendTo(this.$el);
|
||||
|
||||
this.$resized_widget.addClass('resizing');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function is executed every time a widget stops being resized.
|
||||
*
|
||||
* @method on_stop_resize
|
||||
* @param {Event} event The original browser event
|
||||
* @param {Object} ui A prepared ui object with useful drag-related data
|
||||
*/
|
||||
fn.on_stop_resize = function(event, ui) {
|
||||
this.$resized_widget
|
||||
.removeClass('resizing')
|
||||
.css({
|
||||
'width': '',
|
||||
'height': ''
|
||||
});
|
||||
|
||||
delay($.proxy(function() {
|
||||
this.$resize_preview_holder
|
||||
.remove()
|
||||
.css({
|
||||
'min-width': '',
|
||||
'min-height': ''
|
||||
});
|
||||
}, this), 300);
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is executed when a widget is being resized.
|
||||
*
|
||||
* @method on_resize
|
||||
* @param {Event} event The original browser event
|
||||
* @param {Object} ui A prepared ui object with useful drag-related data
|
||||
*/
|
||||
fn.on_resize = function(event, ui) {
|
||||
var rel_x = (ui.pointer.diff_left);
|
||||
var rel_y = (ui.pointer.diff_top);
|
||||
var wbd_x = this.options.widget_base_dimensions[0];
|
||||
var wbd_y = this.options.widget_base_dimensions[1];
|
||||
var max_width = Infinity;
|
||||
var max_height = Infinity;
|
||||
|
||||
var inc_units_x = Math.ceil((rel_x /
|
||||
(this.options.widget_base_dimensions[0] +
|
||||
this.options.widget_margins[0] * 2)) - 0.2);
|
||||
|
||||
var inc_units_y = Math.ceil((rel_y /
|
||||
(this.options.widget_base_dimensions[1] +
|
||||
this.options.widget_margins[1] * 2)) - 0.2);
|
||||
|
||||
var size_x = Math.max(1, this.resize_initial_sizex + inc_units_x);
|
||||
var size_y = Math.max(1, this.resize_initial_sizey + inc_units_y);
|
||||
|
||||
size_x = Math.min(size_x, this.resize_max_size_x);
|
||||
max_width = (this.resize_max_size_x * wbd_x) +
|
||||
((size_x - 1) * this.options.widget_margins[0] * 2);
|
||||
|
||||
size_y = Math.min(size_y, this.resize_max_size_y);
|
||||
max_height = (this.resize_max_size_y * wbd_y) +
|
||||
((size_y - 1) * this.options.widget_margins[1] * 2);
|
||||
|
||||
|
||||
if (this.resize_dir.right) {
|
||||
size_y = this.resize_initial_sizey;
|
||||
} else if (this.resize_dir.bottom) {
|
||||
size_x = this.resize_initial_sizex;
|
||||
}
|
||||
|
||||
var css_props = {};
|
||||
!this.resize_dir.bottom && (css_props.width = Math.min(
|
||||
this.resize_initial_width + rel_x, max_width));
|
||||
!this.resize_dir.right && (css_props.height = Math.min(
|
||||
this.resize_initial_height + rel_y, max_height));
|
||||
|
||||
this.$resized_widget.css(css_props);
|
||||
|
||||
if (size_x !== this.resize_last_sizex ||
|
||||
size_y !== this.resize_last_sizey) {
|
||||
|
||||
this.resize_widget(this.$resized_widget, size_x, size_y, false);
|
||||
|
||||
this.$resize_preview_holder.css({
|
||||
'width': '',
|
||||
'height': ''
|
||||
}).attr({
|
||||
'data-row': this.$resized_widget.attr('data-row'),
|
||||
'data-sizex': size_x,
|
||||
'data-sizey': size_y
|
||||
});
|
||||
}
|
||||
|
||||
this.resize_last_sizex = size_x;
|
||||
this.resize_last_sizey = size_y;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the callbacks passed as arguments when a column begins to be
|
||||
* overlapped or stops being overlapped.
|
||||
|
||||
Reference in New Issue
Block a user