Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
14e45e9
Move action-list from icingadb-web as is
sukhwinder33445 Jun 26, 2024
1dd04ff
ActionList: Make it stand alone widget
sukhwinder33445 Jun 26, 2024
ab0fcf2
ActionList: Define const `listIdentifier` and `listItemIdentifier` va…
sukhwinder33445 Jun 26, 2024
5220cc7
ActionList: First search for the action-list in the focussed container
sukhwinder33445 Jun 27, 2024
c50c2a6
ActionList: remove click selector `not(.page-selector)`
sukhwinder33445 Jun 27, 2024
92caf69
ActionList: OnKeyDown: find next selectable list item based on data-attr
sukhwinder33445 Jun 27, 2024
c507070
ActionList: dont select lastActivated as fallback
sukhwinder33445 Jun 27, 2024
b99a738
ActionList: Remove load-more functionality
sukhwinder33445 Jun 27, 2024
911c055
ActionList: getDirectionalNext: return only selectable next item or n…
sukhwinder33445 Jun 27, 2024
84f63e2
ActionList: Simplify code
sukhwinder33445 Jun 27, 2024
5788d63
ActionList: Load items properly after auto/manual page refresh
sukhwinder33445 Jul 1, 2024
44889d9
ActionList: Unbind the keydown event listner properly
sukhwinder33445 Jul 4, 2024
ea11f58
ActionList: Introduce event `selection-start` and `selection-end`
sukhwinder33445 Jul 4, 2024
ee64395
ActionList: load(): Clear known selection if no detail url given
sukhwinder33445 Jul 16, 2024
d4d94ad
ActionList: Rename `notJquery` to `$`
sukhwinder33445 Jul 16, 2024
21273f3
ActionList: Introduce the `processing` property with getter/setter
sukhwinder33445 Jul 18, 2024
2fd0de1
Introduce `ActionListBehavior`
sukhwinder33445 Jul 18, 2024
976c70b
Add `LoadMore` and `LoadMoreBehavior`
sukhwinder33445 Jul 18, 2024
c1d3ed2
ActionList: Remove special handeling for `.dashboard`
sukhwinder33445 Jul 18, 2024
2766a5e
ActionList: Let the ActionList know if it is the primary list
sukhwinder33445 Jul 18, 2024
c81a257
ActionListBehavior: Don't return before creating actionLists on #col2
sukhwinder33445 Jul 19, 2024
571906f
ActionListBehavior: getActionLists(): Return #col1 lists if no param …
sukhwinder33445 Jul 19, 2024
c2ab9b3
ActionList: Add class ptoperty `isMultiSelectable`
sukhwinder33445 Jul 19, 2024
792ec91
ActionList: Add footer count only to primary lists
sukhwinder33445 Jul 19, 2024
ca189a2
ActionList: Don't navigate if only `a` is pressed
sukhwinder33445 Jul 19, 2024
80978e0
ActionList: getDirectionNext(): Return list's first/last elem if null…
sukhwinder33445 Jul 29, 2024
f8d10bf
ActionList: Simplify code
sukhwinder33445 Jul 29, 2024
d9b8364
ActionList: Use bool `isArrowUp` param instead event.key
sukhwinder33445 Jul 30, 2024
32be9bd
ActionList: Take items with css propery `display:contents` into account
sukhwinder33445 Jul 30, 2024
b6c1d7a
ActionList: load(): Scroll active item into view
sukhwinder33445 Jul 30, 2024
34be0b4
(ActionList|LoadMore)Behavior: Require `Icinga` from defined path and…
sukhwinder33445 Nov 4, 2025
e0fd67e
ActionList: Add properties as fields and required setters
sukhwinder33445 Nov 21, 2025
028d756
ActionList: Unset `lastActivatedItemUrl` when `(detail)column-closed`…
sukhwinder33445 Nov 21, 2025
16f0134
ActionList: Generelize condition for current list check
sukhwinder33445 Nov 25, 2025
4c7f912
ActionListBehavior::onColumnMoved(): Recheck if list is now primary
sukhwinder33445 Nov 25, 2025
ae9d475
ActionList: Only navigate in current view lists
sukhwinder33445 Nov 25, 2025
4a81af3
ActionList: Remove list conparison
sukhwinder33445 Nov 25, 2025
8d41732
Update footer reference on auto-refresh
sukhwinder33445 Nov 27, 2025
27b245e
Let the ActionList constructor calls bind()
sukhwinder33445 Nov 27, 2025
e4cf8b6
ActionList: Remove eventListener on `destroy()` call
sukhwinder33445 Nov 27, 2025
f5813e2
wip: todo
sukhwinder33445 Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions asset/js/compat/ActionListBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
define(["../widget/ActionList", "icinga/legacy-app/Icinga"],function (ActionList, Icinga) {

"use strict";

class ActionListBehavior extends Icinga.EventListener {
constructor(icinga) {
super(icinga);

this.on('beforerender', '#main > .container', this.onBeforeRender, this);
this.on('rendered', '#main > .container', this.onRendered, this);
this.on('close-column', '#main > #col2', this.onColumnClose, this);
this.on('column-moved', this.onColumnMoved, this);
this.on('selection-start', this.onSelectionStart, this);
this.on('selection-end', this.onSelectionEnd, this);
this.on('all-deselected', this.allDeselected, this);

/**
* Action lists
*
* @type {WeakMap<object, ActionList>}
* @private
*/
this._actionLists = new WeakMap();

/**
* Cached action lists
*
* Holds values only during the time between `beforerender` and `rendered`
*
* @type {{}}
* @private
*/
this._cachedActionLists = {};
}

/**
* @param event
* @param content
* @param action
* @param autorefresh
* @param scripted
*/
onBeforeRender(event, content, action, autorefresh, scripted) {
if (! autorefresh) {
return;
}

let _this = event.data.self;
let lists = _this.getActionLists(event.target)

// Remember current instances
lists.forEach((list) => {
let actionList = _this._actionLists.get(list);
if (actionList) {
_this._cachedActionLists[_this.icinga.utils.getDomPath(list).join(' > ')] = actionList;
}
});
}

/**
* @param event
* @param autorefresh
* @param scripted
*/
onRendered(event, autorefresh, scripted) {
let _this = event.data.self;
let container = event.target;
let detailUrl = _this.getDetailUrl();

if (autorefresh) {
// Apply remembered instances
for (let listPath in _this._cachedActionLists) {
let actionList = _this._cachedActionLists[listPath];
let list = container.querySelector(listPath);
if (list !== null) {
actionList.refresh(list, container.querySelector('.footer'), detailUrl);
_this._actionLists.set(list, actionList);
} else {
actionList.destroy();
}

delete _this._cachedActionLists[listPath];
}
}

let lists = _this.getActionLists(event.currentTarget);
lists.forEach(list => {
let actionList = _this._actionLists.get(list);
if (! actionList) {
let isPrimary = _this.isPrimaryList(list);
let isMultiSelectable = list.matches('[data-icinga-multiselect-url]');
let container = list.closest('.container');
let footer = container.querySelector('.footer');
if (! footer && isPrimary && isMultiSelectable) {
footer = document.createElement('div');
footer.classList.add('footer')
footer.dataset.actionListAutomaticallyAdded = true;

container.appendChild(footer);
}

actionList = new ActionList(list)
.setIsPrimary(isPrimary)
.setIsMultiSelectable(isMultiSelectable)
.setFooter(footer);

actionList.load(detailUrl);

_this._actionLists.set(list, actionList);
} else {
actionList.load(detailUrl); // navigated back to the same page
}
});

if (event.target.id === 'col2') { // navigated back/forward and the detail url is changed
let lists = _this.getActionLists();
lists.forEach(list => {
let actionList = _this._actionLists.get(list);

if (actionList) {
actionList.load(detailUrl);
}
});
}
}

onColumnClose(event)
{
let _this = event.data.self;
let lists = _this.getActionLists();
lists.forEach((list) => {
let actionList = _this._actionLists.get(list);
if (actionList) {
actionList.load();
}
});
}

/**
* Triggers when column is moved to left or right
*
* @param event
* @param sourceId The content is moved from
*/
onColumnMoved(event, sourceId) {
// col1 moved to col2 (browser-back)
let col1Moved = event.target.id === 'col2' && sourceId === 'col1';

// col2 moved to col1 (col1 closed, browser-forward)
let col2Moved = event.target.id === 'col1' && sourceId === 'col2';

if (col1Moved || col2Moved) {
let _this = event.data.self;
let lists = _this.getActionLists(event.target);
lists.forEach((list) => {
let actionList = _this._actionLists.get(list);
if (actionList) {
if (col1Moved) {
actionList.setIsPrimary(false).load();
} else if (col2Moved) {
actionList.setIsPrimary(_this.isPrimaryList(list));
}
}
});
}
}

/**
* Selection started and in process
*
* @param event
*/
onSelectionStart(event) {
const container = event.target.closest('.container');
container.dataset.suspendAutorefresh = '';
}

/**
* Triggers when selection ends, the url can be loaded now
* @param event
*/
onSelectionEnd(event) {
let _this = event.data.self;

let req = _this.icinga.loader.loadUrl(
event.detail.url,
_this.icinga.loader.getLinkTargetFor($(event.target.firstChild))
);

req.always((_, __, errorThrown) => {
if (errorThrown !== 'abort') {
delete event.target.closest('.container').dataset.suspendAutorefresh;
event.detail.actionList.setProcessing(false);
}
});
}

allDeselected(event) {
let _this = event.data.self;
if (_this.icinga.loader.getLinkTargetFor($(event.target), false).attr('id') === 'col2') {
_this.icinga.ui.layout1col();
_this.icinga.history.pushCurrentState();
delete event.target.closest('.container').dataset.suspendAutorefresh;
}
}

getDetailUrl() {
return this.icinga.utils.parseUrl(
this.icinga.history.getCol2State().replace(/^#!/, '')
);
}

/**
* Whether the specified list should be considered the primary list
*
* @param {Element} list
*
* @return {boolean}
*/
isPrimaryList(list) {
return list.parentElement.matches('#main > #col1 > .content');
}

/**
* Get action lists from the given element
*
* If element is not provided, all action lists from col1 will be returned
*
* @param element
*
* @return NodeList
*/
getActionLists(element = null) {
if (element === null) {
return document.querySelectorAll('#col1 [data-interactable-action-list]');
}

return element.querySelectorAll('[data-interactable-action-list]');
}
}

Icinga.Behaviors = Icinga.Behaviors || {};

Icinga.Behaviors.ActionListBehavior = ActionListBehavior;
});
86 changes: 86 additions & 0 deletions asset/js/compat/LoadMoreBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
define(["../widget/LoadMore", "icinga/legacy-app/Icinga"],function (LoadMore, Icinga) {

"use strict";

class LoadMoreBehavior extends Icinga.EventListener {
constructor(icinga) {
super(icinga);

this.on('rendered', '#main > .container', this.onRendered, this);
this.on('load', this.onLoad, this);

/**
* Load More elements
*
* @type {WeakMap<object, LoadMore>}
* @private
*/
this._loadMoreElements = new WeakMap();
}

/**
* @param event
*/
onRendered(event)
{
let _this = event.data.self;

event.currentTarget.querySelectorAll('.load-more').forEach(element => {
_this._loadMoreElements.set(element, new LoadMore(element));
});
}

onLoad(event) {
let _this = event.data.self;
let anchor = event.target;
let showMore = anchor.parentElement;
var progressTimer = _this.icinga.timer.register(function () {
var label = anchor.innerText;

var dots = label.substr(-3);
if (dots.slice(0, 1) !== '.') {
dots = '. ';
} else {
label = label.slice(0, -3);
if (dots === '...') {
dots = '. ';
} else if (dots === '.. ') {
dots = '...';
} else if (dots === '. ') {
dots = '.. ';
}
}

anchor.innerText = label + dots;
}, null, 250);

let url = anchor.getAttribute('href');

let req = _this.icinga.loader.loadUrl(
// Add showCompact, we don't want controls in paged results
_this.icinga.utils.addUrlFlag(url, 'showCompact'),
$(showMore.parentElement),
undefined,
undefined,
'append',
false,
progressTimer
);
req.addToHistory = false;
req.done(function () {
showMore.remove();

// Set data-icinga-url to make it available for Icinga.History.getCurrentState()
req.$target.closest('.container').data('icingaUrl', url);

_this.icinga.history.replaceCurrentState();
});

return req;
}
}

Icinga.Behaviors = Icinga.Behaviors || {};

Icinga.Behaviors.ActionListBehavior = LoadMoreBehavior;
});
Loading
Loading