/* components/MatrixCanvas.jsx */
(() => {
const { AxisLine, PlotThumb } = window.Cmp;
function MatrixCanvas(props) {
const {
expanded,
theme,
exporting,
items,
xMetric,
yMetric,
xDef,
yDef,
targetCTR,
targetCVR,
hideNoCV,
toX,
toY,
sX,
sY,
getMetricValue,
canvasOuterRef,
canvasInnerRef,
onWheel,
onPointerDown,
onTouchStart,
onTouchMove,
onTouchEnd,
pan,
zoom,
zoomAt,
resetView,
exportPNG,
setExpanded,
sxKnob,
syKnob,
setSxKnob,
setSyKnob,
onItemClick,
} = props;
const isFiniteNum = (v) => v != null && Number.isFinite(Number(v));
const xLabel = xDef?.label || "X";
const yLabel = yDef?.label || "Y";
const xUnit = xDef?.isPercent ? "%" : xDef?.unit || "";
const yUnit = yDef?.isPercent ? "%" : yDef?.unit || "";
// 端固定ラベル(基準線に追従:panも加味)
const fixedLabels = (() => {
const out = [];
if (isFiniteNum(targetCTR)) {
const pos = toX(Number(targetCTR)) * sX;
out.push(
{xLabel}:{window.Num.fmt(Number(targetCTR))}
{xUnit}
);
}
if (isFiniteNum(targetCVR)) {
const pos = toY(Number(targetCVR)) * sY;
out.push(
{yLabel}:{window.Num.fmt(Number(targetCVR))}
{yUnit}
);
}
return out;
})();
const CanvasBody = (
{/* 端固定の基準線ラベル(canvas外層に固定) */}
{fixedLabels}
{/* 基準線(線自体はキャンバスに追従) */}
{isFiniteNum(targetCTR) ? (
) : null}
{isFiniteNum(targetCVR) ? (
) : null}
{items.map((it) => {
const xVal = getMetricValue(xMetric, it);
const yVal = getMetricValue(yMetric, it);
if (xVal == null || yVal == null) return null;
if (hideNoCV && (!it.cvCount || it.cvCount <= 0))
return null;
const x = toX(xVal);
const y = toY(yVal);
const inside =
Number.isFinite(x) &&
Number.isFinite(y) &&
x >= 0 &&
x <= 100 &&
y >= 0 &&
y <= 100;
if (!inside) return null;
return (
);
})}
{/* 右下:コントローラ(X/Yスケール + 全体縮尺) */}
e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
>
{/* X/Y スケール(密度) */}
{/* 全体縮尺(ズーム) */}
{Math.round(zoom * 100)}%
);
if (!expanded) return CanvasBody;
return (
全画面表示
{CanvasBody}
ヒント:ホイールで拡大縮小、ドラッグで移動。スマホは2本指でピンチ。
);
}
window.Cmp = window.Cmp || {};
window.Cmp.MatrixCanvas = MatrixCanvas;
})();