charts/shared/arrows.mjs (209 lines of code) (raw):
function addLabel(el, config, width, height, margin, clickLoggingOn = false) {
// const viewBox = `0 0 ${width} ${height}`;
// el.attr("viewBox", viewBox)
el.select(`#${config.id}`).remove()
if (config.path || config.path == "true") {
newLabel(el, config, width, height, margin)
}
// Otherwise use the legacy labeller
else {
var labelWrapper = el.append("g").attr("id", config.id).attr("class", "labelWrapper").attr("data-config", JSON.stringify(config)).style("opacity", 0)
var newStuff = generateArc(el, config, margin, width, height)
var curvePath = newStuff.curvePath
var newCoords = newStuff.newCoords
el.append("svg:defs").append("svg:marker").attr("id", "arrow").attr("refX", 6).attr("refY", 6).attr("markerWidth", 30).attr("markerHeight", 30).attr("markerUnits", "userSpaceOnUse").attr("orient", "auto").append("path").attr("d", "M 0 0 12 6 0 12 3 6").style("fill", "black")
labelWrapper.append("path").attr("class", "labelPath mobHide").attr("fill", "none").attr("stroke", "black").attr("stroke-width", 2).attr("data-id", config.id).attr("marker-end", () => {
if (config.arrow) {
if (config.arrow == "true") {
return "url(#arrow)"
} else {
return "none"
}
} else {
return "none"
}
}).attr("d", curvePath)
if (clickLoggingOn) {
var pathEl = el.select(`#${config.id} .labelPath`).node();
var midpoint = pathEl.getPointAtLength(pathEl.getTotalLength() / 2);
labelWrapper.append("circle").attr("fill", "black").attr("stroke", "black").attr("class", "clickControl").attr("r", 5).attr("data-id", config.id)
.attr("cx", midpoint.x).attr("cy", midpoint.y)
}
labelWrapper.append("text").attr("class", "labelText mobHide").attr("text-anchor", () => {
if (config.align) {
return config.align
} else {
return "start"
}
}).attr("x", newCoords.textX).attr("y", newCoords.textY).attr("dy", 15).attr("data-id", config.id).attr("dx", 0).attr("fill", "black").text(config.text)
el.selectAll(`#${config.id} text`).each(insertLinebreaks);
if (clickLoggingOn) {
clickLogging(el, width, height, margin, clickLoggingOn)
}
labelWrapper.transition().style("opacity", 1)
}
}
// Function used by new labels in YC admin
function newLabel(svg, data, containerWidth, containerHeight, margin) {
const width = containerWidth - margin.left - margin.right
const height = containerHeight - margin.top - margin.bottom
const labelWrapper = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
.attr("id", data.id)
.attr("class", "labelWrapper")
.style("opacity", 0)
labelWrapper.append('path')
.attr('class', 'labelPath mobHide')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 2)
.attr('data-id', data.id)
.attr('d', generatePath(
width * data.coords.targetX,
height * data.coords.targetY,
width * data.coords.centreX,
height * data.coords.centreY,
width * data.coords.sourceX,
height * data.coords.sourceY));
const text = labelWrapper.append('text')
.attr('class', 'labelText mobHide')
.attr('x', width * data.coords.textX)
.attr('y', height * data.coords.textY)
.attr('text-anchor', data.align) // Align text to the end
.attr('data-id', data.id)
.text(data.text)
svg.selectAll(`#${data.id} text`).each(insertLinebreaks);
labelWrapper.transition().style("opacity", 1)
}
const generatePath = (x1, y1, c1, c2, x2, y2) => {
let path = `M ${x1} ${y1} Q ${c1} ${c2} ${x2} ${y2}`;
return path
}
const insertLinebreaks = function() {
var el = d3.select(this);
var words = el.text().split('\n');
// console.log(words)
el.text('');
for (var i = 0; i < words.length; i++) {
var tspan = el.append('tspan').text(words[i]);
if (i > 0)
// tspan.attr('dy', '15');
tspan.attr('x', el.attr("x")).attr('dy', '15');
}
};
function generateArc(el, config, margin, width, height) {
// Optional arg for large arc flag, set to zero if doesn't exist
// console.log("margin", margin, "width", width, "height", height)
if (!config.largeArcFlag) {
config.largeArcFlag = 0
}
// Optional arg for sweep flag, set to zero if doesn't exist
if (!config.sweepFlag) {
config.sweepFlag = 0
}
// Optional arg that sets both x and y radius
if (!config.radius) {
config.radius = 75
}
// console.log("yeh")
var newCoords = JSON.parse(JSON.stringify(config.coords))
if (config.coordType) {
if (config.coordType === "pct" || config.coordType === "") {
var group = el.select("g")
var svgWidth = width - margin.left - margin.right
var svgHeight = height - margin.top - margin.bottom
// console.log("svgWidth", svgWidth, "svgHeight", svgHeight)
newCoords.sourceX = (config.coords.sourceX * svgWidth) + margin.left
newCoords.targetX = (config.coords.targetX * svgWidth) + margin.left
newCoords.sourceY = (config.coords.sourceY * svgHeight) + margin.top
newCoords.targetY = (config.coords.targetY * svgHeight) + margin.top
newCoords.textX = (config.coords.textX * svgWidth) + margin.left
newCoords.textY = (config.coords.textY * svgHeight) + margin.top
}
}
var curvePath = `M ${newCoords.sourceX}, ${newCoords.sourceY} A ${config.radius}, ${config.radius} 0 ${config.largeArcFlag},${config.sweepFlag} ${newCoords.targetX}, ${newCoords.targetY}`
return {
"curvePath": curvePath,
"newCoords": newCoords
}
}
function clickLogging(el, width, height, margin, clickLoggingOn) {
var clickLogging = true
if (clickLogging) {
console.log("click")
var clickNo = 0
var id = null
var clickType = "svg"
var clickResults = {
"targetX": 0,
"targetY": 0,
"sourceX": 0,
"sourceY": 0,
"textX": 0,
"textY": 0
}
var labelPaths = el.selectAll(".clickControl")
labelPaths.on("click", function(e) {
clickNo = clickNo - 1
id = d3.select(this).attr("data-id")
// console.log(id)
d3.select(`#${id} .clickControl`).transition().attr("opacity", 0.2)
d3.select(`#${id} .labelPath`).transition().attr("opacity", 0.2)
clickType = "path"
})
var labelTexts = el.selectAll(".labelText")
labelTexts.on("click", function(e) {
clickNo = clickNo - 1
id = d3.select(this).attr("data-id")
// console.log(id)
d3.select(this).transition().attr("opacity", 0.2)
clickType = "label"
})
el.on("click", function(e) {
console.log("clicktype", clickType, "clickNo", clickNo)
console.log("clickPos", d3.pointer(event)[0], d3.pointer(event)[1], "modifiedPos", d3.pointer(event)[0] - margin.left, d3.pointer(event)[1] - margin.top)
console.log("clickPos", d3.pointer(event)[0], d3.pointer(event)[1], "modifiedPos", d3.pointer(event)[0] - margin.left, d3.pointer(event)[1] - margin.top)
var clickXpct = (d3.pointer(event)[0] - margin.left) / (width - margin.left - margin.right)
var clickYpct = (d3.pointer(event)[1] - margin.top) / (height - margin.top - margin.bottom)
console.log("clickXpct", clickXpct, "clickYpct", clickYpct)
if (clickNo == -1) {
clickNo = clickNo + 1
} else if (clickNo == 0) {
clickNo = clickNo + 1
clickResults.targetX = clickXpct
clickResults.targetY = clickYpct
if (clickType === "label") {
if (id != null) {
var newConfig = JSON.parse(el.select(`#${id}`).attr("data-config"))
console.log("newConfig", newConfig)
id = null
clickResults.textX = clickXpct
clickResults.textY = clickYpct
newConfig.coords.textX = clickResults.textX
newConfig.coords.textY = clickResults.textY
addLabel(el, newConfig, width, height, margin, clickLoggingOn)
console.log(JSON.stringify(newConfig.coords))
}
}
} else if (clickNo === 1) {
clickNo = 0
if (id != null) {
var newConfig = JSON.parse(el.select(`#${id}`).attr("data-config"))
console.log("newConfig", newConfig)
id = null
if (clickType === "path") {
clickResults.sourceX = clickXpct
clickResults.sourceY = clickYpct
newConfig.coords.sourceX = clickResults.sourceX
newConfig.coords.sourceY = clickResults.sourceY
newConfig.coords.targetX = clickResults.targetX
newConfig.coords.targetY = clickResults.targetY
}
addLabel(el, newConfig, width, height, margin, clickLoggingOn)
console.log(JSON.stringify(newConfig.coords))
}
}
})
}
}
export { addLabel, clickLogging }