Skip to content

Commit

Permalink
Better text handling in the graph
Browse files Browse the repository at this point in the history
- Display more texts when necessary
- Add tooltips
  • Loading branch information
Sheng Ran committed Feb 15, 2018
1 parent 3367c46 commit 0eb6359
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 224 deletions.
67 changes: 40 additions & 27 deletions core/src/main/javascript/graph/dagger/render/nodesAndEdges.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { nodeKind, edgeKind } from "../layout/symbolic/annotatedGraph";
import * as d3 from "d3";

import forEach from "lodash/forEach";
import { textWrap } from "d3plus-text";
import { forEach, identity } from "lodash";
import { interpolatePath } from "d3-interpolate-path";

const transitionDuration = 500;
const transitionDelay = 0;
const transitionEase = d3.easeLinear;

const computeNewWidth = (
label: string,
Expand All @@ -26,9 +25,8 @@ const computeNewWidth = (
};

const computeNewLabel = (label: string, widthMax: number) => {
const labelLength = Array.from(label).length;
const labelLength = label.length;
const overflowCharacters = Math.max(labelLength - widthMax, 0);

const labelToDisplay = overflowCharacters > 0
? label.substring(0, labelLength - overflowCharacters - 4) + "..."
: label;
Expand Down Expand Up @@ -284,15 +282,14 @@ export const drawNode = (
{ x, y, width, height, id, name, kind },
tags
) => {
const fontSize = 12;
const node = domContainer.append("g").attr("id", id).attr("class", "oneNode");

const newWidth = getRealWidth(id, name, width);
const nameToDisplay = computeNewLabel(name, widthMax);
const color = (tags && tags[id]) || "#E1EFFA";

node
.append("rect")
.attr("width", truncate(newWidth))
.attr("width", truncate(width))
.attr("height", truncate(height))
.style("fill", color)
.attr("rx", 4)
Expand All @@ -302,32 +299,48 @@ export const drawNode = (
.style("box-shadow", "10px 10px 5px #888888;")
.attr("filter", "url(#blur)");

const wrapped = textWrap()
.fontSize(fontSize)
.overflow(true)
.width(newWidth * 0.8)(name);
const [first, ...rest] = wrapped.lines;
const textPadding = (height - 2 * fontSize) / 4;
// display 2 lines at most, if there's enough space
if (rest.length === 0 || textPadding < 3) {
node
.append("text")
.style("text-anchor", "middle")
.text(
computeNewLabel(first + rest.join(""), newWidth / (fontSize / 1.5))
);
} else {
node
.append("text")
.selectAll("tspan")
.data(
[first, rest.join("")].map(line =>
computeNewLabel(line, newWidth / (fontSize / 1.5))
)
)
.enter()
.append("tspan")
.attr("x", fontSize)
.attr("dy", (d, i) => -textPadding + i * (2 * textPadding + fontSize))
.text(identity);
}

// adjust styles
node
.append("text")
.selectAll("text")
.attr("x", truncate(newWidth / 2))
.attr("y", truncate(height / 2))
.style("font-size", `${fontSize}px`)
.style("fill", "black")
.style("font-family", "Fira Mono")
.style("font-weight", "500")
.style("alignment-baseline", "middle")
.style("pointer-events", "none")
.style("text-anchor", "middle")
.style("font-size", "14px")
.text(nameToDisplay);

/*
const bulletOffsetComputer = tagBulletVerticalOffset({ height });
forEach(tags, (name, i) => {
node
.append("rect")
.attr("width", 10)
.attr("height", 10)
.attr("rx", 3)
.attr("ry", 3)
.style("fill", allTags[name] || "#AAA")
.attr("x", 6)
.attr("y", bulletOffsetComputer(i));
});*/
.append("title")
.text(name);

node.attr(
"transform",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ object HelloWorld {
// The `exec` interpolation is provided by the local platform.
// It allows us to declare a sh script to execute.
// More details are in [[exec]] doc.
val hello2 = Job("hello2", hourly(start), "Hello 2") { implicit e =>
val hello2 = Job("hello2", hourly(start), "Dependency for cuttle_example.world_stats") { implicit e =>
exec"""sh -c '
| echo Looping for 20 seconds...
| for i in `seq 1 20`
Expand All @@ -71,7 +71,7 @@ object HelloWorld {
// Here is our third job. Look how we can also define some metadata such as a human friendly
// name and a set of tags. This information is used in the UI to help retrieving your jobs.
val hello3 =
Job("hello3", hourly(start), "Hello 3", tags = Set(Tag("hello"), Tag("unsafe"))) { implicit e =>
Job("hello3", hourly(start), "prepare-export-job.cuttle_example.hello3_stats_daily", tags = Set(Tag("hello"), Tag("unsafe"))) { implicit e =>
// Here we mix a Scala code execution and a sh script execution.
e.streams.info("Hello 3 from an unsafe job")
val completed = exec"sleep 3" ()
Expand Down Expand Up @@ -99,7 +99,7 @@ object HelloWorld {
// Our last job is a daily job. For the daily job we still need to annouce a start date, plus
// we need to define the time zone for which _days_ must be considered. The partitions for
// daily jobs will usually be 24 hours, unless you are choosing a time zone with light saving.
val world = Job("world", daily(UTC, start), "World", tags = Set(Tag("world"))) { implicit e =>
val world = Job("world", daily(UTC, start), "export-job.cuttle.world_stats", tags = Set(Tag("world"))) { implicit e =>
e.streams.info("World!")
// Here we compose our executions in a for-comprehension.
for {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"cytoscape-dagre": "^1.3.0",
"d3": "^4.7.4",
"d3-interpolate-path": "^1.1.1",
"d3plus-text": "^0.9.25",
"fuse.js": "^3.0.0",
"history": "^4.6.1",
"lodash": "^4.17.4",
Expand Down
Loading

0 comments on commit 0eb6359

Please sign in to comment.