from collections import OrderedDict
import param
from ..io.resources import bundled_files
from ..reactive import ReactiveHTML
from ..util import classproperty
from .grid import GridSpec
class GridStack(ReactiveHTML, GridSpec):
"""
The GridStack layout builds on the GridSpec component and
gridstack.js to allow resizing and dragging items in the grid.
"""
allow_resize = param.Boolean(default=True, doc="""
Allow resizing the grid cells.""")
allow_drag = param.Boolean(default=True, doc="""
Allow dragging the grid cells.""")
state = param.List(doc="""
Current state of the grid (updated as items are resized and
dragged).""")
width = param.Integer(default=None)
height = param.Integer(default=None)
_extension_name = 'gridstack'
_template = """
{% for key, obj in objects.items() %}
{% endfor %}
""" # noqa
_scripts = {
'render': ["""
const options = {
column: data.ncols,
disableResize: !data.allow_resize,
disableDrag: !data.allow_drag,
margin: 0
}
if (data.nrows)
options.row = data.nrows
if (model.height)
options.cellHeight = Math.floor(model.height/data.nrows)
const gridstack = GridStack.init(options, grid);
function sync_state() {
const items = []
for (const node of gridstack.engine.nodes) {
items.push({id: node.el.getAttribute('data-id'), x0: node.x, y0: node.y, x1: node.x+node.w, y1: node.y+node.h})
}
data.state = items
}
gridstack.on('resizestop', (event, el) => {
window.dispatchEvent(new Event("resize"));
sync_state()
})
gridstack.on('dragstop', (event, el) => {
sync_state()
})
sync_state()
state.gridstack = gridstack
"""],
'allow_drag': ["state.gridstack.enableMove(data.allow_drag)"],
'allow_resize': ["state.gridstack.enableResize(data.allow_resize)"],
'ncols': ["state.gridstack.column(data.ncols)"],
'nrows': ["""
state.gristack.opts.row = data.nrows
if (data.nrows && model.height)
state.gridstack.cellHeight(Math.floor(model.height/data.nrows))
else
state.gridstack.cellHeight('auto')
"""]
}
__css_raw__ = [
'https://cdn.jsdelivr.net/npm/gridstack@4.2.5/dist/gridstack.min.css',
'https://cdn.jsdelivr.net/npm/gridstack@4.2.5/dist/gridstack-extra.min.css'
]
__javascript_raw__ = [
'https://cdn.jsdelivr.net/npm/gridstack@4.2.5/dist/gridstack-h5.js'
]
__js_require__ = {
'paths': {
'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@4.2.5/dist/gridstack-h5'
},
'exports': {
'gridstack': 'GridStack'
},
'shim': {
'gridstack': {
'exports': 'GridStack'
}
}
}
@classproperty
def __js_skip__(cls):
return {
'GridStack': cls.__javascript__[0:1],
}
_rename = {}
@classproperty
def __javascript__(cls):
return bundled_files(cls)
@classproperty
def __css__(cls):
return bundled_files(cls, 'css')
@param.depends('state', watch=True)
def _update_objects(self):
objects = OrderedDict()
object_ids = {str(id(obj)): obj for obj in self}
for p in self.state:
objects[(p['y0'], p['x0'], p['y1'], p['x1'])] = object_ids[p['id']]
self.objects.clear()
self.objects.update(objects)
self._update_sizing()
@param.depends('objects', watch=True)
def _update_sizing(self):
if self.ncols:
width = int(float(self.width)/self.ncols)
else:
width = 0
if self.nrows:
height = int(float(self.height)/self.nrows)
else:
height = 0
for i, ((y0, x0, y1, x1), obj) in enumerate(self.objects.items()):
x0 = 0 if x0 is None else x0
x1 = (self.ncols) if x1 is None else x1
y0 = 0 if y0 is None else y0
y1 = (self.nrows) if y1 is None else y1
h, w = y1-y0, x1-x0
if self.sizing_mode in ['fixed', None]:
properties = {'width': w*width, 'height': h*height}
else:
properties = {'sizing_mode': self.sizing_mode}
if 'width' in self.sizing_mode:
properties['height'] = h*height
elif 'height' in self.sizing_mode:
properties['width'] = w*width
obj.param.update(**{
k: v for k, v in properties.items()
if not obj.param[k].readonly
})