function GridView(data, label, {
width = window.innerWidth - 20,
height = 0,
cols = 0,
selectableText = false,
fill = "#DDDDDD",
strokeColor = "#000000",
strokeWidth = 2,
selectedFill = "#FFFFFF",
selectedStroke = "#00FF00",
fontSize = 14,
fontColor = "black",
startSelectIdx = -1,
imgLabel = null,
imgShadeAlpha = 0,
cellW = 100,
cellH = 50,
onClick = () =>{}
} = {})
{
var halfStroke = strokeWidth / 2.0;
if (width == 0) {
width = cellW * cols + strokeWidth;
} else {
if (cols == 0) {
cols = Math.floor((width - strokeWidth) / cellW)
} else {
cellW = Math.floor(width / cols - (strokeWidth));
}
}
var rows = Math.ceil(data.length / cols);
var selectedIdx = -1;
if (height == 0) {
height = rows * cellH + strokeWidth;
} else {
cellH = Math.floor(height / rows - (strokeWidth));
}
let svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
function getX(i) { return i < 0 ? 0 : (i % cols) * (cellW) + halfStroke}
function getY(i) { return i < 0 ? 0 : (Math.floor(i / cols)) * (cellH) + halfStroke}
function setSelectedIdx(idx) {
selectedIdx = idx == selectedIdx ? -1 : idx;
var d = (selectedIdx < 0 || selectedIdx >= data.length) ? null : data[idx];
console.log("d is " + d)
onClick(d, selectedIdx);
svg.property("value", d).dispatch("input");
refresh();
}
const index = d3.local();
let refresh = () => {
if (imgLabel != null) {
svg.selectAll(".myimg")
.data(data)
.attr("x", (d,i) => {
return getX(i);
})
.enter()
.append("svg:image")
.attr("class","myimg")
.attr("xlink:href", (d) => d[imgLabel])
.style("background-color", "#ff00ff")
.attr("width", (d,i) => cellW)
.attr("height", (d,i) => cellH)
.attr("x", (d,i) => getX(i))
.attr("y", (d,i) => getY(i))
.exit()
.remove()
}
svg.selectAll(".mycell")
.data(data)
.attr("x", (d,i) => {
return getX(i);
})
.attr("fill", (d, i) => selectedIdx == i ? selectedFill : fill)
.style("stroke", strokeColor)
.enter()
.append("rect")
.attr("class","mycell")
.attr("fill", (d, i) => selectedIdx == i ? selectedFill : fill)
.attr("fill-opacity", imgLabel == null ? 1 : imgShadeAlpha)
.attr("width", (d,i) => cellW)
.attr("height", (d,i) => cellH)
.attr("x", (d,i) => getX(i))
.attr("y", (d,i) => getY(i))
.each(function(d, i) {
index.set(this, i);
})
.on("click", function (e, d) {
setSelectedIdx(index.get(this));
})
.style("stroke-width", strokeWidth)
.style("stroke", (d) => strokeColor)
.append("title").html((d)=> JSON.stringify(d).replaceAll(",","
"))
.exit()
.remove()
svg.selectAll(".mytext")
.data(data, d => d)
.enter()
.append("text")
.attr("class", "mytext")
.attr("x", (d,i) => getX(i) + (cellW / 2))
.attr("y", (d, i) => getY(i) + (cellH / 2))
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("fill",fontColor)
.attr("pointer-events", selectableText ? "default" : "none")
.attr("font-size", fontSize)
.attr("font-family", "monospace")
.text((d)=> {
var txt = d[label] ?? ""
if (txt.length > 16)
txt = txt.substring(0,12) + "…";
return txt;
})
.exit()
.remove()
svg.select(".selectbox")
.attr("x", getX(selectedIdx))
.attr("y", getY(selectedIdx))
.attr("visibility", selectedIdx >= 0 ? "visible" : "hidden")
}
refresh();
if (startSelectIdx != -1) {
setSelectedIdx(startSelectIdx);
}
var selectBox = svg.append("rect")
.attr("class", "selectbox")
.attr("x", getX(selectedIdx))
.attr("y", getY(selectedIdx))
.attr("width", cellW)
.attr("height", cellH)
.attr("visibility", selectedIdx >= 0 ? "visible" : "hidden")
.style("stroke", selectedStroke)
.style("stroke-width", strokeWidth)
.attr("fill", "none");
return Object.assign(svg.node(), {value: selectedIdx == -1 ? null : data[selectedIdx]});
}