diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 86594fa..0182dc0 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -21,9 +21,10 @@ class TreeRenderer extends Renderer 'div', [ 'id' => $bp->getHtmlId(), - 'class' => ['bp', 'sortable'], + 'class' => ['tree', 'sortable'], 'data-sortable-disabled' => $this->isLocked(), 'data-sortable-data-id-attr' => 'id', + 'data-sortable-filter' => '.placeholder', 'data-sortable-direction' => 'vertical', 'data-csrf-token' => CsrfToken::generate(), 'data-action-url' => $this->getUrl()->getAbsoluteUrl() @@ -47,6 +48,7 @@ class TreeRenderer extends Renderer $nodes = $this->parent->getChildren(); } + $html[] = Html::tag('div', ['class' => 'placeholder']); foreach ($nodes as $name => $node) { $html[] = $this->renderNode($bp, $node); } @@ -107,10 +109,10 @@ class TreeRenderer extends Renderer public function renderNode(BpConfig $bp, Node $node, $path = array()) { $table = Html::tag( - 'table', + 'div', [ 'id' => $this->getId($node, $path), - 'class' => ['bp', $node->getObjectClassName()], + 'class' => ['bp', 'movable', $node->getObjectClassName()], 'data-node-name' => $node->getName() ] ); @@ -125,30 +127,16 @@ class TreeRenderer extends Renderer $attributes->add('class', 'node'); } - $tbody = Html::tag('tbody', [ - 'class' => 'sortable', - 'data-sortable-disabled' => $this->isLocked(), - 'data-sortable-data-id-attr' => 'id', - 'data-sortable-draggable' => '.movable', - 'data-sortable-direction' => 'vertical', - 'data-csrf-token' => CsrfToken::generate(), - 'data-action-url' => $this->getUrl() - ->overwriteParams(['node' => (string) $node]) - ->getAbsoluteUrl() - ]); - $table->add($tbody); - $tr = Html::tag('tr'); - $tbody->add($tr); - if ($node instanceof BpNode) { - $tr->add(Html::tag( - 'th', - ['rowspan' => $node->countChildren() + 1 + ($this->isLocked() ? 0 : 1)], + $table->add(Html::tag( + 'div', + null, Html::tag('span', ['class' => 'op'], $node->operatorHtml()) )); } - $td = Html::tag('td'); - $tr->add($td); + + $td = Html::tag('div'); + $table->add($td); if ($node instanceof BpNode && $node->hasInfoUrl()) { $td->add($this->createInfoAction($node)); @@ -175,38 +163,82 @@ class TreeRenderer extends Renderer $td->add($link); + $tbody = Html::tag('ul', [ + 'class' => 'sortable', + 'data-sortable-disabled' => $this->isLocked(), + 'data-sortable-data-id-attr' => 'id', + 'data-sortable-draggable' => '.movable', + 'data-sortable-direction' => 'vertical', + 'data-csrf-token' => CsrfToken::generate(), + 'data-action-url' => $this->getUrl() + ->overwriteParams(['node' => (string) $node]) + ->getAbsoluteUrl() + ]); + $table->add($tbody); + $path[] = (string) $node; foreach ($node->getChildren() as $name => $child) { - $tbody->add(Html::tag( - 'tr', - [ - 'class' => 'movable', - 'id' => $this->getId($child, $path), - 'data-node-name' => $name - ], - Html::tag( - 'td', - null, + if ($child->hasChildren()) { + $tbody->add(Html::tag( + 'li', + [ + 'class' => 'movable', + 'id' => $this->getId($child, $path), + 'data-node-name' => $name + ], $this->renderNode($bp, $child, $this->getCurrentPath()) - ) - )); + )); + } else { + $this->renderChild($bp, $tbody, $child, $path); + } } if (! $this->isLocked() && $node instanceof BpNode && $bp->getMetadata()->canModify()) { $tbody->add(Html::tag( - 'tr', + 'li', null, - Html::tag( - 'td', - null, - $this->renderAddNewNode($node) - ) + $this->renderAddNewNode($node) )); } return $table; } + protected function renderChild($bp, BaseHtmlElement $ul, $node, $path = null) + { + $li = Html::tag('li', [ + 'class' => 'movable', + 'id' => $this->getId($node, $path ?: []), + 'data-node-name' => (string) $node + ]); + $ul->add($li); + + if ($node instanceof BpNode && $node->hasInfoUrl()) { + $li->add($this->createInfoAction($node)); + } + + if (! $this->isLocked()) { + $li->add($this->getActionIcons($bp, $node)); + } + + $link = $node->getLink(); + $link->getAttributes()->set('data-base-target', '_next'); + $link->add($this->getNodeIcons($node)); + + if ($node->hasChildren()) { + $link->add($this->renderStateBadges($node->getStateSummary())); + } + + if ($time = $node->getLastStateChange()) { + $since = $this->timeSince($time)->prepend( + sprintf(' (%s ', $node->getStateName()) + )->add(')'); + $link->add($since); + } + + $li->add($link); + } + protected function getActionIcons(BpConfig $bp, Node $node) { if ($node instanceof BpNode) { diff --git a/public/css/module.less b/public/css/module.less index b0547d8..070cd63 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -33,6 +33,38 @@ div.bp.sortable > .sortable-ghost { background: white; } + + +/* New tree stuff START */ + +div.tree { + padding: 1em; + + div.placeholder { + // Helps to assist Sortable.js/the browser to properly place items at the top + height: 1em; + } + + > div.process, + ul.sortable li { + border: .2em solid @gray; + margin-bottom: .2em; + padding-left: .2em; + + &.sortable-ghost { + border: .2em dashed @gray-light; + } + } + + ul { + list-style-type: none; + } +} + +/* New tree stuff END */ + + + table.bp { /* Business process table styling starts here */ width: 100%; diff --git a/public/js/module.js b/public/js/module.js index f3856c9..7afc069 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -36,7 +36,7 @@ this.module.on('click', 'div.tiles > div', this.tileClick); this.module.on('click', '.dashboard-tile', this.dashboardTileClick); this.module.on('end', 'div.tiles.sortable', this.tileDropped); - this.module.on('end', 'div.bp.sortable, table.bp tbody.sortable', this.rowDropped); + this.module.on('end', 'div.tree.sortable, ul.sortable', this.rowDropped); this.module.icinga.logger.debug('BP module initialized'); }, @@ -126,6 +126,11 @@ movenode: $(evt.item).data('nodeName') }); + if ($('.placeholder', $target).length) { + evt.oldIndex -= 1; + evt.newIndex -= 1; + } + var data = { csrfToken: $target.data('csrfToken'), movenode: 'movenode', // That's the submit button..