<template> <div> <div :style="depthMargin"> <!-- Checkbox --> <div v-if="isSelectMode" class="select-input-wrapper"> <input class="form-check-input select-input" type="checkbox" :aria-label="$t('Check/Uncheck')" :checked="isSelected(monitor.id)" @click.stop="toggleSelection" /> </div> <router-link :to="monitorURL(monitor.id)" class="item" :class="{ 'disabled': ! monitor.active }"> <div class="row"> <div class="col-9 col-md-8 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> <div class="info"> <Uptime :monitor="monitor" type="24" :pill="true" /> <span v-if="hasChildren" class="collapse-padding" @click.prevent="changeCollapsed"> <font-awesome-icon icon="chevron-down" class="animated" :class="{ collapsed: isCollapsed}" /> </span> {{ monitor.name }} </div> <div v-if="monitor.tags.length > 0" class="tags"> <Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div> <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-3 col-md-4"> <HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" /> </div> </div> <div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> <div class="col-12 bottom-style"> <HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" /> </div> </div> </router-link> </div> <transition name="slide-fade-up"> <div v-if="!isCollapsed" class="childs"> <MonitorListItem v-for="(item, index) in sortedChildMonitorList" :key="index" :monitor="item" :isSelectMode="isSelectMode" :isSelected="isSelected" :select="select" :deselect="deselect" :depth="depth + 1" :filter-func="filterFunc" :sort-func="sortFunc" /> </div> </transition> </div> </template> <script> import HeartbeatBar from "../components/HeartbeatBar.vue"; import Tag from "../components/Tag.vue"; import Uptime from "../components/Uptime.vue"; import { getMonitorRelativeURL } from "../util.ts"; export default { name: "MonitorListItem", components: { Uptime, HeartbeatBar, Tag, }, props: { /** Monitor this represents */ monitor: { type: Object, default: null, }, /** If the user is in select mode */ isSelectMode: { type: Boolean, default: false, }, /** How many ancestors are above this monitor */ depth: { type: Number, default: 0, }, /** Callback to determine if monitor is selected */ isSelected: { type: Function, default: () => {} }, /** Callback fired when monitor is selected */ select: { type: Function, default: () => {} }, /** Callback fired when monitor is deselected */ deselect: { type: Function, default: () => {} }, /** Function to filter child monitors */ filterFunc: { type: Function, default: () => {} }, /** Function to sort child monitors */ sortFunc: { type: Function, default: () => {}, } }, data() { return { isCollapsed: true, }; }, computed: { sortedChildMonitorList() { let result = Object.values(this.$root.monitorList); // Get children result = result.filter(childMonitor => childMonitor.parent === this.monitor.id); // Run filter on children result = result.filter(this.filterFunc); result.sort(this.sortFunc); return result; }, hasChildren() { return this.sortedChildMonitorList.length > 0; }, depthMargin() { return { marginLeft: `${31 * this.depth}px`, }; }, }, watch: { isSelectMode() { // TODO: Resize the heartbeat bar, but too slow // this.$refs.heartbeatBar.resize(); } }, beforeMount() { // Always unfold if monitor is accessed directly if (this.monitor.childrenIDs.includes(parseInt(this.$route.params.id))) { this.isCollapsed = false; return; } // Set collapsed value based on local storage let storage = window.localStorage.getItem("monitorCollapsed"); if (storage === null) { return; } let storageObject = JSON.parse(storage); if (storageObject[`monitor_${this.monitor.id}`] == null) { return; } this.isCollapsed = storageObject[`monitor_${this.monitor.id}`]; }, methods: { /** * Changes the collapsed value of the current monitor and saves * it to local storage * @returns {void} */ changeCollapsed() { this.isCollapsed = !this.isCollapsed; // Save collapsed value into local storage let storage = window.localStorage.getItem("monitorCollapsed"); let storageObject = {}; if (storage !== null) { storageObject = JSON.parse(storage); } storageObject[`monitor_${this.monitor.id}`] = this.isCollapsed; window.localStorage.setItem("monitorCollapsed", JSON.stringify(storageObject)); }, /** * Get URL of monitor * @param {number} id ID of monitor * @returns {string} Relative URL of monitor */ monitorURL(id) { return getMonitorRelativeURL(id); }, /** * Toggle selection of monitor * @returns {void} */ toggleSelection() { if (this.isSelected(this.monitor.id)) { this.deselect(this.monitor.id); } else { this.select(this.monitor.id); } }, }, }; </script> <style lang="scss" scoped> @import "../assets/vars.scss"; .small-padding { padding-left: 5px !important; padding-right: 5px !important; } .collapse-padding { padding-left: 8px !important; padding-right: 2px !important; } // .monitor-item { // width: 100%; // } .tags { margin-top: 4px; padding-left: 67px; display: flex; flex-wrap: wrap; gap: 0; } .collapsed { transform: rotate(-90deg); } .animated { transition: all 0.2s $easing-in; } .select-input-wrapper { float: left; margin-top: 15px; margin-left: 3px; margin-right: 10px; padding-left: 4px; position: relative; z-index: 15; } </style>