diff --git a/dashboard/src/styles/detailPage.scss b/dashboard/src/styles/detailPage.scss
index c880159beb7..fb715063c13 100644
--- a/dashboard/src/styles/detailPage.scss
+++ b/dashboard/src/styles/detailPage.scss
@@ -189,7 +189,7 @@ pre.code-block .json-string {
flex-direction: column;
gap: 12px;
min-width: 280px;
- flex: 0 0 auto;
+ flex: 1 1 0;
}
@media (max-width: 599px) {
@@ -216,6 +216,9 @@ pre.code-block .json-string {
background-color: #ffffff;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
min-height: 80px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
}
.relationship-card__header {
@@ -296,6 +299,7 @@ pre.code-block .json-string {
.relationship-card__content {
display: flex;
flex-direction: column;
+ min-width: 0;
}
.relationship-card__search {
@@ -330,15 +334,18 @@ pre.code-block .json-string {
.relationship-card__item {
font-size: 0.875rem;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
line-height: 1.5;
}
.relationship-card__link {
color: #2563eb;
text-decoration: none;
+ display: inline-block;
+ max-width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
}
.relationship-card__link:hover {
@@ -356,6 +363,12 @@ pre.code-block .json-string {
.relationship-card__text {
color: #334155;
+ display: inline-block;
+ max-width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
}
.relationship-card__empty {
@@ -572,3 +585,18 @@ pre.code-block .json-string {
cursor: pointer !important;
margin-left: 6px !important;
}
+
+.relationship-node-link {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.text-active {
+ color: #1976d2 !important;
+}
+
+.text-deleted {
+ color: #BB5838 !important;
+}
diff --git a/dashboard/src/views/DetailPage/EntityDetailTabs/RelationshipLineage.tsx b/dashboard/src/views/DetailPage/EntityDetailTabs/RelationshipLineage.tsx
index 090f550746e..974e5e5641c 100644
--- a/dashboard/src/views/DetailPage/EntityDetailTabs/RelationshipLineage.tsx
+++ b/dashboard/src/views/DetailPage/EntityDetailTabs/RelationshipLineage.tsx
@@ -48,28 +48,40 @@ import { CloseIcon, LightTooltip } from "@components/muiComponents";
import { useAppSelector } from "@hooks/reducerHook";
import { Link as MUILink } from "@mui/material";
+interface CustomLinkProps {
+ href: string;
+ status: string;
+ guid: string;
+ name: string;
+ typeName: string;
+ params: URLSearchParams | string;
+}
+
const CustomLink = ({
href,
status,
- entityColor,
guid,
name,
typeName,
params
-}: any): any => {
+}: CustomLinkProps): JSX.Element => {
+ const displayLabel = typeName ? `${name} (${typeName})` : name;
return (
-
- {name} ({typeName})
-
+
+
+ {displayLabel}
+
+
);
};
@@ -79,8 +91,8 @@ const RelationshipLineage = ({
relationshipAttributes,
isLoading
}: {
- entity: Record;
- relationshipAttributes?: Record;
+ entity: Record;
+ relationshipAttributes?: Record;
isLoading?: boolean;
}) => {
const entityData = cloneDeep(entity);
@@ -95,13 +107,13 @@ const RelationshipLineage = ({
const zoomOutButtonRef = useRef(null);
const relationshipSVG = useRef(null);
const [drawerOpen, setDrawerOpen] = useState(false);
- const [nodeDetails, setNodeDetails] = useState({});
+ const [nodeDetails, setNodeDetails] = useState>({});
const [searchTerm, setSearchTerm] = useState("");
const [zoomId, setZoomId] = useState("");
- const createData = (entityData: Record) => {
+ const createData = (entityData: Record) => {
let links = [];
- let nodes: Record = {};
+ let nodes: Record = {};
if (entityData && entityData.relationshipAttributes) {
for (const obj in entityData.relationshipAttributes) {
if (!isEmpty(entityData.relationshipAttributes[obj])) {
@@ -146,9 +158,9 @@ const RelationshipLineage = ({
selectedNodeColor = "#4a90e2";
var svg = d3
- .select(svgElement)
- .attr("viewBox", `${-padding} ${-padding} ${width + padding * 2} ${height + padding * 2}`)
- .attr("enable-background", `new ${-padding} ${-padding} ${width + padding * 2} ${height + padding * 2}`),
+ .select(svgElement)
+ .attr("viewBox", `${-padding} ${-padding} ${width + padding * 2} ${height + padding * 2}`)
+ .attr("enable-background", `new ${-padding} ${-padding} ${width + padding * 2} ${height + padding * 2}`),
node,
path;
@@ -187,7 +199,7 @@ const RelationshipLineage = ({
var forceLink = d3
.forceLink()
- .id(function (d: any) {
+ .id(function (d: Record) {
return d.id;
})
.distance(function (d) {
@@ -252,7 +264,7 @@ const RelationshipLineage = ({
d.radius = 25;
return d.radius;
})
- .attr("fill", function (d: any) {
+ .attr("fill", function (d: Record) {
if (d && d.value && d.value.guid == guid) {
if (isAllEntityRelationDeleted({ data: d, type: "node" })) {
return deletedEntityColor;
@@ -472,7 +484,6 @@ const RelationshipLineage = ({
? " deleted-relation"
: "";
let nodeGuid = options.guid;
- let entityColor = obj.color;
let name = obj.name;
let typeName = options.typeName;
let keys = Array.from(searchParams.keys());
@@ -491,7 +502,6 @@ const RelationshipLineage = ({
-
- {name} ({options.typeName})
-
+
+
+ {displayLabel}
+
+
);
};
@@ -617,7 +628,7 @@ const RelationshipLineage = ({
listString.push({getElement(data)});
}
return (
-
+
{/* {listString?.length > 1 && ( */}
{
});
});
+ describe('Tooltip Rendering', () => {
+ it('should render LightTooltip for relationship nodes in drawer', async () => {
+ render(
+
+
+
+ );
+
+ const mockNode = {
+ name: 'Process',
+ value: [
+ {
+ guid: 'proc-1',
+ typeName: 'Process',
+ displayText: 'Test Tooltip Name',
+ entityStatus: 'ACTIVE',
+ relationshipStatus: 'ACTIVE'
+ }
+ ]
+ };
+
+ act(() => {
+ if (mockEnterSelection.clickHandler) {
+ mockEnterSelection.clickHandler(mockNode);
+ }
+ });
+
+ await waitFor(() => {
+ const tooltips = screen.getAllByTestId('light-tooltip');
+ const targetTooltip = tooltips.find(t => t.getAttribute('title') === 'Test Tooltip Name (Process)');
+ expect(targetTooltip).toBeInTheDocument();
+ }, { timeout: 3000 });
+ });
+ });
+
describe('Edge Cases', () => {
it('should handle entity without relationshipAttributes', () => {
const entityWithoutAttributes = {
diff --git a/dashboardv2/public/css/scss/graph.scss b/dashboardv2/public/css/scss/graph.scss
index a369841fb51..1d0ae0bfc13 100644
--- a/dashboardv2/public/css/scss/graph.scss
+++ b/dashboardv2/public/css/scss/graph.scss
@@ -99,6 +99,13 @@
margin-bottom: 5px;
text-align: left;
+ &.entity-list-item {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%;
+ }
+
&.deleted-relation {
.deleteBtn {
padding: 2px 8px !important;
diff --git a/dashboardv2/public/css/scss/relationship.scss b/dashboardv2/public/css/scss/relationship.scss
index 6ea9f83e124..ad08bb6f673 100644
--- a/dashboardv2/public/css/scss/relationship.scss
+++ b/dashboardv2/public/css/scss/relationship.scss
@@ -121,7 +121,7 @@
flex-direction: column;
gap: 12px;
min-width: 280px;
- flex: 0 0 auto;
+ flex: 1 1 0;
}
@media (max-width: 599px) {
@@ -220,6 +220,7 @@
flex-direction: column;
padding: 12px;
min-height: 0;
+ min-width: 0;
overflow: hidden;
.relationship-card-search {
@@ -241,6 +242,7 @@
overflow-y: auto;
overflow-x: hidden;
min-height: 0;
+ min-width: 0;
position: relative;
&::-webkit-scrollbar {
@@ -267,10 +269,14 @@
padding: 0;
margin: 0;
text-align: left;
+ width: 100%;
+ overflow: hidden;
.relationship-card-item {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
+ width: 100%;
+ overflow: hidden;
&:last-child {
border-bottom: none;
@@ -290,7 +296,12 @@
font-size: 13px;
font-family: inherit;
line-height: 1.5;
- word-break: break-word;
+ display: inline-block;
+ max-width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
&:hover {
text-decoration: underline;
@@ -547,4 +558,12 @@
50% {
background-position: -200% 0;
}
-}
\ No newline at end of file
+}
+.entity-type-name {
+ &.active {
+ color: #1976d2;
+ }
+ &.deleted {
+ color: #BB5838;
+ }
+}
diff --git a/dashboardv2/public/css/scss/theme.scss b/dashboardv2/public/css/scss/theme.scss
index 46abb127aed..fa564471994 100644
--- a/dashboardv2/public/css/scss/theme.scss
+++ b/dashboardv2/public/css/scss/theme.scss
@@ -88,6 +88,7 @@ header.atlas-header {
overflow: auto;
padding-top: 15px !important;
padding-bottom: 10px !important;
+ box-sizing: border-box;
&>div {
height: 100%;
@@ -536,7 +537,8 @@ hr[size="10"] {
}
.tooltip-inner {
- max-width: none;
+ max-width: 300px;
+ word-wrap: break-word;
color: #2c2c2c;
background-color: #f9f9f9;
box-shadow: 0px 0px 3px 0px #8080806b;
diff --git a/dashboardv2/public/js/views/detail_page/RelationshipCardsLayoutView.js b/dashboardv2/public/js/views/detail_page/RelationshipCardsLayoutView.js
index 5ded221b458..66917fd894c 100644
--- a/dashboardv2/public/js/views/detail_page/RelationshipCardsLayoutView.js
+++ b/dashboardv2/public/js/views/detail_page/RelationshipCardsLayoutView.js
@@ -489,8 +489,8 @@ define([
var nameValue = item && item.attributes && item.attributes.name ? item.attributes.name : "";
var searchText = _.escape((displayText + " " + qualifiedName + " " + nameValue).toLowerCase());
return "" +
- (href ? "" + _.escape(displayLabel) + "" :
- "" + _.escape(displayLabel) + "") +
+ (href ? "" + _.escape(displayLabel) + "" :
+ "" + _.escape(displayLabel) + "") +
"";
}).join("");
@@ -691,11 +691,26 @@ define([
}
if (this.$el && this.$el.length) {
+ if ($.fn.tooltip) {
+ this.$el.find('[title]').tooltip('destroy');
+ }
this.$el.html(html);
this.bindCardEvents();
+ if ($.fn.tooltip) {
+ this.$el.find('[title]').tooltip({
+ placement: 'bottom',
+ container: 'body'
+ });
+ }
} else {
console.warn("[RelationshipCardsLayoutView] $el not available, cannot render cards");
}
+ },
+
+ onDestroy: function() {
+ if ($.fn.tooltip && this.$el) {
+ this.$el.find('[title]').tooltip('destroy');
+ }
}
});
diff --git a/dashboardv2/public/js/views/graph/RelationshipLayoutView.js b/dashboardv2/public/js/views/graph/RelationshipLayoutView.js
index 9c11c0017b1..d9e6ed8fe0e 100644
--- a/dashboardv2/public/js/views/graph/RelationshipLayoutView.js
+++ b/dashboardv2/public/js/views/graph/RelationshipLayoutView.js
@@ -29,7 +29,7 @@ define([
"utils/Enums",
"utils/UrlLinks",
"platform"
-], function(require, Backbone, RelationshipLayoutViewtmpl, VLineageList, VEntity, Utils, CommonViewFunction, d3, d3Tip, Enums, UrlLinks, platform) {
+], function (require, Backbone, RelationshipLayoutViewtmpl, VLineageList, VEntity, Utils, CommonViewFunction, d3, d3Tip, Enums, UrlLinks, platform) {
"use strict";
var RelationshipLayoutView = Backbone.Marionette.LayoutView.extend(
@@ -60,17 +60,17 @@ define([
},
/** ui events hash */
- events: function() {
+ events: function () {
var events = {};
- events["click " + this.ui.relationshipDetailClose] = function() {
+ events["click " + this.ui.relationshipDetailClose] = function () {
this.toggleInformationSlider({ close: true });
};
events["keyup " + this.ui.searchNode] = "searchNode";
events["click " + this.ui.boxClose] = "toggleBoxPanel";
- events["change " + this.ui.relationshipViewToggle] = function(e) {
+ events["change " + this.ui.relationshipViewToggle] = function (e) {
this.relationshipViewToggle(e.currentTarget.checked);
};
- events["click " + this.ui.noValueToggle] = function(e) {
+ events["click " + this.ui.noValueToggle] = function (e) {
Utils.togglePropertyRelationshipTableEmptyValues({
inputType: this.ui.noValueToggle,
tableEl: this.ui.relationshipDetailValue
@@ -84,19 +84,19 @@ define([
* intialize a new RelationshipLayoutView Layout
* @constructs
*/
- initialize: function(options) {
+ initialize: function (options) {
_.extend(this, _.pick(options, "entity", "entityName", "guid", "actionCallBack", "attributeDefs", "referredEntities", "entityDefCollection"));
this.graphData = this.createData(this.entity);
this.relationshipCardCounts = {};
this.relationshipLoadedCounts = {};
this.cardsViewLoadInProgress = false;
-
- this.handleRelationshipDataUpdate = _.bind(function(payload) {
+
+ this.handleRelationshipDataUpdate = _.bind(function (payload) {
var relationshipAttributes = payload && payload.data ? payload.data : payload;
var relationshipCounts = payload && payload.counts ? payload.counts : this.relationshipCardCounts;
var loadedCounts = payload && payload.loadedCounts ? payload.loadedCounts : this.relationshipLoadedCounts;
var referredEntities = payload && payload.referredEntities ? payload.referredEntities : null;
-
+
if (!relationshipAttributes) {
return;
}
@@ -116,7 +116,7 @@ define([
}
}
}, this);
- this.handleRelationshipLoading = _.bind(function(isLoading) {
+ this.handleRelationshipLoading = _.bind(function (isLoading) {
if (isLoading) {
this.$(".fontLoader").show();
} else {
@@ -124,12 +124,12 @@ define([
}
}, this);
},
- createData: function(entity) {
+ createData: function (entity) {
var that = this,
links = [],
nodes = {};
if (entity && entity.relationshipAttributes) {
- _.each(entity.relationshipAttributes, function(obj, key) {
+ _.each(entity.relationshipAttributes, function (obj, key) {
var relationValue = obj;
if (relationValue && relationValue.entities) {
relationValue = relationValue.entities;
@@ -152,14 +152,14 @@ define([
}
return { nodes: nodes, links: links };
},
- onRender: function() {
+ onRender: function () {
this.isRendered = true;
// Initialize: show card view by default (checked = Card)
this.ui.relationshipViewToggle.prop('checked', true);
this.relationshipViewToggle(true);
},
- onShow: function(argument) {
+ onShow: function (argument) {
// Always create graph on show if graph view is active
var isGraphView = !this.ui.relationshipViewToggle.is(':checked');
if (isGraphView) {
@@ -170,12 +170,12 @@ define([
}
}
this.createTable();
-
+
this.ensureCardsView();
},
- ensureCardsView: function(forceRefresh) {
+ ensureCardsView: function (forceRefresh) {
var that = this;
-
+
if (this.relationshipCardsViewInstance) {
if (forceRefresh) {
this.relationshipCardsViewInstance.cardData = {};
@@ -198,7 +198,7 @@ define([
return;
}
this.cardsViewLoadInProgress = true;
- require(['views/detail_page/RelationshipCardsLayoutView'], function(RelationshipCardsLayoutView) {
+ require(['views/detail_page/RelationshipCardsLayoutView'], function (RelationshipCardsLayoutView) {
try {
if (!that.relationshipCardsView) {
console.warn("[RelationshipLayoutView] Region not available");
@@ -221,22 +221,22 @@ define([
} finally {
that.cardsViewLoadInProgress = false;
}
- }, function(err) {
+ }, function (err) {
console.error("[RelationshipLayoutView] Failed to load RelationshipCardsLayoutView:", err);
that.cardsViewLoadInProgress = false;
});
},
- updateCardsShowEmptyValues: function(showEmptyValues) {
+ updateCardsShowEmptyValues: function (showEmptyValues) {
if (!this.relationshipCardsViewInstance) {
return;
}
this.relationshipCardsViewInstance.showEmptyValues = !!showEmptyValues;
this.relationshipCardsViewInstance.renderCards();
},
- noRelationship: function() {
+ noRelationship: function () {
this.$("svg").html('No relationship data found');
},
- toggleInformationSlider: function(options) {
+ toggleInformationSlider: function (options) {
var panel = this.$(".relationship-node-details");
if (options && options.close) {
panel.removeClass("slide-to-left").addClass("slide-from-left");
@@ -248,10 +248,10 @@ define([
}
}
},
- toggleBoxPanel: function() {
+ toggleBoxPanel: function () {
this.$(".relationship-node-details").removeClass("slide-to-left").addClass("slide-from-left");
},
- searchNode: function(e) {
+ searchNode: function (e) {
var searchString = $(e.currentTarget).val(),
listString = "",
data = this.selectedNodeData,
@@ -259,7 +259,7 @@ define([
activeEntityColor = "#1976d2",
deletedEntityColor = "#BB5838",
defaultEntityColor = "#e0e0e0",
- normalizeEntity = function(entity) {
+ normalizeEntity = function (entity) {
if (!entity) {
return entity;
}
@@ -271,27 +271,29 @@ define([
}
return entity;
}.bind(this),
- getdefault = function(options) {
- return "" + options.name + "
";
+ getdefault = function (options) {
+ var colorClass = options.color === deletedEntityColor ? "deleted" : "active";
+ return "" + options.name + "
";
},
- getWithButton = function(options) {
+ getWithButton = function (options) {
var name = options.name,
guid = options.options.guid,
- entityTypeButton = "";
+ entityTypeButton = "",
+ colorClass = options.color === deletedEntityColor ? "deleted" : "active";
if (guid) {
if (options.entity) {
- entityTypeButton = "" + name + "";
+ entityTypeButton = "" + name + "";
} else if (options.relationship) {
- entityTypeButton = "" + name + "";
+ entityTypeButton = "" + name + "";
} else {
- entityTypeButton = "" + name + "";
+ entityTypeButton = "" + name + "";
}
} else {
- entityTypeButton = "" + name + "
";
+ entityTypeButton = "" + name + "
";
}
return entityTypeButton;
},
- getEntityTypelist = function(options) {
+ getEntityTypelist = function (options) {
var name = options.entityName ? options.entityName : Utils.getName(options, "displayText"),
entityTypeHtml = "";
if (options.entityStatus == "ACTIVE" || options.status == "ACTIVE") {
@@ -327,12 +329,12 @@ define([
};
this.ui.searchNode.hide();
this.$("[data-id='typeName']").text(typeName);
- var getElement = function(options) {
+ var getElement = function (options) {
var name = options.entityName ? options.entityName : Utils.getName(options, "displayText");
var entityTypeButton = getEntityTypelist(options);
return entityTypeButton;
};
- var buildEntityObj = function(item) {
+ var buildEntityObj = function (item) {
var normalized = normalizeEntity(item);
var ref = normalized && normalized.guid ? this.referredEntities && this.referredEntities[normalized.guid] : null;
var displayText = (ref && (ref.displayText || (ref.attributes && ref.attributes.name))) ||
@@ -347,26 +349,26 @@ define([
typeName: typeName
});
}.bind(this);
- var buildListItem = function(item) {
+ var buildListItem = function (item) {
var name = item.entityName || Utils.getName(item, "displayText");
var typeName = item.typeName || "";
var displayLabel = typeName ? name + " (" + typeName + ")" : name;
var href = item.guid ? "#!/detailPage/" + item.guid + "?tabActive=relationship" : "";
var isDeleted = (item.entityStatus || item.status) == "DELETED";
- var color = isDeleted ? deletedEntityColor : activeEntityColor;
+ var colorClass = isDeleted ? "deleted" : "active";
var content = href
- ? "" + _.escape(displayLabel) + ""
- : "" + _.escape(displayLabel) + "";
+ ? "" + _.escape(displayLabel) + ""
+ : "" + _.escape(displayLabel) + "";
return "" + content + "";
};
if (_.isArray(data)) {
- data = _.map(data, function(item) {
+ data = _.map(data, function (item) {
return buildEntityObj(item);
});
if (data.length > 1) {
this.ui.searchNode.show();
}
- _.each(_.sortBy(data, "entityName"), function(val) {
+ _.each(_.sortBy(data, "entityName"), function (val) {
var name = val.entityName || Utils.getName(val, "displayText");
var searchTarget = (name + " " + (val.typeName || "")).toLowerCase();
if (searchString) {
@@ -383,9 +385,18 @@ define([
data = buildEntityObj(data);
listString += buildListItem(data);
}
+ if ($.fn.tooltip) {
+ this.$("[data-id='entityList']").find('[title]').tooltip('destroy');
+ }
this.$("[data-id='entityList']").html(listString);
+ if ($.fn.tooltip) {
+ this.$("[data-id='entityList']").find('[title]').tooltip({
+ placement: 'bottom',
+ container: 'body'
+ });
+ }
},
- createGraph: function(data) {
+ createGraph: function (data) {
//Ref - http://bl.ocks.org/fancellu/2c782394602a93921faff74e594d1bb1
var that = this,
@@ -398,7 +409,7 @@ define([
deletedEntityColor = "#BB5838",
defaultEntityColor = "#e0e0e0",
selectedNodeColor = "#4a90e2";
- var getNodeCount = function(node) {
+ var getNodeCount = function (node) {
if (!node) {
return 0;
}
@@ -434,7 +445,7 @@ define([
var zoom = d3
.zoom()
.scaleExtent([0.1, 4])
- .on("zoom", function() {
+ .on("zoom", function () {
container.attr("transform", d3.event.transform);
});
@@ -456,7 +467,7 @@ define([
.attr("xoverflow", "visible")
.append("svg:path")
.attr("d", "M 0,-5 L 10,0 L 0,5")
- .attr("fill", function(d) {
+ .attr("fill", function (d) {
return d === "deletedLink" ? deletedEntityColor : activeEntityColor;
})
.attr("stroke", "none");
@@ -467,7 +478,7 @@ define([
"link",
d3
.forceLink(links)
- .id(function(d) {
+ .id(function (d) {
return d.name;
})
.distance(150)
@@ -476,45 +487,45 @@ define([
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collision", d3.forceCollide().radius(50));
- path = container
+ path = container
.append("g")
- .selectAll("path")
- .data(links)
- .enter()
+ .selectAll("path")
+ .data(links)
+ .enter()
.append("path")
- .attr("class", "relatioship-link")
- .attr("marker-end", function(d) {
+ .attr("class", "relatioship-link")
+ .attr("marker-end", function (d) {
return isAllEntityRelationDeleted({ data: d, type: "link" }) ? "url(#deletedLink)" : "url(#activeLink)";
})
- .attr("stroke", function(d) {
+ .attr("stroke", function (d) {
return isAllEntityRelationDeleted({ data: d, type: "link" }) ? deletedEntityColor : activeEntityColor;
- });
+ });
- node = container
+ node = container
.append("g")
- .selectAll(".node")
- .data(nodes)
- .enter()
- .append("g")
- .attr("class", "node")
- .on("mousedown", function() {
- d3.event.preventDefault();
- })
- .on("click", function(d) {
- if (d3.event.defaultPrevented) return; // ignore drag
- if (d && d.value && d.value.guid == that.guid) {
- that.ui.boxClose.trigger("click");
- return;
- }
- that.toggleBoxPanel({ el: that.$(".relationship-node-details") });
- that.ui.searchNode.data({ obj: d });
- $(this)
- .find("circle")
- .addClass("node-detail-highlight");
- that.updateRelationshipDetails({ obj: d });
- })
- .call(
- d3
+ .selectAll(".node")
+ .data(nodes)
+ .enter()
+ .append("g")
+ .attr("class", "node")
+ .on("mousedown", function () {
+ d3.event.preventDefault();
+ })
+ .on("click", function (d) {
+ if (d3.event.defaultPrevented) return; // ignore drag
+ if (d && d.value && d.value.guid == that.guid) {
+ that.ui.boxClose.trigger("click");
+ return;
+ }
+ that.toggleBoxPanel({ el: that.$(".relationship-node-details") });
+ that.ui.searchNode.data({ obj: d });
+ $(this)
+ .find("circle")
+ .addClass("node-detail-highlight");
+ that.updateRelationshipDetails({ obj: d });
+ })
+ .call(
+ d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
@@ -522,105 +533,105 @@ define([
);
node.append("circle")
- .attr("r", function() {
+ .attr("r", function () {
return 25;
- })
- .attr("fill", function(d) {
+ })
+ .attr("fill", function (d) {
if (d.name === that.entity.typeName) {
- return selectedNodeColor;
- } else {
+ return selectedNodeColor;
+ } else {
return isAllEntityRelationDeleted({ data: d, type: "node" }) ? deletedEntityColor : activeEntityColor;
- }
- })
+ }
+ })
.attr("stroke", "#fff")
.attr("stroke-width", "2px")
.style("cursor", "pointer")
- .on("click", function(d) {
+ .on("click", function (d) {
if (d && d.value && d.value.guid == that.guid) {
return;
}
that.selectedNodeData = d.value;
that.selectedNodeType = d.name;
-
+
// Show the panel
var panel = that.$(".relationship-node-details");
panel.removeClass("slide-to-left").addClass("slide-from-left");
-
+
// Trigger after a small delay to ensure DOM is ready
- setTimeout(function() {
+ setTimeout(function () {
panel.removeClass("slide-from-left").addClass("slide-to-left");
that.searchNode({ currentTarget: that.ui.searchNode });
}, 10);
- });
+ });
- node.append("text")
- .attr("x", 0)
- .attr("y", 0)
- .attr("dy", function() {
- return 25 - 17;
- })
- .attr("text-anchor", "middle")
- .style("font-family", "FontAwesome")
- .style("font-size", "25px")
- .attr("class", "relationship-node-icon")
- .text(function(d) {
- var iconObj = Enums.graphIcon[d.name];
- if (iconObj && iconObj.textContent) {
- return iconObj.textContent;
- }
- if (d && _.isArray(d.value) && d.value.length > 1) {
- return "\uf0c5";
- }
- return "\uf016";
- })
- .attr("fill", "#fff");
-
- var countBox = node.append("g");
-
- countBox
- .append("circle")
- .attr("cx", 18)
- .attr("cy", -20)
- .attr("class", "relationship-node-count")
- .attr("r", function(d) {
- var count = getNodeCount(d);
- if (count > 1) {
- return 9;
- }
- });
+ node.append("text")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("dy", function () {
+ return 25 - 17;
+ })
+ .attr("text-anchor", "middle")
+ .style("font-family", "FontAwesome")
+ .style("font-size", "25px")
+ .attr("class", "relationship-node-icon")
+ .text(function (d) {
+ var iconObj = Enums.graphIcon[d.name];
+ if (iconObj && iconObj.textContent) {
+ return iconObj.textContent;
+ }
+ if (d && _.isArray(d.value) && d.value.length > 1) {
+ return "\uf0c5";
+ }
+ return "\uf016";
+ })
+ .attr("fill", "#fff");
+
+ var countBox = node.append("g");
+
+ countBox
+ .append("circle")
+ .attr("cx", 18)
+ .attr("cy", -20)
+ .attr("class", "relationship-node-count")
+ .attr("r", function (d) {
+ var count = getNodeCount(d);
+ if (count > 1) {
+ return 9;
+ }
+ });
- countBox
- .append("text")
- .attr("dx", 18)
- .attr("dy", -16)
- .attr("text-anchor", "middle")
- .attr("fill", defaultEntityColor)
- .attr("class", "relationship-node-count")
- .text(function(d) {
- var count = getNodeCount(d);
- if (count > 1) {
- return count;
- }
- });
+ countBox
+ .append("text")
+ .attr("dx", 18)
+ .attr("dy", -16)
+ .attr("text-anchor", "middle")
+ .attr("fill", defaultEntityColor)
+ .attr("class", "relationship-node-count")
+ .text(function (d) {
+ var count = getNodeCount(d);
+ if (count > 1) {
+ return count;
+ }
+ });
- node
- .append("text")
- .attr("x", -15)
- .attr("y", "35")
- .attr("class", "relationship-node-label")
- .text(function(d) {
- return d.name;
- });
+ node
+ .append("text")
+ .attr("x", -15)
+ .attr("y", "35")
+ .attr("class", "relationship-node-label")
+ .text(function (d) {
+ return d.name;
+ });
- simulation.on("tick", function() {
- path.attr("d", function(d) {
+ simulation.on("tick", function () {
+ path.attr("d", function (d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
- node.attr("transform", function(d) {
+ node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
@@ -659,7 +670,7 @@ define([
}
return (
- _.findIndex(d.value, function(val) {
+ _.findIndex(d.value, function (val) {
if (type == "node") {
return (val.entityStatus || val.status) == "ACTIVE";
} else {
@@ -668,7 +679,7 @@ define([
}) == -1
);
}
- var zoomClick = function() {
+ var zoomClick = function () {
var scaleFactor = 0.8;
if (this.id === 'zoom_in') {
scaleFactor = 1.3;
@@ -678,7 +689,7 @@ define([
d3.selectAll(this.$('.lineageZoomButton')).on('click', zoomClick);
},
- createTable: function() {
+ createTable: function () {
this.entityModel = new VEntity({});
var table = CommonViewFunction.propertyTable({
scope: this,
@@ -691,7 +702,7 @@ define([
tableEl: this.ui.relationshipDetailValue
});
},
- relationshipViewToggle: function(checked) {
+ relationshipViewToggle: function (checked) {
var that = this;
// In the original code: checked = Table, unchecked = Graph
@@ -705,12 +716,12 @@ define([
this.ui.relationshipDetailTable.hide();
this.ui.relationshipDetailValue.hide();
this.ui.relationshipCardsView.show();
-
+
// Render card view if not already rendered
this.ensureCardsView();
-
+
// Force a re-render after a short delay to ensure DOM is ready
- setTimeout(function() {
+ setTimeout(function () {
if (that.relationshipCardsViewInstance) {
that.relationshipCardsViewInstance.renderCards();
}
@@ -724,7 +735,7 @@ define([
this.ui.relationshipDetailTable.show();
this.ui.relationshipDetailValue.show();
this.ui.relationshipCardsView.hide();
-
+
// Ensure graph is created if it hasn't been created yet
if (this.graphData && !_.isEmpty(this.graphData.links)) {
// Clear existing graph and recreate
@@ -735,8 +746,11 @@ define([
}
}
},
-
- onDestroy: function() {
+
+ onDestroy: function () {
+ if ($.fn.tooltip && this.$el) {
+ this.$el.find('[title]').tooltip('destroy');
+ }
this.cardsViewLoadInProgress = false;
if (this.relationshipCardsViewInstance) {
this.relationshipCardsViewInstance.destroy();