Fix memory leaks from Tippy.js instances and unremoved event listeners
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@
|
|||||||
* with links to account management, my account, and logout.
|
* with links to account management, my account, and logout.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed, onMounted, onBeforeUnmount, ref } from "vue";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import i18next from "@/i18n/i18n";
|
import i18next from "@/i18n/i18n";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
@@ -106,21 +106,27 @@ const onBtnMyAccountClick = async () => {
|
|||||||
await router.push("/my-account");
|
await router.push("/my-account");
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Registers a click listener to close the menu when clicking outside. */
|
/**
|
||||||
const clickOtherPlacesThenCloseMenu = () => {
|
* Closes the menu when clicking outside. Stored as a named
|
||||||
|
* function so it can be removed in onBeforeUnmount.
|
||||||
|
* @param {MouseEvent} event - The click event.
|
||||||
|
*/
|
||||||
|
const handleDocumentClick = (event) => {
|
||||||
const acctMgmtButton = document.getElementById("acct_mgmt_button");
|
const acctMgmtButton = document.getElementById("acct_mgmt_button");
|
||||||
const acctMgmtMenu = document.getElementById("account_menu");
|
const acctMgmtMenu = document.getElementById("account_menu");
|
||||||
|
if (
|
||||||
|
acctMgmtMenu &&
|
||||||
|
acctMgmtButton &&
|
||||||
|
!acctMgmtMenu.contains(event.target) &&
|
||||||
|
!acctMgmtButton.contains(event.target)
|
||||||
|
) {
|
||||||
|
acctMgmtStore.closeAcctMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener("click", (event) => {
|
/** Registers a click listener to close the menu when clicking outside. */
|
||||||
if (
|
const clickOtherPlacesThenCloseMenu = () => {
|
||||||
acctMgmtMenu &&
|
document.addEventListener("click", handleDocumentClick);
|
||||||
acctMgmtButton &&
|
|
||||||
!acctMgmtMenu.contains(event.target) &&
|
|
||||||
!acctMgmtButton.contains(event.target)
|
|
||||||
) {
|
|
||||||
acctMgmtStore.closeAcctMenu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Navigates to the Account Admin page. */
|
/** Navigates to the Account Admin page. */
|
||||||
@@ -161,6 +167,10 @@ onMounted(async () => {
|
|||||||
await getIsAdminValue();
|
await getIsAdminValue();
|
||||||
clickOtherPlacesThenCloseMenu();
|
clickOtherPlacesThenCloseMenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener("click", handleDocumentClick);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ export default function cytoscapeMap(
|
|||||||
// creat tippy.js
|
// creat tippy.js
|
||||||
let tip;
|
let tip;
|
||||||
cy.on("mouseover", "node", function (event) {
|
cy.on("mouseover", "node", function (event) {
|
||||||
|
tip?.destroy();
|
||||||
const node = event.target;
|
const node = event.target;
|
||||||
let ref = node.popperRef();
|
let ref = node.popperRef();
|
||||||
let dummyDomEle = document.createElement("div");
|
let dummyDomEle = document.createElement("div");
|
||||||
@@ -319,7 +320,8 @@ export default function cytoscapeMap(
|
|||||||
if (node.data("label").length > 10) tip.show();
|
if (node.data("label").length > 10) tip.show();
|
||||||
});
|
});
|
||||||
cy.on("mouseout", "node", function (event) {
|
cy.on("mouseout", "node", function (event) {
|
||||||
tip?.hide();
|
tip?.destroy();
|
||||||
|
tip = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// here we remember and recall positions
|
// here we remember and recall positions
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
|||||||
// creat tippy.js
|
// creat tippy.js
|
||||||
let tip;
|
let tip;
|
||||||
cy.on("mouseover", "node", function (event) {
|
cy.on("mouseover", "node", function (event) {
|
||||||
|
tip?.destroy();
|
||||||
const node = event.target;
|
const node = event.target;
|
||||||
let ref = node.popperRef();
|
let ref = node.popperRef();
|
||||||
let dummyDomEle = document.createElement("div");
|
let dummyDomEle = document.createElement("div");
|
||||||
@@ -109,6 +110,7 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
|||||||
tip.show();
|
tip.show();
|
||||||
});
|
});
|
||||||
cy.on("mouseout", "node", function (event) {
|
cy.on("mouseout", "node", function (event) {
|
||||||
tip.hide();
|
tip?.destroy();
|
||||||
|
tip = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ const {
|
|||||||
conformanceFileName,
|
conformanceFileName,
|
||||||
} = storeToRefs(conformanceStore);
|
} = storeToRefs(conformanceStore);
|
||||||
|
|
||||||
|
let loadingTimerId = null;
|
||||||
|
|
||||||
// Created logic
|
// Created logic
|
||||||
(async () => {
|
(async () => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
@@ -109,7 +111,7 @@ const {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to initialize conformance:", error);
|
console.error("Failed to initialize conformance:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => (isLoading.value = false), 500);
|
loadingTimerId = setTimeout(() => (isLoading.value = false), 500);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -124,6 +126,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
clearTimeout(loadingTimerId);
|
||||||
conformanceLogId.value = null;
|
conformanceLogId.value = null;
|
||||||
conformanceFilterId.value = null;
|
conformanceFilterId.value = null;
|
||||||
conformanceLogCreateCheckId.value = null;
|
conformanceLogCreateCheckId.value = null;
|
||||||
|
|||||||
@@ -465,7 +465,7 @@
|
|||||||
* and file operations (rename, delete).
|
* and file operations (rename, delete).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ref, computed, watch, onMounted } from "vue";
|
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useMapCompareStore } from "@/stores/mapCompareStore";
|
import { useMapCompareStore } from "@/stores/mapCompareStore";
|
||||||
@@ -917,19 +917,29 @@ function getGridSortData(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the active file selection when clicking outside a list item.
|
||||||
|
* @param {MouseEvent} e - The click event.
|
||||||
|
*/
|
||||||
|
const handleWindowClick = (e) => {
|
||||||
|
const clickedLi = e.target.closest("li");
|
||||||
|
if (!clickedLi || !clickedLi.id.startsWith("li")) isActive.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
// Mounted
|
// Mounted
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
store.fetchAllFiles();
|
store.fetchAllFiles();
|
||||||
window.addEventListener("click", (e) => {
|
window.addEventListener("click", handleWindowClick);
|
||||||
const clickedLi = e.target.closest("li");
|
|
||||||
if (!clickedLi || !clickedLi.id.startsWith("li")) isActive.value = null;
|
|
||||||
});
|
|
||||||
// Add the .scrollbar class to the DataTable tbody
|
// Add the .scrollbar class to the DataTable tbody
|
||||||
const tbodyElement = document.querySelector(".p-datatable-tbody");
|
const tbodyElement = document.querySelector(".p-datatable-tbody");
|
||||||
tbodyElement?.classList.add("scrollbar");
|
tbodyElement?.classList.add("scrollbar");
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener("click", handleWindowClick);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@reference "../../assets/tailwind.css";
|
@reference "../../assets/tailwind.css";
|
||||||
|
|||||||
Reference in New Issue
Block a user