[Bps-public-commit] rt-extension-lifecycleui branch, master, updated. b26cb45ab608b0d0844cf07e65f5e659a5c65c04
Shawn Moore
shawn at bestpractical.com
Thu Sep 7 17:14:04 EDT 2017
The branch, master has been updated
via b26cb45ab608b0d0844cf07e65f5e659a5c65c04 (commit)
via 148ab7307b9a324c883ecfc8f01e50368d30be1d (commit)
via 3c354f928b258420783d95ca09b54f36eac8be4e (commit)
via cdb5f40a3cd02af15389f2fdb3b0ff8c7740c815 (commit)
via 67e19b975e7ff00da99e4962a48495ce84a01c9d (commit)
via 4bbc8c5cf9f0405a280795941802435594060f83 (commit)
via 3bd048b54f289c865b44372ad97475845c7bc9ee (commit)
via 79eac8b89a49d6b24d9b136539726f9edb708260 (commit)
via 98c3d84bca1078bc4258588b55b1a2019c5a82cb (commit)
via 61c70eeb2f7473803629f3fc1a6ea78984c0a04f (commit)
via 140b2f2f505a19ee1e9d261ffc61be530448ab4d (commit)
via df756dda8e2c0e29e145d530e282dab744860a49 (commit)
via 3039b2f0ef56cf27f5897dfe9ada7f857ba42dff (commit)
via 273e91f10779f74ca85cac4913415b88ce31ef79 (commit)
via c17a7447cc8daf3df328084bfd58439c922223e0 (commit)
via a7240ae314e9d63b70ba53ea7db6ea3b166b6c7a (commit)
via 02488118c00a7a9ab74fa761daedfd5a0ada0123 (commit)
via 1cc2a858719c054068effe27121ec21ffcf565e4 (commit)
via 189651226219b8500341698632e3f3f4d266aff5 (commit)
via d4aa5976fe376461096988acea99b0c53ef7c73a (commit)
from 6bf469d8ed93f0b4c3727df8e08b5dcad484ad63 (commit)
Summary of changes:
.../Ticket/Elements/ShowSummary/AfterReminders | 5 +-
html/Elements/LifecycleGraph | 39 +++--
html/Elements/LifecycleInteractive | 91 +++++++++++
lib/RT/Extension/LifecycleUI.pm | 2 +
static/css/lifecycleui-editor.css | 3 +
static/css/lifecycleui-viewer-interactive.css | 20 +++
static/css/lifecycleui-viewer.css | 2 +-
static/js/lifecycleui-editor.js | 171 ++++++++++-----------
static/js/lifecycleui-model.js | 138 +++++++++++++----
static/js/lifecycleui-viewer-interactive.js | 66 ++++++++
static/js/lifecycleui-viewer.js | 4 +-
11 files changed, 402 insertions(+), 139 deletions(-)
create mode 100644 html/Elements/LifecycleInteractive
create mode 100644 static/css/lifecycleui-viewer-interactive.css
create mode 100644 static/js/lifecycleui-viewer-interactive.js
- Log -----------------------------------------------------------------
commit d4aa5976fe376461096988acea99b0c53ef7c73a
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 16:00:30 2017 +0000
Add a new ViewerInteractive subclass
diff --git a/html/Callbacks/RT-Extension-LifecycleUI/Ticket/Elements/ShowSummary/AfterReminders b/html/Callbacks/RT-Extension-LifecycleUI/Ticket/Elements/ShowSummary/AfterReminders
index 1d3c1e7..88ac753 100644
--- a/html/Callbacks/RT-Extension-LifecycleUI/Ticket/Elements/ShowSummary/AfterReminders
+++ b/html/Callbacks/RT-Extension-LifecycleUI/Ticket/Elements/ShowSummary/AfterReminders
@@ -9,5 +9,8 @@ return unless $config eq 'readonly' || $config eq 'interactive';
title => loc("Lifecycle"),
class => 'ticket-info-lifecycle',
&>
- <& /Elements/LifecycleGraph, Ticket => $Ticket &>
+ <& /Elements/LifecycleGraph,
+ Ticket => $Ticket,
+ Interactive => ($config eq 'interactive'),
+ &>
</&>
diff --git a/html/Elements/LifecycleGraph b/html/Elements/LifecycleGraph
index 7bb5518..546016d 100644
--- a/html/Elements/LifecycleGraph
+++ b/html/Elements/LifecycleGraph
@@ -1,4 +1,4 @@
-<div class="lifecycle-ui<% $Editing ? ' editing' : '' %>" id="lifecycle-<% $id %>">
+<div class="lifecycle-ui<% $Editing ? ' editing' : '' %><% $Interactive ? ' interactive' : '' %>" id="lifecycle-<% $id %>">
<div class="overlay-buttons">
<button class="zoom-in">+</button>
<button class="zoom-reset">0</button>
@@ -32,7 +32,11 @@
var editor = new RT.LifecycleEditor();
editor.initializeEditor(container, name, config, ticketStatus);
% } else {
+% if ($Interactive) {
+ var viewer = new RT.LifecycleViewerInteractive();
+% } else {
var viewer = new RT.LifecycleViewer();
+% }
viewer.initializeViewer(container, name, config, ticketStatus);
% }
});
@@ -41,6 +45,7 @@
</div>
<%ARGS>
$Editing => 0
+$Interactive => 0
$Lifecycle => undef
$Ticket => undef
</%ARGS>
diff --git a/lib/RT/Extension/LifecycleUI.pm b/lib/RT/Extension/LifecycleUI.pm
index 464bc8a..0bade1a 100644
--- a/lib/RT/Extension/LifecycleUI.pm
+++ b/lib/RT/Extension/LifecycleUI.pm
@@ -9,10 +9,12 @@ RT->AddJavaScript("d3.min.js");
RT->AddJavaScript("handlebars-4.0.6.min.js");
RT->AddJavaScript("lifecycleui-model.js");
RT->AddJavaScript("lifecycleui-viewer.js");
+RT->AddJavaScript("lifecycleui-viewer-interactive.js");
RT->AddJavaScript("lifecycleui-editor.js");
RT->AddStyleSheets("lifecycleui.css");
RT->AddStyleSheets("lifecycleui-viewer.css");
+RT->AddStyleSheets("lifecycleui-viewer-interactive.css");
RT->AddStyleSheets("lifecycleui-editor.css");
$RT::Config::META{Lifecycles}{EditLink} = RT->Config->Get('WebURL') . 'Admin/Lifecycles/';
diff --git a/static/css/lifecycleui-viewer-interactive.css b/static/css/lifecycleui-viewer-interactive.css
new file mode 100644
index 0000000..e69de29
diff --git a/static/js/lifecycleui-viewer-interactive.js b/static/js/lifecycleui-viewer-interactive.js
new file mode 100644
index 0000000..8069068
--- /dev/null
+++ b/static/js/lifecycleui-viewer-interactive.js
@@ -0,0 +1,18 @@
+jQuery(function () {
+ var Super = RT.LifecycleViewer;
+
+ function Interactive (container) {
+ Super.call(this);
+ };
+ Interactive.prototype = Object.create(Super.prototype);
+
+ Interactive.prototype.clickedStatus = function (d) {
+ };
+
+ Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
+ Super.prototype.initializeViewer.call(this, node, name, config, focusStatus);
+ };
+
+ RT.LifecycleViewerInteractive = Interactive;
+});
+
commit 189651226219b8500341698632e3f3f4d266aff5
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 16:00:45 2017 +0000
Reduce grid size
This makes it easier to make things look just so, without sacrificing
repeatability too much
diff --git a/static/js/lifecycleui-viewer.js b/static/js/lifecycleui-viewer.js
index 167823b..d203dee 100644
--- a/static/js/lifecycleui-viewer.js
+++ b/static/js/lifecycleui-viewer.js
@@ -3,7 +3,7 @@ jQuery(function () {
this.width = 809;
this.height = 500;
this.statusCircleRadius = 35;
- this.gridSize = 25;
+ this.gridSize = 10;
this.padding = this.statusCircleRadius;
};
commit 1cc2a858719c054068effe27121ec21ffcf565e4
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 17:16:43 2017 +0000
Show/hide undo button based on whether there are undo frames
diff --git a/static/css/lifecycleui-editor.css b/static/css/lifecycleui-editor.css
index 71cdb5a..e047a1d 100644
--- a/static/css/lifecycleui-editor.css
+++ b/static/css/lifecycleui-editor.css
@@ -105,3 +105,6 @@
color: gray;
}
+.lifecycle-ui .invisible {
+ visibility: hidden;
+}
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index 7fba169..c3d21e2 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -517,6 +517,11 @@ jQuery(function () {
}
return payload;
};
+
+ self.lifecycle.undoStackChangedCallback = function () {
+ d3.select(node).select('button.undo').classed('invisible', !self.lifecycle.hasUndoStack());
+ };
+ self.lifecycle.undoStackChangedCallback();
};
RT.LifecycleEditor = Editor;
diff --git a/static/js/lifecycleui-model.js b/static/js/lifecycleui-model.js
index b508d18..d6a1fcf 100644
--- a/static/js/lifecycleui-model.js
+++ b/static/js/lifecycleui-model.js
@@ -673,6 +673,14 @@ jQuery(function () {
}
undoStack.push([entry, extra]);
this._undoStack = undoStack;
+
+ if (this.undoStackChangedCallback) {
+ this.undoStackChangedCallback();
+ }
+ };
+
+ Lifecycle.prototype.hasUndoStack = function () {
+ return this._undoStack.length > 0;
};
Lifecycle.prototype.undo = function () {
@@ -693,6 +701,10 @@ jQuery(function () {
this._undoStack = undoStack;
+ if (this.undoStackChangedCallback) {
+ this.undoStackChangedCallback();
+ }
+
return payload[1];
};
commit 02488118c00a7a9ab74fa761daedfd5a0ada0123
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 18:10:41 2017 +0000
Piggyback on focusItem/defocus rather than select*
This simplifies the logic of selecting and/or focusing an item
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index c3d21e2..6a22b89 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -151,8 +151,7 @@ jQuery(function () {
}
else {
lifecycle.deleteItemForKey(self.inspectorNode._key);
- self.deselectAll(true);
- self.renderDisplay();
+ self.defocus();
}
});
@@ -180,13 +179,13 @@ jQuery(function () {
inspector.find('a.select-transition[data-from="'+fromStatus+'"][data-to="'+toStatus+'"]').closest('li').removeClass('hidden');
self.renderDisplay();
- self.selectStatus(self.inspectorNode.name);
});
inspector.on('click', 'a.select-status', function (e) {
e.preventDefault();
var statusName = jQuery(this).data('name');
- self.selectStatus(statusName);
+ var d = self.lifecycle.statusObjectForName(statusName);
+ self.focusItem(d);
});
inspector.on('click', 'a.select-transition', function (e) {
@@ -195,13 +194,15 @@ jQuery(function () {
var fromStatus = button.data('from');
var toStatus = button.data('to');
- self.selectTransition(fromStatus, toStatus);
+ var d = self.lifecycle.hasTransition(fromStatus, toStatus);
+ self.focusItem(d);
});
inspector.on('click', 'a.select-decoration', function (e) {
e.preventDefault();
var key = jQuery(this).data('key');
- self.selectDecoration(key);
+ var d = self.lifecycle.itemForKey(key);
+ self.focusItem(d);
});
inspector.on('click', '.add-status', function (e) {
@@ -236,14 +237,6 @@ jQuery(function () {
return;
}
- if (payload.inspectorKey) {
- var node = self.lifecycle.itemForKey(payload.inspectorKey);
- self.setInspectorContent(node);
- }
- else {
- self.setInspectorContent(null);
- }
-
if (payload.focusKey) {
var node = self.lifecycle.itemForKey(payload.focusKey);
self.focusItem(node);
@@ -251,9 +244,6 @@ jQuery(function () {
else {
self.defocus();
}
-
- self.renderDisplay();
-
});
inspector.on('click', 'button.redo', function (e) {
@@ -261,53 +251,6 @@ jQuery(function () {
});
};
- Editor.prototype.deselectAll = function (clearSelection) {
- var svg = this.svg;
-
- this.removePointHandles();
-
- if (clearSelection) {
- this.setInspectorContent(null);
- }
-
- this.defocus();
- this.renderDisplay();
- };
-
- Editor.prototype.selectStatus = function (name) {
- var self = this;
- var d = self.lifecycle.statusObjectForName(name);
-
- self.deselectAll(false);
- self.focusItem(d);
- self.setInspectorContent(d);
- self.renderDisplay();
- };
-
- Editor.prototype.selectTransition = function (fromStatus, toStatus) {
- var self = this;
- var d = self.lifecycle.hasTransition(fromStatus, toStatus);
-
- self.deselectAll(false);
- self.focusItem(d);
- self.setInspectorContent(d);
- self.renderDisplay();
- };
-
- Editor.prototype.selectDecoration = function (key) {
- var d = this.lifecycle.itemForKey(key);
-
- this.deselectAll(false);
- this.focusItem(d);
- this.setInspectorContent(d);
-
- if (d._type == 'polygon' || d._type == 'line') {
- this.addPointHandles(d);
- }
-
- this.renderDisplay();
- };
-
Editor.prototype.addPointHandles = function (d) {
var self = this;
var points = [];
@@ -406,15 +349,15 @@ jQuery(function () {
};
Editor.prototype.clickedStatus = function (d) {
- this.selectStatus(d.name);
+ this.focusItem(d);
};
Editor.prototype.clickedTransition = function (d) {
- this.selectTransition(d.from, d.to);
+ this.focusItem(d);
};
Editor.prototype.clickedDecoration = function (d) {
- this.selectDecoration(d._key);
+ this.focusItem(d);
};
Editor.prototype.didDragItem = function (d, node) {
@@ -463,27 +406,27 @@ jQuery(function () {
Editor.prototype.addNewStatus = function () {
var status = this.lifecycle.createStatus();
- this.selectStatus(status.name);
+ this.focusItem(status);
};
Editor.prototype.addNewTextDecoration = function () {
var text = this.lifecycle.createTextDecoration();
- this.selectDecoration(text._key);
+ this.focusItem(text);
};
Editor.prototype.addNewPolygonDecoration = function (type) {
var polygon = this.lifecycle.createPolygonDecoration(type);
- this.selectDecoration(polygon._key);
+ this.focusItem(polygon);
};
Editor.prototype.addNewCircleDecoration = function () {
var circle = this.lifecycle.createCircleDecoration();
- this.selectDecoration(circle._key);
+ this.focusItem(circle);
};
Editor.prototype.addNewLineDecoration = function () {
var line = this.lifecycle.createLineDecoration();
- this.selectDecoration(line._key);
+ this.focusItem(line);
};
Editor.prototype.initializeEditor = function (node, name, config, focusStatus) {
@@ -505,13 +448,10 @@ jQuery(function () {
return true;
});
- self.svg.on('click', function () { self.deselectAll(true) });
+ self.svg.on('click', function () { self.defocus() });
self.lifecycle.saveUndoCallback = function () {
var payload = {};
- if (self.inspectorNode) {
- payload.inspectorKey = self.inspectorNode._key;
- }
if (self._focusItem) {
payload.focusKey = self._focusItem._key;
}
@@ -524,5 +464,23 @@ jQuery(function () {
self.lifecycle.undoStackChangedCallback();
};
+ Editor.prototype.defocus = function () {
+ Super.prototype.defocus.call(this);
+ this.setInspectorContent(null);
+ this.removePointHandles();
+ this.renderDisplay();
+ };
+
+ Editor.prototype.focusItem = function (item) {
+ Super.prototype.focusItem.call(this, item);
+ this.setInspectorContent(item);
+
+ if (item._type == 'polygon' || item._type == 'line') {
+ this.addPointHandles(item);
+ }
+
+ this.renderDisplay();
+ };
+
RT.LifecycleEditor = Editor;
});
commit a7240ae314e9d63b70ba53ea7db6ea3b166b6c7a
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 18:14:10 2017 +0000
Defocus before refocusing
This should help keep things consistent, even if it causes a bit more
work
diff --git a/static/js/lifecycleui-viewer.js b/static/js/lifecycleui-viewer.js
index d203dee..3a81e9f 100644
--- a/static/js/lifecycleui-viewer.js
+++ b/static/js/lifecycleui-viewer.js
@@ -314,6 +314,8 @@ jQuery(function () {
};
Viewer.prototype.focusItem = function (d) {
+ this.defocus();
+
this._focusItem = d;
this.svg.classed("has-focus", true)
.attr('data-focus-type', d._type);
commit c17a7447cc8daf3df328084bfd58439c922223e0
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 18:33:45 2017 +0000
Use a _key for pointHandles
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index 6a22b89..b1cd069 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -256,6 +256,7 @@ jQuery(function () {
var points = [];
for (var i = 0; i < d.points.length; ++i) {
points.push({
+ _key: d._key + '-' + i,
i: i,
x: d.points[i].x,
y: d.points[i].y,
@@ -330,7 +331,7 @@ jQuery(function () {
var self = this;
var handles = self.decorationContainer.selectAll("circle.point-handle")
- .data(self.pointHandles || [], function (d) { return d.i });
+ .data(self.pointHandles || [], function (d) { return d._key });
handles.exit()
.remove();
commit 273e91f10779f74ca85cac4913415b88ce31ef79
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 18:34:18 2017 +0000
Avoid generating an undo frame immediately on selection
Selecting an object generates a drag-start event, which was generating
an undo frame. Instead, wait until we actually move the object for the
first time before we save state
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index b1cd069..d134bb5 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -286,6 +286,11 @@ jQuery(function () {
return;
}
+ if (!d._dragging) {
+ this.lifecycle.beginDragging();
+ d._dragging = true;
+ }
+
d.x = x;
d.y = y;
@@ -340,8 +345,9 @@ jQuery(function () {
.classed("point-handle", true)
.call(d3.drag()
.subject(function (d) { return { x: d.xScale(d.x), y : d.yScale(d.y) } })
- .on("start", function (d) { self.lifecycle.beginDragging() })
+ .on("start", function (d) { self.didBeginDrag(d, this) })
.on("drag", function (d) { self.didDragPointHandle(d) })
+ .on("end", function (d) { self.didEndDrag(d, this) })
)
.merge(handles)
.attr("transform", function (d) { return self.inspectorNode._type == 'polygon' ? "translate(" + self.xScale(self.inspectorNode.x) + ", " + self.yScale(self.inspectorNode.y) + ")" : 'translate(0, 20)'})
@@ -361,6 +367,12 @@ jQuery(function () {
this.focusItem(d);
};
+ Editor.prototype.didBeginDrag = function (d, node) { };
+
+ Editor.prototype.didEndDrag = function (d, node) {
+ d._dragging = false;
+ };
+
Editor.prototype.didDragItem = function (d, node) {
if (this.inspectorNode && this.inspectorNode._key != d._key) {
return;
@@ -373,6 +385,11 @@ jQuery(function () {
return;
}
+ if (!d._dragging) {
+ this.lifecycle.beginDragging();
+ d._dragging = true;
+ }
+
this.lifecycle.moveItem(d, x, y);
this.renderDisplay();
};
@@ -381,8 +398,9 @@ jQuery(function () {
var self = this;
return d3.drag()
.subject(function (d) { return { x: self.xScale(d.x), y : self.yScale(d.y) } })
- .on("start", function (d) { self.lifecycle.beginDragging() })
- .on("drag", function (d) { self.didDragItem(d, this) });
+ .on("start", function (d) { self.didBeginDrag(d, this) })
+ .on("drag", function (d) { self.didDragItem(d, this) })
+ .on("end", function (d) { self.didEndDrag(d, this) })
};
Editor.prototype.didEnterStatusNodes = function (statuses) {
commit 3039b2f0ef56cf27f5897dfe9ada7f857ba42dff
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 18:58:36 2017 +0000
Make _initialPointsForPolygon a class method
diff --git a/static/js/lifecycleui-model.js b/static/js/lifecycleui-model.js
index d6a1fcf..187faa0 100644
--- a/static/js/lifecycleui-model.js
+++ b/static/js/lifecycleui-model.js
@@ -14,20 +14,20 @@ jQuery(function () {
this._undoStack = [];
this._keyMap = {};
this._statusMeta = {};
+ };
- this._initialPointsForPolygon = {
- Triangle: [
- {x: .07, y: .2},
- {x: 0, y: 0},
- {x: -.06, y: .2}
- ],
- Rectangle: [
- {x: -.06, y: -.06},
- {x: .06, y: -.06},
- {x: .06, y: .06},
- {x: -.06, y: .06}
- ]
- };
+ Lifecycle.prototype._initialPointsForPolygon = {
+ Triangle: [
+ {x: .07, y: .2},
+ {x: 0, y: 0},
+ {x: -.06, y: .2}
+ ],
+ Rectangle: [
+ {x: -.06, y: -.06},
+ {x: .06, y: -.06},
+ {x: .06, y: .06},
+ {x: -.06, y: .06}
+ ]
};
Lifecycle.prototype.initializeFromConfig = function (config) {
commit df756dda8e2c0e29e145d530e282dab744860a49
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 19:07:31 2017 +0000
Fix object identity being messed up on undo
diff --git a/static/js/lifecycleui-model.js b/static/js/lifecycleui-model.js
index 187faa0..162ad8f 100644
--- a/static/js/lifecycleui-model.js
+++ b/static/js/lifecycleui-model.js
@@ -666,13 +666,17 @@ jQuery(function () {
Lifecycle.prototype._saveUndoEntry = function () {
var undoStack = this._undoStack;
delete this._undoStack;
- var entry = jQuery.extend(true, {}, this);
+ var keyMap = this._keyMap;
+ delete this._keyMap;
+
+ var entry = JSON.stringify(this);
var extra = {};
if (this.saveUndoCallback) {
extra = this.saveUndoCallback();
}
undoStack.push([entry, extra]);
this._undoStack = undoStack;
+ this._keyMap = keyMap;
if (this.undoStackChangedCallback) {
this.undoStackChangedCallback();
@@ -683,23 +687,46 @@ jQuery(function () {
return this._undoStack.length > 0;
};
+ Lifecycle.prototype._rebuildKeyMap = function () {
+ var keyMap = {};
+ jQuery.each(this._statusMeta, function (name, meta) {
+ keyMap[meta._key] = meta;
+ });
+
+ jQuery.each(this.transitions, function (i, transition) {
+ keyMap[transition._key] = transition;
+ jQuery.each(transition.actions, function (j, action) {
+ keyMap[action._key] = action;
+ });
+ });
+
+ jQuery.each(this.decorations, function (type, decorations) {
+ jQuery.each(decorations, function (i, decoration) {
+ keyMap[decoration._key] = decoration;
+ });
+ });
+
+ this._keyMap = keyMap;
+ };
+
+ Lifecycle.prototype._restoreState = function (state) {
+ for (var key in state) {
+ this[key] = state[key];
+ }
+
+ this._rebuildKeyMap();
+ };
+
Lifecycle.prototype.undo = function () {
var undoStack = this._undoStack;
if (undoStack.length == 0) {
return null;
}
- delete this._undoStack;
var payload = undoStack.pop();
- var entry = payload[0];
+ var entry = JSON.parse(payload[0]);
- for (var key in entry) {
- if (entry.hasOwnProperty(key)) {
- this[key] = entry[key];
- }
- }
-
- this._undoStack = undoStack;
+ this._restoreState(entry);
if (this.undoStackChangedCallback) {
this.undoStackChangedCallback();
commit 140b2f2f505a19ee1e9d261ffc61be530448ab4d
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 19:10:41 2017 +0000
Make _undoState a hash
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index d134bb5..d750685 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -477,10 +477,10 @@ jQuery(function () {
return payload;
};
- self.lifecycle.undoStackChangedCallback = function () {
+ self.lifecycle.undoStateChangedCallback = function () {
d3.select(node).select('button.undo').classed('invisible', !self.lifecycle.hasUndoStack());
};
- self.lifecycle.undoStackChangedCallback();
+ self.lifecycle.undoStateChangedCallback();
};
Editor.prototype.defocus = function () {
diff --git a/static/js/lifecycleui-model.js b/static/js/lifecycleui-model.js
index 162ad8f..cb3eec4 100644
--- a/static/js/lifecycleui-model.js
+++ b/static/js/lifecycleui-model.js
@@ -11,7 +11,7 @@ jQuery(function () {
this.transitions = [];
this.decorations = {};
- this._undoStack = [];
+ this._undoState = { undoStack: [] };
this._keyMap = {};
this._statusMeta = {};
};
@@ -664,8 +664,8 @@ jQuery(function () {
};
Lifecycle.prototype._saveUndoEntry = function () {
- var undoStack = this._undoStack;
- delete this._undoStack;
+ var undoState = this._undoState;
+ delete this._undoState;
var keyMap = this._keyMap;
delete this._keyMap;
@@ -674,17 +674,17 @@ jQuery(function () {
if (this.saveUndoCallback) {
extra = this.saveUndoCallback();
}
- undoStack.push([entry, extra]);
- this._undoStack = undoStack;
+ undoState.undoStack.push([entry, extra]);
+ this._undoState = undoState;
this._keyMap = keyMap;
- if (this.undoStackChangedCallback) {
- this.undoStackChangedCallback();
+ if (this.undoStateChangedCallback) {
+ this.undoStateChangedCallback();
}
};
Lifecycle.prototype.hasUndoStack = function () {
- return this._undoStack.length > 0;
+ return this._undoState.undoStack.length > 0;
};
Lifecycle.prototype._rebuildKeyMap = function () {
@@ -718,7 +718,7 @@ jQuery(function () {
};
Lifecycle.prototype.undo = function () {
- var undoStack = this._undoStack;
+ var undoStack = this._undoState.undoStack;
if (undoStack.length == 0) {
return null;
}
@@ -728,8 +728,8 @@ jQuery(function () {
this._restoreState(entry);
- if (this.undoStackChangedCallback) {
- this.undoStackChangedCallback();
+ if (this.undoStateChangedCallback) {
+ this.undoStateChangedCallback();
}
return payload[1];
commit 61c70eeb2f7473803629f3fc1a6ea78984c0a04f
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 19:28:37 2017 +0000
Implement redo
diff --git a/static/js/lifecycleui-editor.js b/static/js/lifecycleui-editor.js
index d750685..b43e6f6 100644
--- a/static/js/lifecycleui-editor.js
+++ b/static/js/lifecycleui-editor.js
@@ -232,13 +232,11 @@ jQuery(function () {
inspector.on('click', 'button.undo', function (e) {
e.preventDefault();
- var payload = self.lifecycle.undo();
- if (!payload) {
- return;
- }
+ var frame = self.lifecycle.undo();
+ var uiState = frame[1];
- if (payload.focusKey) {
- var node = self.lifecycle.itemForKey(payload.focusKey);
+ if (uiState.focusKey) {
+ var node = self.lifecycle.itemForKey(uiState.focusKey);
self.focusItem(node);
}
else {
@@ -248,6 +246,16 @@ jQuery(function () {
inspector.on('click', 'button.redo', function (e) {
e.preventDefault();
+ var frame = self.lifecycle.redo();
+ var uiState = frame[1];
+
+ if (uiState.focusKey) {
+ var node = self.lifecycle.itemForKey(uiState.focusKey);
+ self.focusItem(node);
+ }
+ else {
+ self.defocus();
+ }
});
};
@@ -469,16 +477,17 @@ jQuery(function () {
self.svg.on('click', function () { self.defocus() });
- self.lifecycle.saveUndoCallback = function () {
- var payload = {};
+ self.lifecycle.undoFrameCallback = function (frame) {
+ var uiState = {};
if (self._focusItem) {
- payload.focusKey = self._focusItem._key;
+ uiState.focusKey = self._focusItem._key;
}
- return payload;
+ frame.push(uiState);
};
self.lifecycle.undoStateChangedCallback = function () {
d3.select(node).select('button.undo').classed('invisible', !self.lifecycle.hasUndoStack());
+ d3.select(node).select('button.redo').classed('invisible', !self.lifecycle.hasRedoStack());
};
self.lifecycle.undoStateChangedCallback();
};
diff --git a/static/js/lifecycleui-model.js b/static/js/lifecycleui-model.js
index cb3eec4..f196f51 100644
--- a/static/js/lifecycleui-model.js
+++ b/static/js/lifecycleui-model.js
@@ -11,7 +11,7 @@ jQuery(function () {
this.transitions = [];
this.decorations = {};
- this._undoState = { undoStack: [] };
+ this._undoState = { undoStack: [], redoStack: [] };
this._keyMap = {};
this._statusMeta = {};
};
@@ -663,21 +663,30 @@ jQuery(function () {
}
};
- Lifecycle.prototype._saveUndoEntry = function () {
+ Lifecycle.prototype._currentUndoFrame = function () {
var undoState = this._undoState;
- delete this._undoState;
var keyMap = this._keyMap;
+ delete this._undoState;
delete this._keyMap;
var entry = JSON.stringify(this);
- var extra = {};
- if (this.saveUndoCallback) {
- extra = this.saveUndoCallback();
- }
- undoState.undoStack.push([entry, extra]);
+
this._undoState = undoState;
this._keyMap = keyMap;
+ var frame = [entry];
+ if (this.undoFrameCallback) {
+ this.undoFrameCallback(frame);
+ }
+
+ return frame;
+ };
+
+ Lifecycle.prototype._saveUndoEntry = function () {
+ var frame = this._currentUndoFrame();
+ this._undoState.undoStack.push(frame);
+ this._undoState.redoStack = [];
+
if (this.undoStateChangedCallback) {
this.undoStateChangedCallback();
}
@@ -687,6 +696,10 @@ jQuery(function () {
return this._undoState.undoStack.length > 0;
};
+ Lifecycle.prototype.hasRedoStack = function () {
+ return this._undoState.redoStack.length > 0;
+ };
+
Lifecycle.prototype._rebuildKeyMap = function () {
var keyMap = {};
jQuery.each(this._statusMeta, function (name, meta) {
@@ -723,8 +736,30 @@ jQuery(function () {
return null;
}
- var payload = undoStack.pop();
- var entry = JSON.parse(payload[0]);
+ this._undoState.redoStack.push(this._currentUndoFrame());
+
+ var frame = undoStack.pop();
+ var entry = JSON.parse(frame[0]);
+
+ this._restoreState(entry);
+
+ if (this.undoStateChangedCallback) {
+ this.undoStateChangedCallback();
+ }
+
+ return frame;
+ };
+
+ Lifecycle.prototype.redo = function () {
+ var redoStack = this._undoState.redoStack;
+ if (redoStack.length == 0) {
+ return null;
+ }
+
+ this._undoState.undoStack.push(this._currentUndoFrame());
+
+ var frame = redoStack.pop();
+ var entry = JSON.parse(frame[0]);
this._restoreState(entry);
@@ -732,7 +767,7 @@ jQuery(function () {
this.undoStateChangedCallback();
}
- return payload[1];
+ return frame;
};
RT.Lifecycle = Lifecycle;
commit 98c3d84bca1078bc4258588b55b1a2019c5a82cb
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:18:48 2017 +0000
Add lifecycle menu markup for each status for interactive viewer
diff --git a/html/Elements/LifecycleGraph b/html/Elements/LifecycleGraph
index 546016d..de7265b 100644
--- a/html/Elements/LifecycleGraph
+++ b/html/Elements/LifecycleGraph
@@ -13,7 +13,10 @@
</g>
</svg>
% if ($Editing) {
- <& /Elements/LifecycleInspector &>
+ <& /Elements/LifecycleInspector, %ARGS &>
+% }
+% if ($Interactive) {
+ <& /Elements/LifecycleInteractive, %ARGS &>
% }
<script type="text/javascript">
jQuery(function () {
diff --git a/html/Elements/LifecycleInteractive b/html/Elements/LifecycleInteractive
new file mode 100644
index 0000000..09d8f0c
--- /dev/null
+++ b/html/Elements/LifecycleInteractive
@@ -0,0 +1,68 @@
+<div class="status-menus">
+% for my $status (keys %menus) {
+<div class="status-menu" data-status="<% $status %>">
+% my $menu = $menus{$status};
+<& /Elements/Menu, menu => $menu &>
+</div>
+% }
+</div>
+
+<%INIT>
+my $Lifecycle = $Ticket->LifecycleObj;
+my $id = $Ticket->Id;
+my %menus;
+
+# largely borrowed from /Elements/Tabs
+my $hide_resolve_with_deps = RT->Config->Get('HideResolveActionsWithDependencies')
+ && $Ticket->HasUnresolvedDependencies;
+my $query_string = sub {
+ my %args = @_;
+ my $u = URI->new();
+ $u->query_form(map { $_ => $args{$_} } sort keys %args);
+ return $u->query;
+};
+
+for my $from ($Lifecycle->Valid) {
+ my $actions = RT::Interface::Web::Menu->new();
+
+ foreach my $info ( $Lifecycle->Actions($from) ) {
+ my @class;
+ my $next = $info->{'to'};
+
+ if (!$Lifecycle->IsTransition( $from => $next )) {
+ push @class, 'no-transition';
+ }
+ else {
+ my $check = $Lifecycle->CheckRight( $from => $next );
+ if (!$Ticket->CurrentUserHasRight($check)) {
+ push @class, 'no-permission';
+ }
+ }
+
+ if ($hide_resolve_with_deps
+ && $Lifecycle->IsInactive($next)
+ && !$Lifecycle->IsInactive($from)) {
+ push @class, 'hide-resolve-with-deps';
+ }
+
+ my $action = $info->{'update'} || '';
+ my $url = '/Ticket/';
+ $url .= "Update.html?". $query_string->(
+ $action
+ ? (Action => $action)
+ : (SubmitTicket => 1, Status => $next),
+ DefaultStatus => $next,
+ id => $id,
+ );
+ my $key = $info->{'label'} || ucfirst($next);
+ $actions->child( $key => title => loc( $key ), path => $url, class => (join " ", @class));
+ }
+
+ $m->callback( CallbackName => 'StatusMenu', TicketObj => $Ticket, LifecycleObj => $Lifecycle, Status => $from, Menu => $actions);
+
+ $menus{$from} = $actions;
+}
+</%INIT>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
diff --git a/static/css/lifecycleui-viewer-interactive.css b/static/css/lifecycleui-viewer-interactive.css
index e69de29..0c5c2e7 100644
--- a/static/css/lifecycleui-viewer-interactive.css
+++ b/static/css/lifecycleui-viewer-interactive.css
@@ -0,0 +1,3 @@
+.lifecycle-ui .status-menu {
+ display: none;
+}
commit 79eac8b89a49d6b24d9b136539726f9edb708260
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:28:01 2017 +0000
Add .not-current for other status menus
diff --git a/html/Elements/LifecycleInteractive b/html/Elements/LifecycleInteractive
index 09d8f0c..5db7546 100644
--- a/html/Elements/LifecycleInteractive
+++ b/html/Elements/LifecycleInteractive
@@ -29,6 +29,10 @@ for my $from ($Lifecycle->Valid) {
my @class;
my $next = $info->{'to'};
+ if ($from ne $Ticket->Status) {
+ push @class, 'not-current';
+ }
+
if (!$Lifecycle->IsTransition( $from => $next )) {
push @class, 'no-transition';
}
commit 3bd048b54f289c865b44372ad97475845c7bc9ee
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:29:45 2017 +0000
Only include urls for transitions you can really make
diff --git a/html/Elements/LifecycleInteractive b/html/Elements/LifecycleInteractive
index 5db7546..d67afab 100644
--- a/html/Elements/LifecycleInteractive
+++ b/html/Elements/LifecycleInteractive
@@ -27,19 +27,23 @@ for my $from ($Lifecycle->Valid) {
foreach my $info ( $Lifecycle->Actions($from) ) {
my @class;
+ my $include_url = 1;
my $next = $info->{'to'};
if ($from ne $Ticket->Status) {
push @class, 'not-current';
+ $include_url = 0;
}
if (!$Lifecycle->IsTransition( $from => $next )) {
push @class, 'no-transition';
+ $include_url = 0;
}
else {
my $check = $Lifecycle->CheckRight( $from => $next );
if (!$Ticket->CurrentUserHasRight($check)) {
push @class, 'no-permission';
+ $include_url = 0;
}
}
@@ -47,6 +51,7 @@ for my $from ($Lifecycle->Valid) {
&& $Lifecycle->IsInactive($next)
&& !$Lifecycle->IsInactive($from)) {
push @class, 'hide-resolve-with-deps';
+ $include_url = 0;
}
my $action = $info->{'update'} || '';
@@ -59,7 +64,12 @@ for my $from ($Lifecycle->Valid) {
id => $id,
);
my $key = $info->{'label'} || ucfirst($next);
- $actions->child( $key => title => loc( $key ), path => $url, class => (join " ", @class));
+ $actions->child(
+ $key =>
+ title => loc( $key ),
+ ($include_url ? (path => $url) : ()),
+ class => (join " ", @class),
+ );
}
$m->callback( CallbackName => 'StatusMenu', TicketObj => $Ticket, LifecycleObj => $Lifecycle, Status => $from, Menu => $actions);
commit 4bbc8c5cf9f0405a280795941802435594060f83
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:32:31 2017 +0000
Grey out any actions you can't take
diff --git a/static/css/lifecycleui-viewer-interactive.css b/static/css/lifecycleui-viewer-interactive.css
index 0c5c2e7..13e274c 100644
--- a/static/css/lifecycleui-viewer-interactive.css
+++ b/static/css/lifecycleui-viewer-interactive.css
@@ -1,3 +1,13 @@
.lifecycle-ui .status-menu {
display: none;
}
+
+.lifecycle-ui .status-menu .not-current,
+.lifecycle-ui .status-menu .no-transition,
+.lifecycle-ui .status-menu .no-permission,
+.lifecycle-ui .status-menu .hide-resolve-with-deps {
+ color: #AAAAAA;
+ cursor: default;
+ text-decoration: none;
+}
+
commit 67e19b975e7ff00da99e4962a48495ce84a01c9d
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:50:29 2017 +0000
Open menu near the currently-selected status
diff --git a/html/Elements/LifecycleGraph b/html/Elements/LifecycleGraph
index de7265b..5c291f0 100644
--- a/html/Elements/LifecycleGraph
+++ b/html/Elements/LifecycleGraph
@@ -1,17 +1,20 @@
<div class="lifecycle-ui<% $Editing ? ' editing' : '' %><% $Interactive ? ' interactive' : '' %>" id="lifecycle-<% $id %>">
- <div class="overlay-buttons">
- <button class="zoom-in">+</button>
- <button class="zoom-reset">0</button>
- <button class="zoom-out">-</button>
+ <div class="lifecycle-view">
+ <div class="overlay-buttons">
+ <button class="zoom-in">+</button>
+ <button class="zoom-reset">0</button>
+ <button class="zoom-out">-</button>
+ </div>
+ <svg>
+ <& /Elements/LifecycleGraphExtras, %ARGS &>
+ <g class="transform">
+ <g class="decorations"></g>
+ <g class="transitions"></g>
+ <g class="statuses"></g>
+ </g>
+ </svg>
</div>
- <svg>
- <& /Elements/LifecycleGraphExtras, %ARGS &>
- <g class="transform">
- <g class="decorations"></g>
- <g class="transitions"></g>
- <g class="statuses"></g>
- </g>
- </svg>
+
% if ($Editing) {
<& /Elements/LifecycleInspector, %ARGS &>
% }
diff --git a/static/css/lifecycleui-viewer-interactive.css b/static/css/lifecycleui-viewer-interactive.css
index 13e274c..6a9f6c6 100644
--- a/static/css/lifecycleui-viewer-interactive.css
+++ b/static/css/lifecycleui-viewer-interactive.css
@@ -1,7 +1,14 @@
.lifecycle-ui .status-menu {
+ position: absolute;
+ top: 0;
+ left: 0;
display: none;
}
+.lifecycle-ui .status-menu.selected {
+ display: block;
+}
+
.lifecycle-ui .status-menu .not-current,
.lifecycle-ui .status-menu .no-transition,
.lifecycle-ui .status-menu .no-permission,
diff --git a/static/css/lifecycleui-viewer.css b/static/css/lifecycleui-viewer.css
index d8bfc50..6e9fb50 100644
--- a/static/css/lifecycleui-viewer.css
+++ b/static/css/lifecycleui-viewer.css
@@ -98,7 +98,7 @@
font-style: italic;
}
-.lifecycle-ui {
+.lifecycle-ui .lifecycle-view {
position: relative;
}
diff --git a/static/js/lifecycleui-viewer-interactive.js b/static/js/lifecycleui-viewer-interactive.js
index 8069068..a590616 100644
--- a/static/js/lifecycleui-viewer-interactive.js
+++ b/static/js/lifecycleui-viewer-interactive.js
@@ -6,11 +6,34 @@ jQuery(function () {
};
Interactive.prototype = Object.create(Super.prototype);
+ Interactive.prototype._setMenuPosition = function () {
+ if (!this.selectedStatus) {
+ return;
+ }
+
+ var d = this.selectedStatus;
+ var circle = this.statusContainer.select('circle[data-key="'+ d._key + '"]');
+ var bbox = circle.node().getBoundingClientRect();
+ var x = bbox.right + window.scrollX;
+ var y = bbox.top + window.scrollY;
+
+ this.selectedMenu.css({top: y, left: x});
+ };
+
Interactive.prototype.clickedStatus = function (d) {
+ var statusName = d.name;
+ this.selectedMenu = this.menuContainer.find('.status-menu[data-status="'+statusName+'"]');
+ this.selectedStatus = d;
+
+ this.menuContainer.find('.status-menu.selected').removeClass('selected');
+ this.selectedMenu.addClass('selected');
+
+ this._setMenuPosition();
};
Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
Super.prototype.initializeViewer.call(this, node, name, config, focusStatus);
+ this.menuContainer = jQuery(node).find('.status-menus');
};
RT.LifecycleViewerInteractive = Interactive;
commit cdb5f40a3cd02af15389f2fdb3b0ff8c7740c815
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:50:50 2017 +0000
Move menu as you pan and zoom
diff --git a/static/js/lifecycleui-viewer-interactive.js b/static/js/lifecycleui-viewer-interactive.js
index a590616..8b21228 100644
--- a/static/js/lifecycleui-viewer-interactive.js
+++ b/static/js/lifecycleui-viewer-interactive.js
@@ -31,6 +31,11 @@ jQuery(function () {
this._setMenuPosition();
};
+ Interactive.prototype.didZoom = function () {
+ Super.prototype.didZoom.call(this);
+ this._setMenuPosition();
+ };
+
Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
Super.prototype.initializeViewer.call(this, node, name, config, focusStatus);
this.menuContainer = jQuery(node).find('.status-menus');
commit 3c354f928b258420783d95ca09b54f36eac8be4e
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 20:56:30 2017 +0000
If the menu has scrolled away, close it
diff --git a/static/js/lifecycleui-viewer-interactive.js b/static/js/lifecycleui-viewer-interactive.js
index 8b21228..650a0b7 100644
--- a/static/js/lifecycleui-viewer-interactive.js
+++ b/static/js/lifecycleui-viewer-interactive.js
@@ -6,6 +6,12 @@ jQuery(function () {
};
Interactive.prototype = Object.create(Super.prototype);
+ Interactive.prototype.deselectStatus = function () {
+ delete this.selectedStatus;
+ delete this.selectedMenu;
+ this.menuContainer.find('.status-menu.selected').removeClass('selected');
+ };
+
Interactive.prototype._setMenuPosition = function () {
if (!this.selectedStatus) {
return;
@@ -33,7 +39,19 @@ jQuery(function () {
Interactive.prototype.didZoom = function () {
Super.prototype.didZoom.call(this);
- this._setMenuPosition();
+ if (this.selectedMenu) {
+ this._setMenuPosition();
+ var svgBox = this.svg.node().getBoundingClientRect();
+ var menuBox = this.selectedMenu[0].getBoundingClientRect();
+
+ var overlap = !(svgBox.right < menuBox.left ||
+ svgBox.left > menuBox.right ||
+ svgBox.bottom < menuBox.top ||
+ svgBox.top > menuBox.bottom);
+ if (!overlap) {
+ this.deselectStatus();
+ }
+ }
};
Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
commit 148ab7307b9a324c883ecfc8f01e50368d30be1d
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 21:10:24 2017 +0000
Refactor menu building
This way we get a menu for every single item
diff --git a/html/Elements/LifecycleInteractive b/html/Elements/LifecycleInteractive
index d67afab..4367453 100644
--- a/html/Elements/LifecycleInteractive
+++ b/html/Elements/LifecycleInteractive
@@ -13,6 +13,7 @@ my $id = $Ticket->Id;
my %menus;
# largely borrowed from /Elements/Tabs
+my $current = $Ticket->Status;
my $hide_resolve_with_deps = RT->Config->Get('HideResolveActionsWithDependencies')
&& $Ticket->HasUnresolvedDependencies;
my $query_string = sub {
@@ -22,60 +23,68 @@ my $query_string = sub {
return $u->query;
};
-for my $from ($Lifecycle->Valid) {
- my $actions = RT::Interface::Web::Menu->new();
-
- foreach my $info ( $Lifecycle->Actions($from) ) {
- my @class;
- my $include_url = 1;
- my $next = $info->{'to'};
+for my $status ($Lifecycle->Valid) {
+ $menus{$status} = RT::Interface::Web::Menu->new();
+}
- if ($from ne $Ticket->Status) {
- push @class, 'not-current';
- $include_url = 0;
- }
+my %seen_status;
- if (!$Lifecycle->IsTransition( $from => $next )) {
- push @class, 'no-transition';
- $include_url = 0;
- }
- else {
- my $check = $Lifecycle->CheckRight( $from => $next );
- if (!$Ticket->CurrentUserHasRight($check)) {
- push @class, 'no-permission';
- $include_url = 0;
- }
- }
-
- if ($hide_resolve_with_deps
- && $Lifecycle->IsInactive($next)
- && !$Lifecycle->IsInactive($from)) {
- push @class, 'hide-resolve-with-deps';
+my $add_menu = sub {
+ my $next = shift;
+ my $info = shift || {};
+ my @class;
+ my $include_url = 1;
+
+ $seen_status{$next}++;
+
+ if (!$Lifecycle->IsTransition( $current => $next )) {
+ push @class, 'no-transition';
+ $include_url = 0;
+ }
+ else {
+ my $check = $Lifecycle->CheckRight( $current => $next );
+ if (!$Ticket->CurrentUserHasRight($check)) {
+ push @class, 'no-permission';
$include_url = 0;
}
-
- my $action = $info->{'update'} || '';
- my $url = '/Ticket/';
- $url .= "Update.html?". $query_string->(
- $action
- ? (Action => $action)
- : (SubmitTicket => 1, Status => $next),
- DefaultStatus => $next,
- id => $id,
- );
- my $key = $info->{'label'} || ucfirst($next);
- $actions->child(
- $key =>
- title => loc( $key ),
- ($include_url ? (path => $url) : ()),
- class => (join " ", @class),
- );
}
- $m->callback( CallbackName => 'StatusMenu', TicketObj => $Ticket, LifecycleObj => $Lifecycle, Status => $from, Menu => $actions);
+ if ($hide_resolve_with_deps
+ && $Lifecycle->IsInactive($next)
+ && !$Lifecycle->IsInactive($current)) {
+ push @class, 'hide-resolve-with-deps';
+ $include_url = 0;
+ }
+
+ my $action = $info->{'update'} || '';
+ my $url = '/Ticket/';
+ $url .= "Update.html?". $query_string->(
+ $action
+ ? (Action => $action)
+ : (SubmitTicket => 1, Status => $next),
+ DefaultStatus => $next,
+ id => $id,
+ );
+ my $key = $info->{'label'} || ucfirst($next);
+ $menus{$next}->child(
+ $key =>
+ title => loc( $key ),
+ ($include_url ? (path => $url) : ()),
+ class => (join " ", @class),
+ );
+};
+
+foreach my $info ( $Lifecycle->Actions($current) ) {
+ $add_menu->($info->{to}, $info);
+}
- $menus{$from} = $actions;
+for my $status ($Lifecycle->Valid) {
+ next if $seen_status{$status};
+ $add_menu->($status);
}
+
+$m->callback( CallbackName => 'StatusMenus', TicketObj => $Ticket, LifecycleObj => $Lifecycle, Menus => \%menus);
+
</%INIT>
<%ARGS>
$Ticket => undef
commit b26cb45ab608b0d0844cf07e65f5e659a5c65c04
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Thu Sep 7 21:12:12 2017 +0000
Deselect menu on svg click
diff --git a/static/js/lifecycleui-viewer-interactive.js b/static/js/lifecycleui-viewer-interactive.js
index 650a0b7..ee107d4 100644
--- a/static/js/lifecycleui-viewer-interactive.js
+++ b/static/js/lifecycleui-viewer-interactive.js
@@ -55,8 +55,10 @@ jQuery(function () {
};
Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
- Super.prototype.initializeViewer.call(this, node, name, config, focusStatus);
- this.menuContainer = jQuery(node).find('.status-menus');
+ var self = this;
+ Super.prototype.initializeViewer.call(self, node, name, config, focusStatus);
+ self.menuContainer = jQuery(node).find('.status-menus');
+ self.svg.on('click', function () { self.deselectStatus() });
};
RT.LifecycleViewerInteractive = Interactive;
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list