Merge pull request #531 from mabashian/4796-workflow-resize-v2

Zoom workflow graph to fit screen on initial load
This commit is contained in:
Michael Abashian
2017-10-23 10:47:14 -04:00
committed by GitHub
11 changed files with 85 additions and 34 deletions

View File

@@ -163,7 +163,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
}, },
add_survey: { add_survey: {
ngClick: 'addSurvey()', ngClick: 'addSurvey()',
ngShow: '$state.is(\'templates.addWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate.workflowMaker\')', ngShow: '!survey_exists && ($state.is(\'templates.addWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate.workflowMaker\'))',
awFeature: 'surveys', awFeature: 'surveys',
awToolTip: '{{surveyTooltip}}', awToolTip: '{{surveyTooltip}}',
dataPlacement: 'top', dataPlacement: 'top',
@@ -173,7 +173,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n) {
edit_survey: { edit_survey: {
ngClick: 'editSurvey()', ngClick: 'editSurvey()',
awFeature: 'surveys', awFeature: 'surveys',
ngShow: '$state.is(\'templates.addWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate.workflowMaker\')', ngShow: 'survey_exists && ($state.is(\'templates.addWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate\') || $state.is(\'templates.editWorkflowJobTemplate.workflowMaker\'))',
label: i18n._('Edit Survey'), label: i18n._('Edit Survey'),
class: 'Form-primaryButton', class: 'Form-primaryButton',
awToolTip: '{{surveyTooltip}}', awToolTip: '{{surveyTooltip}}',

View File

@@ -21,7 +21,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
restrict: 'E', restrict: 'E',
link: function(scope, element) { link: function(scope, element) {
let margin = {top: 20, right: 20, bottom: 20, left: 20}, let marginLeft = 20,
i = 0, i = 0,
nodeW = 180, nodeW = 180,
nodeH = 60, nodeH = 60,
@@ -36,7 +36,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
line, line,
zoomObj, zoomObj,
baseSvg, baseSvg,
svgGroup; svgGroup,
graphLoaded;
scope.dimensionsSet = false; scope.dimensionsSet = false;
@@ -75,7 +76,8 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
); );
svgGroup = baseSvg.append("g") svgGroup = baseSvg.append("g")
.attr("transform", "translate(" + margin.left + "," + (windowHeight/2 - rootH/2 - startNodeOffsetY) + ")"); .attr("id", "aw-workflow-chart-g")
.attr("transform", "translate(" + marginLeft + "," + (windowHeight/2 - rootH/2 - startNodeOffsetY) + ")");
} }
function calcAvailableScreenSpace() { function calcAvailableScreenSpace() {
@@ -158,7 +160,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
let scale = d3.event.scale, let scale = d3.event.scale,
translation = d3.event.translate; translation = d3.event.translate;
translation = [translation[0] + (margin.left*scale), translation[1] + ((windowHeight/2 - rootH/2 - startNodeOffsetY)*scale)]; translation = [translation[0] + (marginLeft*scale), translation[1] + ((windowHeight/2 - rootH/2 - startNodeOffsetY)*scale)];
svgGroup.attr("transform", "translate(" + translation + ")scale(" + scale + ")"); svgGroup.attr("transform", "translate(" + translation + ")scale(" + scale + ")");
@@ -177,7 +179,7 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
translateX = unscaledOffsetX*scale - ((scale*windowWidth)-windowWidth)/2, translateX = unscaledOffsetX*scale - ((scale*windowWidth)-windowWidth)/2,
translateY = unscaledOffsetY*scale - ((scale*windowHeight)-windowHeight)/2; translateY = unscaledOffsetY*scale - ((scale*windowHeight)-windowHeight)/2;
svgGroup.attr("transform", "translate(" + [translateX + (margin.left*scale), translateY + ((windowHeight/2 - rootH/2 - startNodeOffsetY)*scale)] + ")scale(" + scale + ")"); svgGroup.attr("transform", "translate(" + [translateX + (marginLeft*scale), translateY + ((windowHeight/2 - rootH/2 - startNodeOffsetY)*scale)] + ")scale(" + scale + ")");
zoomObj.scale(scale); zoomObj.scale(scale);
zoomObj.translate([translateX, translateY]); zoomObj.translate([translateX, translateY]);
} }
@@ -200,12 +202,36 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
} }
function resetZoomAndPan() { function resetZoomAndPan() {
svgGroup.attr("transform", "translate(" + margin.left + "," + (windowHeight/2 - rootH/2 - startNodeOffsetY) + ")scale(" + 1 + ")"); svgGroup.attr("transform", "translate(" + marginLeft + "," + (windowHeight/2 - rootH/2 - startNodeOffsetY) + ")scale(" + 1 + ")");
// Update the zoomObj // Update the zoomObj
zoomObj.scale(1); zoomObj.scale(1);
zoomObj.translate([0,0]); zoomObj.translate([0,0]);
} }
function zoomToFitChart() {
let graphDimensions = d3.select('#aw-workflow-chart-g')[0][0].getBoundingClientRect(),
startNodeDimensions = d3.select('.WorkflowChart-rootNode')[0][0].getBoundingClientRect(),
availableScreenSpace = calcAvailableScreenSpace(),
currentZoomValue = zoomObj.scale(),
unscaledH = graphDimensions.height/currentZoomValue,
unscaledW = graphDimensions.width/currentZoomValue,
scaleNeededForMaxHeight = (availableScreenSpace.height)/unscaledH,
scaleNeededForMaxWidth = (availableScreenSpace.width - marginLeft)/unscaledW,
lowerScale = Math.min(scaleNeededForMaxHeight, scaleNeededForMaxWidth),
scaleToFit = lowerScale < 0.5 ? 0.5 : (lowerScale > 2 ? 2 : Math.floor(lowerScale * 10)/10),
startNodeOffsetFromGraphCenter = Math.round((((rootH/2) + (startNodeDimensions.top/currentZoomValue)) - ((graphDimensions.top/currentZoomValue) + (unscaledH/2)))*scaleToFit);
manualZoom(scaleToFit*100);
scope.workflowZoomed({
zoom: scaleToFit
});
svgGroup.attr("transform", "translate(" + marginLeft + "," + (windowHeight/2 - (nodeH*scaleToFit/2) + startNodeOffsetFromGraphCenter) + ")scale(" + scaleToFit + ")");
zoomObj.translate([marginLeft - scaleToFit*marginLeft, windowHeight/2 - (nodeH*scaleToFit/2) + startNodeOffsetFromGraphCenter - ((windowHeight/2 - rootH/2 - startNodeOffsetY)*scaleToFit)]);
}
function update() { function update() {
let userCanAddEdit = (scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate; let userCanAddEdit = (scope.workflowJobTemplateObj && scope.workflowJobTemplateObj.summary_fields && scope.workflowJobTemplateObj.summary_fields.user_capabilities && scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) || scope.canAddWorkflowJobTemplate;
if(scope.dimensionsSet) { if(scope.dimensionsSet) {
@@ -243,7 +269,6 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
thisNode.append("rect") thisNode.append("rect")
.attr("width", rootW) .attr("width", rootW)
.attr("height", rootH) .attr("height", rootH)
//.attr("y", (windowHeight-margin.top-margin.bottom)/2 - rootH)
.attr("y", 10) .attr("y", 10)
.attr("rx", 5) .attr("rx", 5)
.attr("ry", 5) .attr("ry", 5)
@@ -252,7 +277,6 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
.call(add_node); .call(add_node);
thisNode.append("text") thisNode.append("text")
.attr("x", 13) .attr("x", 13)
//.attr("y", (windowHeight-margin.top-margin.bottom)/2 - rootH + rootH/2)
.attr("y", 30) .attr("y", 30)
.attr("dy", ".35em") .attr("dy", ".35em")
.attr("class", "WorkflowChart-startText") .attr("class", "WorkflowChart-startText")
@@ -544,6 +568,12 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
node.exit().remove(); node.exit().remove();
if(nodes && nodes.length > 1 && !graphLoaded) {
zoomToFitChart();
}
graphLoaded = true;
let link = svgGroup.selectAll("g.link") let link = svgGroup.selectAll("g.link")
.data(links, function(d) { .data(links, function(d) {
return d.source.id + "-" + d.target.id; return d.source.id + "-" + d.target.id;
@@ -927,6 +957,10 @@ export default ['$state','moment', '$timeout', '$window', '$filter', 'Rest', 'Ge
manualZoom(params.zoom); manualZoom(params.zoom);
}); });
scope.$on('zoomToFitChart', function() {
zoomToFitChart();
});
let clearWatchTreeData = scope.$watch('treeData', function(newVal) { let clearWatchTreeData = scope.$watch('treeData', function(newVal) {
if(newVal) { if(newVal) {
update(); update();

View File

@@ -7,7 +7,7 @@
flex: 1 0 auto; flex: 1 0 auto;
} }
.WorkflowControls-Pan { .WorkflowControls-Pan {
flex: 0 0 85px; flex: 0 0 87px;
} }
.WorkflowControls-Pan--button { .WorkflowControls-Pan--button {
color: @default-icon; color: @default-icon;
@@ -49,17 +49,10 @@
.WorkflowControls-Zoom--button:hover { .WorkflowControls-Zoom--button:hover {
color: @default-link-hov; color: @default-link-hov;
} }
.WorkflowControls-Zoom--minus {
margin-left: 20px;
padding-right: 8px;
}
.WorkflowControls-Zoom--plus {
padding-left: 8px;
}
.WorkflowControls-zoomSlider { .WorkflowControls-zoomSlider {
width: 150px; width: 150px;
padding-left: 10px; padding-left: 8px;
padding-right: 10px; padding-right: 8px;
} }
.WorkflowControls-zoomPercentage { .WorkflowControls-zoomPercentage {
text-align: center; text-align: center;
@@ -67,6 +60,16 @@
height: 24px; height: 24px;
line-height: 24px; line-height: 24px;
} }
.WorkflowControls-ZoomToFit {
display: flex;
flex: 0 0 62px;
align-items: center;
justify-content: center;
color: @default-icon;
}
.WorkflowControls-ZoomToFit:hover {
color: @default-link-hov;
}
.ui-slider-handle.ui-state-default.ui-corner-all { .ui-slider-handle.ui-state-default.ui-corner-all {
border-radius: 50%; border-radius: 50%;

View File

@@ -10,7 +10,8 @@ export default ['templateUrl',
scope: { scope: {
panChart: '&', panChart: '&',
resetChart: '&', resetChart: '&',
zoomChart: '&' zoomChart: '&',
zoomToFitChart: '&'
}, },
templateUrl: templateUrl('templates/workflows/workflow-controls/workflow-controls'), templateUrl: templateUrl('templates/workflows/workflow-controls/workflow-controls'),
restrict: 'E', restrict: 'E',
@@ -60,6 +61,10 @@ export default ['templateUrl',
}); });
}; };
scope.zoomToFit = function() {
scope.zoomToFitChart();
};
scope.$on('workflowZoomed', function(evt, params) { scope.$on('workflowZoomed', function(evt, params) {
scope.zoom = Math.round(params.zoom * 10) * 10; scope.zoom = Math.round(params.zoom * 10) * 10;
$("#slider").slider('value',scope.zoom); $("#slider").slider('value',scope.zoom);

View File

@@ -1,3 +1,6 @@
<div class="WorkflowControls-ZoomToFit">
<i class="fa fa-desktop" ng-click="zoomToFit()"></i>
</div>
<div class="WorkflowControls-Zoom"> <div class="WorkflowControls-Zoom">
<div class="WorkflowControls-Zoom--button WorkflowControls-Zoom--minus" ng-click="zoomOut()"> <div class="WorkflowControls-Zoom--button WorkflowControls-Zoom--minus" ng-click="zoomOut()">
<i class="fa fa-minus" aria-hidden="true"></i> <i class="fa fa-minus" aria-hidden="true"></i>

View File

@@ -168,7 +168,7 @@
flex: 1 0 auto; flex: 1 0 auto;
} }
.WorkflowLegend-maker--right { .WorkflowLegend-maker--right {
flex: 0 0 215px; flex: 0 0 217px;
text-align: right; text-align: right;
padding-right: 20px; padding-right: 20px;
position: relative; position: relative;
@@ -246,9 +246,9 @@
} }
.WorkflowMaker-manualControls { .WorkflowMaker-manualControls {
position: absolute; position: absolute;
left: -77px; left: -106px;
height: 60px; height: 60px;
width: 293px; width: 322px;
background-color: @default-bg; background-color: @default-bg;
display: flex; display: flex;
border: 1px solid @b7grey; border: 1px solid @b7grey;
@@ -259,10 +259,10 @@
} }
.WorkflowLegend-manualControls { .WorkflowLegend-manualControls {
position: absolute; position: absolute;
left: -239px; left: -272px;
top: 38px; top: 38px;
height: 60px; height: 60px;
width: 290px; width: 322px;
background-color: @default-bg; background-color: @default-bg;
display: flex; display: flex;
border: 1px solid @d7grey; border: 1px solid @d7grey;

View File

@@ -940,9 +940,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
}); });
}; };
$scope.$on('WorkflowDialogReady', function(){ $scope.zoomToFitChart = function() {
$scope.modalOpen = true; $scope.$broadcast('zoomToFitChart');
}); };
init(); init();

View File

@@ -57,6 +57,8 @@ export default ['templateUrl', 'CreateDialog', 'Wait', '$state', '$window',
scope.removeWorkflowDialogReady = scope.$on('WorkflowDialogReady', function() { scope.removeWorkflowDialogReady = scope.$on('WorkflowDialogReady', function() {
$('#workflow-modal-dialog').dialog('open'); $('#workflow-modal-dialog').dialog('open');
scope.modalOpen = true;
scope.$broadcast("refreshWorkflowChart"); scope.$broadcast("refreshWorkflowChart");
}); });

View File

@@ -69,7 +69,7 @@
<span class="badge List-titleBadge" ng-bind="treeData.data.totalNodes"></span> <span class="badge List-titleBadge" ng-bind="treeData.data.totalNodes"></span>
<i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i> <i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i>
<div ng-show="showManualControls" class="WorkflowMaker-manualControls noselect"> <div ng-show="showManualControls" class="WorkflowMaker-manualControls noselect">
<workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()"></workflow-controls> <workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()" zoom-to-fit-chart="zoomToFitChart()"></workflow-controls>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -165,6 +165,10 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
$scope.$broadcast('resetWorkflowChart'); $scope.$broadcast('resetWorkflowChart');
}; };
$scope.zoomToFitChart = function() {
$scope.$broadcast('zoomToFitChart');
};
$scope.workflowZoomed = function(zoom) { $scope.workflowZoomed = function(zoom) {
$scope.$broadcast('workflowZoomed', { $scope.$broadcast('workflowZoomed', {
zoom: zoom zoom: zoom

View File

@@ -289,7 +289,7 @@
<div class="WorkflowLegend-details--right"> <div class="WorkflowLegend-details--right">
<i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i> <i ng-class="{'WorkflowMaker-manualControlsIcon--active': showManualControls}" class="fa fa-cog WorkflowMaker-manualControlsIcon" aria-hidden="true" alt="Controls" ng-click="toggleManualControls()"></i>
<div ng-show="showManualControls" class="WorkflowLegend-manualControls noselect"> <div ng-show="showManualControls" class="WorkflowLegend-manualControls noselect">
<workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()"></workflow-controls> <workflow-controls class="WorkflowControls" pan-chart="panChart(direction)" zoom-chart="zoomChart(zoom)" reset-chart="resetChart()" zoom-to-fit-chart="zoomToFitChart()"></workflow-controls>
</div> </div>
</div> </div>
</div> </div>