feat(dashboard): add expand/collapse all groups button (#6743)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
parent
a0d73aba1a
commit
bf9b734f6c
@ -35,7 +35,13 @@
|
||||
:aria-label="selectAll ? $t('deselectAllMonitorsAria') : $t('selectAllMonitorsAria')"
|
||||
/>
|
||||
|
||||
<MonitorListFilter :filterState="filterState" @update-filter="updateFilter" />
|
||||
<MonitorListFilter
|
||||
:filterState="filterState"
|
||||
:allCollapsed="allGroupsCollapsed"
|
||||
:hasGroups="groupMonitors.length >= 2"
|
||||
@update-filter="updateFilter"
|
||||
@toggle-collapse-all="toggleCollapseAll"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -100,8 +106,8 @@
|
||||
</div>
|
||||
|
||||
<MonitorListItem
|
||||
v-for="(item, index) in sortedMonitorList"
|
||||
:key="index"
|
||||
v-for="item in sortedMonitorList"
|
||||
:key="`${item.id}-${collapseKey}`"
|
||||
:monitor="item"
|
||||
:isSelectMode="selectMode"
|
||||
:isSelected="isSelected"
|
||||
@ -154,6 +160,7 @@ export default {
|
||||
active: null,
|
||||
tags: null,
|
||||
},
|
||||
collapseKey: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -229,6 +236,38 @@ export default {
|
||||
this.searchText !== ""
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets all group monitors at root level that have children
|
||||
* @returns {Array} Array of group monitors with children
|
||||
*/
|
||||
groupMonitors() {
|
||||
const monitors = Object.values(this.$root.monitorList);
|
||||
return monitors.filter(
|
||||
(m) => m.type === "group" && m.parent === null && monitors.some((child) => child.parent === m.id)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if all groups are collapsed.
|
||||
* Note: collapseKey is included to force re-computation when toggleCollapseAll()
|
||||
* updates localStorage, since Vue cannot detect localStorage changes.
|
||||
* @returns {boolean} True if all groups are collapsed
|
||||
*/
|
||||
allGroupsCollapsed() {
|
||||
// collapseKey forces this computed to re-evaluate after localStorage updates
|
||||
if (this.collapseKey < 0 || this.groupMonitors.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const storage = window.localStorage.getItem("monitorCollapsed");
|
||||
if (storage === null) {
|
||||
return true; // Default is collapsed
|
||||
}
|
||||
|
||||
const storageObject = JSON.parse(storage);
|
||||
return this.groupMonitors.every((group) => storageObject[`monitor_${group.id}`] !== false);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
searchText() {
|
||||
@ -303,6 +342,26 @@ export default {
|
||||
updateFilter(newFilter) {
|
||||
this.filterState = newFilter;
|
||||
},
|
||||
/**
|
||||
* Toggle collapse state for all group monitors
|
||||
* @returns {void}
|
||||
*/
|
||||
toggleCollapseAll() {
|
||||
const shouldCollapse = !this.allGroupsCollapsed;
|
||||
|
||||
let storageObject = {};
|
||||
const storage = window.localStorage.getItem("monitorCollapsed");
|
||||
if (storage !== null) {
|
||||
storageObject = JSON.parse(storage);
|
||||
}
|
||||
|
||||
this.groupMonitors.forEach((group) => {
|
||||
storageObject[`monitor_${group.id}`] = shouldCollapse;
|
||||
});
|
||||
|
||||
window.localStorage.setItem("monitorCollapsed", JSON.stringify(storageObject));
|
||||
this.collapseKey++;
|
||||
},
|
||||
/**
|
||||
* Deselect a monitor
|
||||
* @param {number} id ID of monitor
|
||||
@ -731,6 +790,7 @@ export default {
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding-right: 30px;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.monitor-item {
|
||||
|
||||
@ -137,6 +137,15 @@
|
||||
</li>
|
||||
</template>
|
||||
</MonitorListFilterDropdown>
|
||||
<button
|
||||
v-if="hasGroups"
|
||||
type="button"
|
||||
class="btn btn-outline-normal btn-collapse-all"
|
||||
:title="allCollapsed ? $t('Expand All Groups') : $t('Collapse All Groups')"
|
||||
@click="$emit('toggle-collapse-all')"
|
||||
>
|
||||
<font-awesome-icon :icon="allCollapsed ? 'folder' : 'folder-open'" fixed-width />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -155,8 +164,16 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
allCollapsed: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
hasGroups: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["updateFilter"],
|
||||
emits: ["updateFilter", "toggle-collapse-all"],
|
||||
data() {
|
||||
return {
|
||||
tagsList: [],
|
||||
@ -322,4 +339,8 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-collapse-all {
|
||||
transition: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -53,6 +53,8 @@ import {
|
||||
faInfoCircle,
|
||||
faClone,
|
||||
faCertificate,
|
||||
faFolder,
|
||||
faFolderOpen,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(
|
||||
@ -103,6 +105,8 @@ library.add(
|
||||
faInfoCircle,
|
||||
faClone,
|
||||
faCertificate,
|
||||
faFolder,
|
||||
faFolderOpen,
|
||||
);
|
||||
|
||||
export { FontAwesomeIcon };
|
||||
|
||||
@ -1368,5 +1368,7 @@
|
||||
"Expected TLS Alert": "Expected TLS Alert",
|
||||
"None (Successful Connection)": "None (Successful Connection)",
|
||||
"expectedTlsAlertDescription": "Select the TLS alert you expect the server to return. Use {code} to verify mTLS endpoints reject connections without client certificates. See {link} for details.",
|
||||
"TLS Alert Spec": "RFC 8446"
|
||||
"TLS Alert Spec": "RFC 8446",
|
||||
"Expand All Groups": "Expand All Groups",
|
||||
"Collapse All Groups": "Collapse All Groups"
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div v-if="!$root.isMobile" class="col-12 col-md-5 col-xl-4">
|
||||
<div v-if="!$root.isMobile" class="col-12 col-md-5 col-xl-4 ps-0">
|
||||
<div>
|
||||
<router-link to="/add" class="btn btn-primary mb-3">
|
||||
<font-awesome-icon icon="plus" />
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{{ $t("Quick Stats") }}
|
||||
</h1>
|
||||
|
||||
<div class="shadow-box big-padding text-center mb-4">
|
||||
<div class="shadow-box big-padding text-center mb-3">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ $t("Up") }}</h3>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user