mirror of
https://github.com/Icinga/icingaweb2-module-businessprocess.git
synced 2025-12-24 16:49:34 -05:00
TreeRenderer: Use Icinga Web's collapsible implementation now
resolves #254
This commit is contained in:
parent
6abcf8a757
commit
c2e06aca0a
4 changed files with 95 additions and 78 deletions
|
|
@ -265,7 +265,7 @@ abstract class Renderer extends HtmlDocument
|
|||
*/
|
||||
public function getId(Node $node, $path)
|
||||
{
|
||||
return md5((empty($path) ? '' : implode(';', $path)) . $node->getName());
|
||||
return 'businessprocess-' . md5((empty($path) ? '' : implode(';', $path)) . $node->getName());
|
||||
}
|
||||
|
||||
public function setPath(array $path)
|
||||
|
|
|
|||
|
|
@ -2,19 +2,24 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Renderer;
|
||||
|
||||
use Icinga\Application\Version;
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Web\Form\CsrfToken;
|
||||
use Icinga\Module\Icingadb\Model\State;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class TreeRenderer extends Renderer
|
||||
{
|
||||
const NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE = '2.11.2';
|
||||
|
||||
public function assemble()
|
||||
{
|
||||
$bp = $this->config;
|
||||
|
|
@ -32,7 +37,6 @@ class TreeRenderer extends Renderer
|
|||
'put' => 'function:rowPutAllowed'
|
||||
]),
|
||||
'data-sortable-invert-swap' => 'true',
|
||||
'data-is-root-config' => $this->wantsRootNodes() ? 'true' : 'false',
|
||||
'data-csrf-token' => CsrfToken::generate()
|
||||
],
|
||||
$this->renderBp($bp)
|
||||
|
|
@ -42,6 +46,10 @@ class TreeRenderer extends Renderer
|
|||
'data-action-url',
|
||||
$this->getUrl()->with(['config' => $bp->getName()])->getAbsoluteUrl()
|
||||
);
|
||||
|
||||
if (version_compare(Version::VERSION, self::NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE, '<')) {
|
||||
$tree->getAttributes()->add('data-is-root-config', true);
|
||||
}
|
||||
} else {
|
||||
$nodeName = $this->parent instanceof ImportedNode
|
||||
? $this->parent->getNodeName()
|
||||
|
|
@ -192,27 +200,35 @@ class TreeRenderer extends Renderer
|
|||
$attributes->add('class', 'node');
|
||||
}
|
||||
|
||||
$div = Html::tag('div');
|
||||
$li->add($div);
|
||||
$details = new HtmlElement('details', Attributes::create(['open' => true]));
|
||||
$summary = new HtmlElement('summary');
|
||||
if (version_compare(Version::VERSION, self::NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE, '>=')) {
|
||||
$details->getAttributes()->add('class', 'collapsible');
|
||||
$summary->getAttributes()->add('class', 'collapsible-control'); // Helps JS, improves performance a bit
|
||||
}
|
||||
|
||||
$div->add($node->getLink());
|
||||
$div->add($this->getNodeIcons($node, $path));
|
||||
$summary->addHtml(
|
||||
new Icon('caret-down', ['class' => 'collapse-icon']),
|
||||
new Icon('caret-right', ['class' => 'expand-icon'])
|
||||
);
|
||||
|
||||
$div->add(Html::tag('span', null, $node->getAlias()));
|
||||
$summary->add($this->getNodeIcons($node, $path));
|
||||
|
||||
$summary->add(Html::tag('span', null, $node->getAlias()));
|
||||
|
||||
if ($node instanceof BpNode) {
|
||||
$div->add(Html::tag('span', ['class' => 'op'], $node->operatorHtml()));
|
||||
$summary->add(Html::tag('span', ['class' => 'op'], $node->operatorHtml()));
|
||||
}
|
||||
|
||||
if ($node instanceof BpNode && $node->hasInfoUrl()) {
|
||||
$div->add($this->createInfoAction($node));
|
||||
$summary->add($this->createInfoAction($node));
|
||||
}
|
||||
|
||||
$differentConfig = $node->getBpConfig()->getName() !== $this->getBusinessProcess()->getName();
|
||||
if (! $this->isLocked() && !$differentConfig) {
|
||||
$div->add($this->getActionIcons($bp, $node));
|
||||
$summary->add($this->getActionIcons($bp, $node));
|
||||
} elseif ($differentConfig) {
|
||||
$div->add($this->actionIcon(
|
||||
$summary->add($this->actionIcon(
|
||||
'forward',
|
||||
$this->getSourceUrl($node)->addParams(['mode' => 'tree'])->getAbsoluteUrl(),
|
||||
mt('businessprocess', 'Show this process as part of its original configuration')
|
||||
|
|
@ -240,7 +256,6 @@ class TreeRenderer extends Renderer
|
|||
])
|
||||
->getAbsoluteUrl()
|
||||
]);
|
||||
$li->add($ul);
|
||||
|
||||
$path[] = $differentConfig ? $node->getIdentifier() : $node->getName();
|
||||
foreach ($node->getChildren() as $name => $child) {
|
||||
|
|
@ -251,6 +266,10 @@ class TreeRenderer extends Renderer
|
|||
}
|
||||
}
|
||||
|
||||
$details->addHtml($summary);
|
||||
$details->addHtml($ul);
|
||||
$li->addHtml($details);
|
||||
|
||||
return $li;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ ul.bp {
|
|||
}
|
||||
}
|
||||
&[data-sortable-disabled="true"] {
|
||||
li.process > div {
|
||||
li.process summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,15 +143,17 @@ ul.bp {
|
|||
|
||||
// ghost style
|
||||
&.sortable > li.sortable-ghost {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-height: 30em;
|
||||
background-color: @gray-lighter;
|
||||
border: .2em dotted @gray-light;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
> details {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-height: 30em;
|
||||
background-color: @gray-lighter;
|
||||
border: .2em dotted @gray-light;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
&.process:after {
|
||||
&.process > .details:after {
|
||||
// TODO: Only apply if content overflows?
|
||||
content: " ";
|
||||
position: absolute;
|
||||
|
|
@ -164,12 +166,14 @@ ul.bp {
|
|||
}
|
||||
|
||||
// header style
|
||||
li.process > div {
|
||||
li.process summary {
|
||||
padding: .291666667em 0;
|
||||
border-bottom: 1px solid @gray-light;
|
||||
user-select: none;
|
||||
|
||||
> a.toggle {
|
||||
min-width: 1.25em; // So that process icons align with their node's icons
|
||||
> .icon:nth-child(1),
|
||||
> .icon:nth-child(2) {
|
||||
min-width: 1.3em; // So that process icons align with their node's icons
|
||||
color: @gray;
|
||||
}
|
||||
|
||||
|
|
@ -187,8 +191,23 @@ ul.bp {
|
|||
}
|
||||
}
|
||||
|
||||
li.process.sortable-ghost details:not([open]) > summary {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// TODO: Remove once support for Icinga Web 2.10.x is dropped
|
||||
li.process details:not(.collapsible) {
|
||||
&[open] > summary .expand-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:not([open]) > summary .collapse-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// subprocess style
|
||||
li.process > ul {
|
||||
li.process > details ul {
|
||||
padding-left: 2em;
|
||||
list-style-type: none;
|
||||
|
||||
|
|
@ -216,7 +235,7 @@ ul.bp {
|
|||
}
|
||||
|
||||
// horizontal layout
|
||||
li.process > div,
|
||||
li.process summary,
|
||||
li:not(.process) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -241,42 +260,23 @@ ul.bp {
|
|||
}
|
||||
|
||||
// collapse handling
|
||||
li.process {
|
||||
// toggle, default
|
||||
> div > a.toggle > i:before {
|
||||
-webkit-transition: -webkit-transform 0.3s;
|
||||
-moz-transition: -moz-transform 0.3s;
|
||||
-o-transition: -o-transform 0.3s;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
li.process details:not([open]) {
|
||||
margin-bottom: (@vertical-tree-item-gap * 2);
|
||||
|
||||
// toggle, collapsed
|
||||
&.collapsed > div > a.toggle > i:before {
|
||||
-moz-transform:rotate(-90deg);
|
||||
-ms-transform:rotate(-90deg);
|
||||
-o-transform:rotate(-90deg);
|
||||
-webkit-transform:rotate(-90deg);
|
||||
transform:rotate(-90deg);
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
margin-bottom: (@vertical-tree-item-gap * 2);
|
||||
|
||||
> ul.bp {
|
||||
display: none;
|
||||
}
|
||||
> ul.bp {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// hover style
|
||||
li.process:hover > div {
|
||||
li.process:hover summary {
|
||||
background-color: @tr-active-color;
|
||||
}
|
||||
li:not(.process):hover {
|
||||
background-color: @tr-active-color;
|
||||
}
|
||||
|
||||
li.process > div > .state-ball,
|
||||
li.process summary > .state-ball,
|
||||
li:not(.process) > .state-ball {
|
||||
border: .15em solid @body-bg-color;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@
|
|||
|
||||
this.module.on('focus', 'form input, form textarea, form select', this.formElementFocus);
|
||||
|
||||
this.module.on('click', 'li.process a.toggle', this.processToggleClick);
|
||||
this.module.on('click', 'li.process > div', this.processHeaderClick);
|
||||
this.module.on('click', 'li.process summary:not(.collapsible-control)', this.processHeaderClick);
|
||||
this.module.on('end', 'ul.sortable', this.rowDropped);
|
||||
|
||||
this.module.on('click', 'div.tiles > div', this.tileClick);
|
||||
|
|
@ -42,42 +41,41 @@
|
|||
onRendered: function (event) {
|
||||
var $container = $(event.currentTarget);
|
||||
this.fixFullscreen($container);
|
||||
this.restoreCollapsedBps($container);
|
||||
this.restoreCollapsedBps(event.target);
|
||||
this.highlightFormErrors($container);
|
||||
this.hideInactiveFormDescriptions($container);
|
||||
this.fixTileLinksOnDashboard($container);
|
||||
},
|
||||
|
||||
processToggleClick: function (event) {
|
||||
// TODO: Remove once support for Icinga Web 2.10.x is dropped
|
||||
processHeaderClick: function (event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
var $li = $(event.currentTarget).closest('li.process');
|
||||
$li.toggleClass('collapsed');
|
||||
let details = event.currentTarget.parentNode;
|
||||
details.open = ! details.open;
|
||||
|
||||
var $bpUl = $(event.currentTarget).closest('.content > ul.bp');
|
||||
if (! $bpUl.length || !$bpUl.data('isRootConfig')) {
|
||||
let bpUl = event.currentTarget.closest('.content > ul.bp');
|
||||
if (! bpUl || ! ('isRootConfig' in bpUl.dataset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bpName = $bpUl.attr('id');
|
||||
let bpName = bpUl.id;
|
||||
if (typeof this.idCache[bpName] === 'undefined') {
|
||||
this.idCache[bpName] = [];
|
||||
}
|
||||
|
||||
var index = this.idCache[bpName].indexOf($li.attr('id'));
|
||||
if ($li.is('.collapsed')) {
|
||||
let li = details.parentNode;
|
||||
let index = this.idCache[bpName].indexOf(li.id);
|
||||
if (! details.open) {
|
||||
if (index === -1) {
|
||||
this.idCache[bpName].push($li.attr('id'));
|
||||
this.idCache[bpName].push(li.id);
|
||||
}
|
||||
} else if (index !== -1) {
|
||||
this.idCache[bpName].splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
processHeaderClick: function (event) {
|
||||
this.processToggleClick(event);
|
||||
},
|
||||
|
||||
hideInactiveFormDescriptions: function($container) {
|
||||
$container.find('dd').not('.active').find('p.description').hide();
|
||||
},
|
||||
|
|
@ -226,23 +224,23 @@
|
|||
}
|
||||
},
|
||||
|
||||
restoreCollapsedBps: function($container) {
|
||||
var $bpUl = $container.find('.content > ul.bp');
|
||||
if (! $bpUl.length || !$bpUl.data('isRootConfig')) {
|
||||
// TODO: Remove once support for Icinga Web 2.10.x is dropped
|
||||
restoreCollapsedBps: function(container) {
|
||||
let bpUl = container.querySelector('.content > ul.bp');
|
||||
if (! bpUl || ! ('isRootConfig' in bpUl.dataset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bpName = $bpUl.attr('id');
|
||||
let bpName = bpUl.id;
|
||||
if (typeof this.idCache[bpName] === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var _this = this;
|
||||
$bpUl.find('li.process')
|
||||
.filter(function () {
|
||||
return _this.idCache[bpName].indexOf(this.id) !== -1;
|
||||
})
|
||||
.addClass('collapsed');
|
||||
bpUl.querySelectorAll('li.process').forEach(li => {
|
||||
if (this.idCache[bpName].indexOf(li.id) !== -1) {
|
||||
li.querySelector(':scope > details').open = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** BEGIN Form handling, borrowed from Director **/
|
||||
|
|
|
|||
Loading…
Reference in a new issue