diff --git a/src/pages/answer/queryGraph/QueryGraph.jsx b/src/pages/answer/queryGraph/QueryGraph.jsx index 41eb97e0..f7b9c7f8 100644 --- a/src/pages/answer/queryGraph/QueryGraph.jsx +++ b/src/pages/answer/queryGraph/QueryGraph.jsx @@ -74,7 +74,7 @@ export default function QueryGraph({ query_graph }) { // .force('forceX', d3.forceX(width / 2).strength(0.02)) .force('forceY', d3.forceY(height / 2).strength(0.2)) .force('collide', d3.forceCollide().radius(nodeRadius * 2)) - .force('link', d3.forceLink(edges).id((d) => d.id).distance(edgeLength).strength(1)) + .force('link', d3.forceLink(edges).id((d) => d.id).distance(edgeLength).strength(0)) .on('tick', () => { node .attr('transform', (d) => { @@ -130,7 +130,7 @@ export default function QueryGraph({ query_graph }) { const { name } = d; return name || 'Any'; }) - .each(graphUtils.ellipsisOverflow)); + .each(graphUtils.fitTextIntoCircle)); edges = edgeUtils.addEdgeCurveProperties(edges); edge = edge.data(edges) diff --git a/src/pages/answer/resultsTable/ResultExplorer.jsx b/src/pages/answer/resultsTable/ResultExplorer.jsx index 94473bb6..9165f16f 100644 --- a/src/pages/answer/resultsTable/ResultExplorer.jsx +++ b/src/pages/answer/resultsTable/ResultExplorer.jsx @@ -199,44 +199,14 @@ export default function ResultExplorer({ answerStore }) { .on('click', function () { handleClickNode(d3.select(this).datum()); })) - .call((n) => { - const textGroup = n.append('text') - .attr('class', 'result_node_label') - .style('pointer-events', 'none') - .attr('text-anchor', 'middle') - .style('font-weight', 600) - .attr('alignment-baseline', 'middle') - .style('font-size', (d) => { - const maxFontSize = nodeRadius * 0.4; - const minFontSize = 8; - const textLength = d.name.length; - return `${Math.max(minFontSize, Math.min(maxFontSize, nodeRadius / Math.sqrt(textLength)))}px`; - }); - textGroup.each(function (d) { - const words = d.name.split(' '); - const textElement = d3.select(this); - if (words.length === 1 || d.name.length < 10) { - // Short text, no need to split - textElement.append('tspan') - .attr('x', 0) - .attr('dy', '0em') - .text(d.name); - } else { - // Split into two lines - const middle = Math.ceil(words.length / 2); - const firstLine = words.slice(0, middle).join(' '); - const secondLine = words.slice(middle).join(' '); - textElement.append('tspan') - .attr('x', 0) - .attr('dy', '-0.4em') // Move first line up - .text(firstLine); - textElement.append('tspan') - .attr('x', 0) - .attr('dy', '1.2em') // Move second line down - .text(secondLine); - } - }); - }), + .call((n) => n.append('text') + .attr('class', 'result_node_label') + .style('pointer-events', 'none') + .attr('text-anchor', 'middle') + .style('font-weight', 600) + .attr('alignment-baseline', 'middle') + .text((d) => d.name) + .each(graphUtils.fitTextIntoCircle)), (update) => update, (exit) => exit .transition() diff --git a/src/utils/d3/graph.js b/src/utils/d3/graph.js index ef03f709..40ba152e 100644 --- a/src/utils/d3/graph.js +++ b/src/utils/d3/graph.js @@ -101,6 +101,68 @@ function getBoundedValue(value, upperBound, lowerBound = 0) { return Math.max(lowerBound, Math.min(value, upperBound)); } +function fitTextWithEllipsis(text, el, nodeRadius, fontSize, dy) { + // Set up the SVG and circle + const svg = d3.select('body') + .append('svg') + .attr('width', 300) + .attr('height', 300); + const tempText = svg.append('text') + .attr('font-size', fontSize) + .style('visibility', 'hidden')// Hide temp text + .text(text); + + const targetLength = nodeRadius * 2 * 0.9; + let finalText = text; + let textLength = tempText.node().getComputedTextLength(); + if (textLength > nodeRadius * 2 * 1.25) { + while (textLength > targetLength && finalText.length > 0) { + finalText = finalText.slice(0, -1); + tempText.text(`${finalText.slice(0, -1)}...`); + textLength = tempText.node().getComputedTextLength(); + } + finalText = `${finalText}...`; + } + tempText.remove(); + el.append('tspan') + .attr('x', 0) + .attr('dy', dy) + .text(finalText); +} + +function fitTextIntoCircle() { + const el = d3.select(this); + const textLength = el.node().getComputedTextLength(); + const text = el.text(); + // grab the parent g tag + const parent = el.node().parentNode; + // grab the corresponding circle + const circle = d3.select(parent) + .select('circle'); + // get circle radius + const nodeRadius = circle.attr('r'); + const maxFontSize = nodeRadius * 0.4; + const minFontSize = 9; + const diameter = nodeRadius * 2; + const fontSize = `${Math.max(minFontSize, Math.min(maxFontSize, diameter / Math.sqrt(textLength)))}px`; + el.style('font-size', fontSize); + el.text(''); + const words = text.split(' '); + console.log('before splitting checks'); + console.log(textLength); + if (words.length === 1 || textLength < 10) { + console.log(text); + fitTextWithEllipsis(text, el, nodeRadius, fontSize, '0em'); + } else { + // Split into two lines + const middle = Math.ceil(words.length / 2); + const firstLine = words.slice(0, middle).join(' '); + const secondLine = words.slice(middle).join(' '); + fitTextWithEllipsis(firstLine, el, nodeRadius, fontSize, '-0.4em'); + fitTextWithEllipsis(secondLine, el, nodeRadius, fontSize, '1.2em'); + } +} + /** * Trim and add an ellipsis to the end of long node labels */ @@ -188,6 +250,7 @@ export default { ellipsisOverflow, getEdgeMidpoint, + fitTextIntoCircle, isInside, shouldShowArrow, diff --git a/src/utils/d3/nodes.js b/src/utils/d3/nodes.js index ef5c9377..caaac92b 100644 --- a/src/utils/d3/nodes.js +++ b/src/utils/d3/nodes.js @@ -64,7 +64,7 @@ function enter(node, args) { const { name } = d; return name || 'Something'; }) - .each(graphUtils.ellipsisOverflow)) + .each(graphUtils.fitTextIntoCircle)) // create delete button .call((nodeDelete) => nodeDelete.append('rect') .attr('rx', 5) @@ -135,7 +135,7 @@ function update(node, args) { const { name } = d; return name || 'Something'; }) - .each(graphUtils.ellipsisOverflow)); + .each(graphUtils.fitTextIntoCircle)); } /**