ux: gridstack poc

This commit is contained in:
Torkel Ödegaard
2017-06-13 16:10:56 -04:00
parent 35c440eb79
commit a6bbcb8fef
11 changed files with 4328 additions and 188 deletions

View File

@@ -7,25 +7,47 @@ import 'jquery-ui';
import 'gridstack';
import 'gridstack.jquery-ui';
export class DashGridCtrl {
}
const template = `
<div class="grid-stack">
<div class="grid-stack-item"
data-gs-x="0" data-gs-y="0"
data-gs-width="4" data-gs-height="2">
<div class="grid-stack-item-content"></div>
</div>
<div class="grid-stack-item"
data-gs-x="4" data-gs-y="0"
data-gs-width="4" data-gs-height="4">
<div class="grid-stack-item-content"></div>
</div>
<div gridstack gridstack-handler="ctrl.gridstack" class="grid-stack"
options="ctrl.options"
on-change="ctrl.onChange(event,items)"
on-drag-start="ctrl.onDragStart(event,ui)"
on-drag-stop="ctrl.onDragStop(event, ui)"
on-resize-start="ctrl.onResizeStart(event, ui)"
on-resize-stop="ctrl.onResizeStop(event, ui)">
<div gridstack-item ng-repeat="panel in ctrl.panels"
class="grid-stack-item"
gs-item-id="panel.id"
gs-item-x="panel.x"
gs-item-y="panel.y"
gs-item-width="panel.width"
gs-item-height="panel.height"
gs-item-autopos="1"
on-item-added="ctrl.onItemAdded(item)"
on-item-removed="ctrl.onItemRemoved(item)">
<plugin-component type="panel" class="panel-margin grid-stack-item-content">
</plugin-component>
</div>
</div>
`;
export function dashGrid() {
export class DashGridCtrl {
options: any;
/** @ngInject */
constructor(private $rootScope) {
this.options = {
animate: true,
};
}
onResizeStop() {
this.$rootScope.$broadcast('render');
}
}
export function dashGrid($timeout) {
return {
restrict: 'E',
template: template,
@@ -35,10 +57,156 @@ export function dashGrid() {
scope: {
dashboard: "="
},
link: function(scope, elem) {
$('.grid-stack').gridstack();
link: function(scope, elem, attrs, ctrl) {
ctrl.panels = [];
ctrl.dashboard.forEachPanel((panel, panelIndex, row, rowIndex) => {
panel.width = 4;
panel.height = 4;
panel.x = panelIndex * 4;
panel.y = rowIndex * 4;
ctrl.panels.push(panel);
});
}
};
}
/** @ngInject */
coreModule.controller('GridstackController', ['$scope', function($scope) {
var gridstack = null;
this.init = function(element, options) {
gridstack = element.gridstack(options).data('gridstack');
return gridstack;
};
this.removeItem = function(element) {
if (gridstack) {
return gridstack.removeWidget(element, false);
}
return null;
};
this.addItem = function(element) {
if (gridstack) {
gridstack.makeWidget(element);
return element;
}
return null;
};
}]);
/** @ngInject */
coreModule.directive('gridstack', ['$timeout', function($timeout) {
return {
restrict: "A",
controller: 'GridstackController',
scope: {
onChange: '&',
onDragStart: '&',
onDragStop: '&',
onResizeStart: '&',
onResizeStop: '&',
gridstackHandler: '=',
options: '='
},
link: function (scope, element, attrs, controller, ngModel) {
var gridstack = controller.init(element, scope.options);
scope.gridstackHandler = gridstack;
element.on('change', function (e, items) {
$timeout(function() {
scope.$apply();
scope.onChange({event: e, items: items});
});
});
element.on('dragstart', function(e, ui) {
scope.onDragStart({event: e, ui: ui});
});
element.on('dragstop', function(e, ui) {
$timeout(function() {
scope.$apply();
scope.onDragStop({event: e, ui: ui});
});
});
element.on('resizestart', function(e, ui) {
scope.onResizeStart({event: e, ui: ui});
});
element.on('resizestop', function(e, ui) {
$timeout(function() {
scope.$apply();
scope.onResizeStop({event: e, ui: ui});
});
});
}
};
}]);
/** @ngInject */
coreModule.directive('gridstackItem', ['$timeout', function($timeout) {
return {
restrict: "A",
controller: 'GridstackController',
require: '^gridstack',
scope: {
gridstackItem: '=',
onItemAdded: '&',
onItemRemoved: '&',
gsItemId: '=',
gsItemX: '=',
gsItemY: '=',
gsItemWidth: '=',
gsItemHeight: '=',
gsItemAutopos: '='
},
link: function (scope, element, attrs, controller) {
$(element).attr('data-gs-id', scope.gsItemId);
$(element).attr('data-gs-x', scope.gsItemX);
$(element).attr('data-gs-y', scope.gsItemY);
$(element).attr('data-gs-width', scope.gsItemWidth);
$(element).attr('data-gs-height', scope.gsItemHeight);
$(element).attr('data-gs-auto-position', scope.gsItemAutopos);
var widget = controller.addItem(element);
var item = element.data('_gridstack_node');
$timeout(function() {
scope.onItemAdded({item: item});
});
scope.$watch(function () { return $(element).attr('data-gs-id'); }, function (val) {
scope.gsItemId = val;
});
scope.$watch(function(){ return $(element).attr('data-gs-x'); }, function(val) {
scope.gsItemX = val;
});
scope.$watch(function(){ return $(element).attr('data-gs-y'); }, function(val) {
scope.gsItemY = val;
});
scope.$watch(function(){ return $(element).attr('data-gs-width'); }, function(val) {
scope.gsItemWidth = val;
});
scope.$watch(function(){ return $(element).attr('data-gs-height'); }, function(val) {
scope.gsItemHeight = val;
});
element.bind('$destroy', function() {
var item = element.data('_gridstack_node');
scope.onItemRemoved({item: item});
controller.removeItem(element);
});
}
};
}]);
coreModule.directive('dashGrid', dashGrid);

View File

@@ -46,7 +46,7 @@ import {contextSrv} from './services/context_srv';
import {KeybindingSrv} from './services/keybindingSrv';
import {helpModal} from './components/help/help';
import {NavModelSrv, NavModel} from './nav_model_srv';
import {DashGridCtrl} from './components/dashgrid/dashgrid';
import {dashGrid} from './components/dashgrid/dashgrid';
export {
arrayJoin,
@@ -72,5 +72,5 @@ export {
helpModal,
NavModelSrv,
NavModel,
DashGridCtrl,
dashGrid,
};

View File

@@ -160,18 +160,19 @@ export class PanelCtrl {
}
calculatePanelHeight() {
if (this.fullscreen) {
var docHeight = $(window).height();
var editHeight = Math.floor(docHeight * 0.4);
var fullscreenHeight = Math.floor(docHeight * 0.8);
this.containerHeight = this.editMode ? editHeight : fullscreenHeight;
} else {
this.containerHeight = this.panel.height || this.row.height;
if (_.isString(this.containerHeight)) {
this.containerHeight = parseInt(this.containerHeight.replace('px', ''), 10);
}
}
// if (this.fullscreen) {
// var docHeight = $(window).height();
// var editHeight = Math.floor(docHeight * 0.4);
// var fullscreenHeight = Math.floor(docHeight * 0.8);
// this.containerHeight = this.editMode ? editHeight : fullscreenHeight;
// } else {
// this.containerHeight = this.panel.height || this.row.height;
// if (_.isString(this.containerHeight)) {
// this.containerHeight = parseInt(this.containerHeight.replace('px', ''), 10);
// }
// }
const rowSpan = this.panel.height || 4;
this.containerHeight = rowSpan * 60 + ((rowSpan-1) * 20);
this.height = this.containerHeight - (PANEL_BORDER + PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
}

View File

@@ -26,7 +26,6 @@ var panelTemplate = `
<div class="panel-content">
<ng-transclude></ng-transclude>
</div>
<panel-resizer></panel-resizer>
</div>
<div class="panel-full-edit" ng-if="ctrl.editMode">

View File

@@ -1,10 +1,8 @@
define([
'angular',
'jquery',
'lodash',
'tether',
],
function (angular, $, _, Tether) {
function (angular, $) {
'use strict';
angular
@@ -17,139 +15,96 @@ function (angular, $, _, Tether) {
'<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>' +
'</span>';
function createMenuTemplate(ctrl) {
var template = '<div class="panel-menu small">';
if (ctrl.dashboard.meta.canEdit) {
template += '<div class="panel-menu-inner">';
template += '<div class="panel-menu-row">';
if (!ctrl.dashboard.meta.fullscreen) {
template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(-1)"><i class="fa fa-minus"></i></a>';
template += '<a class="panel-menu-icon pull-left" ng-click="ctrl.updateColumnSpan(1)"><i class="fa fa-plus"></i></a>';
}
template += '<a class="panel-menu-icon pull-right" ng-click="ctrl.removePanel()"><i class="fa fa-trash"></i></a>';
template += '<div class="clearfix"></div>';
template += '</div>';
}
template += '<div class="panel-menu-row">';
template += '<a class="panel-menu-link" gf-dropdown="extendedMenu"><i class="fa fa-bars"></i></a>';
_.each(ctrl.getMenu(), function(item) {
// skip edit actions if not editor
if (item.role === 'Editor' && !ctrl.dashboard.meta.canEdit) {
return;
}
template += '<a class="panel-menu-link" ';
if (item.click) { template += ' ng-click="' + item.click + '"'; }
if (item.href) { template += ' href="' + item.href + '"'; }
template += '>';
template += item.text + '</a>';
});
template += '</div>';
template += '</div>';
template += '</div>';
return template;
}
function getExtendedMenu(ctrl) {
return ctrl.getExtendedMenu();
}
return {
restrict: 'A',
link: function($scope, elem) {
var $link = $(linkTemplate);
var $panelContainer = elem.parents(".panel-container");
var menuScope = null;
var ctrl = $scope.ctrl;
var timeout = null;
var $menu = null;
var teather;
//var menuScope = null;
//var timeout = null;
//var $menu = null;
//var teather;
elem.append($link);
function dismiss(time, force) {
clearTimeout(timeout);
timeout = null;
// function dismiss(time, force) {
// clearTimeout(timeout);
// timeout = null;
//
// if (time) {
// timeout = setTimeout(dismiss, time);
// return;
// }
//
// // if hovering or draging pospone close
// if (force !== true) {
// if ($menu.is(':hover') || $scope.ctrl.dashboard.$$panelDragging) {
// dismiss(2200);
// return;
// }
// }
//
// if (menuScope) {
// teather.destroy();
// $menu.unbind();
// $menu.remove();
// menuScope.$destroy();
// menuScope = null;
// $menu = null;
// $panelContainer.removeClass('panel-highlight');
// }
// }
if (time) {
timeout = setTimeout(dismiss, time);
return;
}
// if hovering or draging pospone close
if (force !== true) {
if ($menu.is(':hover') || $scope.ctrl.dashboard.$$panelDragging) {
dismiss(2200);
return;
}
}
if (menuScope) {
teather.destroy();
$menu.unbind();
$menu.remove();
menuScope.$destroy();
menuScope = null;
$menu = null;
$panelContainer.removeClass('panel-highlight');
}
}
var showMenu = function(e) {
// if menu item is clicked and menu was just removed from dom ignore this event
if (!$.contains(document, e.target)) {
return;
}
if ($menu) {
dismiss();
return;
}
var menuTemplate;
menuTemplate = createMenuTemplate(ctrl);
$menu = $(menuTemplate);
$menu.mouseleave(function() {
dismiss(1000);
});
menuScope = $scope.$new();
menuScope.extendedMenu = getExtendedMenu(ctrl);
menuScope.dismiss = function() {
dismiss(null, true);
};
$(".panel-container").removeClass('panel-highlight');
$panelContainer.toggleClass('panel-highlight');
$('.panel-menu').remove();
elem.append($menu);
$scope.$apply(function() {
$compile($menu.contents())(menuScope);
teather = new Tether({
element: $menu,
target: $panelContainer,
attachment: 'bottom center',
targetAttachment: 'top center',
constraints: [
{
to: 'window',
attachment: 'together',
pin: true
}
]
});
});
dismiss(2200);
var showMenu = function() {
// // if menu item is clicked and menu was just removed from dom ignore this event
// if (!$.contains(document, e.target)) {
// return;
// }
//
// if ($menu) {
// dismiss();
// return;
// }
//
// var menuTemplate;
// menuTemplate = createMenuTemplate(ctrl);
//
// $menu = $(menuTemplate);
// $menu.mouseleave(function() {
// dismiss(1000);
// });
//
// menuScope = $scope.$new();
// menuScope.extendedMenu = getExtendedMenu(ctrl);
// menuScope.dismiss = function() {
// dismiss(null, true);
// };
//
// $(".panel-container").removeClass('panel-highlight');
// $panelContainer.toggleClass('panel-highlight');
//
// $('.panel-menu').remove();
//
// elem.append($menu);
//
// $scope.$apply(function() {
// $compile($menu.contents())(menuScope);
//
// teather = new Tether({
// element: $menu,
// target: $panelContainer,
// attachment: 'bottom center',
// targetAttachment: 'top center',
// constraints: [
// {
// to: 'window',
// attachment: 'together',
// pin: true
// }
// ]
// });
// });
//
// dismiss(2200);
};
elem.click(showMenu);

View File

@@ -77,3 +77,4 @@ declare module 'gridstack' {
var gridstack: any;
export default gridstack;
}

View File

@@ -33,30 +33,9 @@ System.config({
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
"d3": "vendor/d3/d3.js",
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
"jquery-ui": "vendor/npm/jquery-ui-dist/jquery-ui.js",
"jquery-ui": "vendor/jquery-ui/custom.js",
"gridstack": "vendor/npm/gridstack/dist/gridstack.js",
"gridstack.jquery-ui": "vendor/npm/gridstack/dist/gridstack.jQueryUI.js",
'jquery-ui/data': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/disable-selection': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/focusable': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/form': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/ie': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/keycode': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/labels': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/jquery-1-7': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/plugin': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/safe-active-element': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/safe-blur': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/scroll-parent': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/tabbable': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/unique-id': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/version': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/widget': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/widgets/mouse': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/widgets/draggable': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/widgets/droppable': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
'jquery-ui/widgets/resizable': 'vendor/npm/jquery-ui-dist/jquery-ui.js',
},
packages: {
@@ -85,12 +64,12 @@ System.config({
deps: ['jquery'],
},
'vendor/npm/gridstack/dist/gridstack.js': {
format: 'amd',
format: 'global',
deps: ['jquery', 'jquery-ui', 'lodash'],
},
"vendor/npm/gridstack/dist/gridstack.jQueryUI.js": {
format: 'amd',
deps: ['gridstack.jquery-ui'],
format: 'global',
deps: ['gridstack'],
},
'vendor/npm/virtual-scroll/src/indx.js': {
format: 'cjs',

View File

@@ -76,6 +76,7 @@
@import "components/edit_sidemenu.scss";
@import "components/row.scss";
@import "components/gridstack.scss";
@import "components/griddash.scss";
// PAGES
@import "pages/login";

View File

@@ -0,0 +1,2 @@
.grid-stack-item-content {
}

View File

@@ -20,8 +20,8 @@ div.flot-text {
}
.panel {
display: inline-block;
float: left;
//display: inline-block;
//float: left;
&--solo {
.resize-panel-handle {
@@ -35,7 +35,7 @@ div.flot-text {
.panel-margin {
margin: 0 0.4rem 0.8rem 0.4rem;
display: block;
//display: block;
}
.panel-container {

4034
public/vendor/jquery-ui/custom.js vendored Normal file

File diff suppressed because it is too large Load Diff