Implemented the Phase C1C polish pass.
This commit is contained in:
parent
377d0540ca
commit
ca63a994dc
@ -1067,7 +1067,8 @@ body.timeline-inspector-resizing {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.plot-lane-svg-overlay {
|
||||
.plot-lane-svg-overlay,
|
||||
.plot-lane-warning-overlay {
|
||||
position: absolute;
|
||||
inset: 0 auto auto 0;
|
||||
z-index: 4;
|
||||
@ -1075,6 +1076,10 @@ body.timeline-inspector-resizing {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.plot-lane-warning-overlay {
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
.plot-line-svg-path {
|
||||
fill: none;
|
||||
stroke-width: 3.5;
|
||||
@ -1286,6 +1291,7 @@ body.timeline-inspector-resizing {
|
||||
.thread-marker {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
left: var(--thread-marker-offset-x, 0);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
2
PlotLine/wwwroot/css/site.min.css
vendored
2
PlotLine/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1589,14 +1589,26 @@
|
||||
if (!svg) {
|
||||
return;
|
||||
}
|
||||
let warningSvg = board.querySelector("[data-plot-line-warning-svg]");
|
||||
if (!warningSvg) {
|
||||
warningSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
warningSvg.setAttribute("class", "plot-lane-warning-overlay");
|
||||
warningSvg.setAttribute("data-plot-line-warning-svg", "true");
|
||||
warningSvg.setAttribute("aria-hidden", "true");
|
||||
board.append(warningSvg);
|
||||
}
|
||||
|
||||
svg.replaceChildren();
|
||||
warningSvg.replaceChildren();
|
||||
const boardRect = board.getBoundingClientRect();
|
||||
const width = Math.max(board.scrollWidth, boardRect.width);
|
||||
const height = Math.max(board.scrollHeight, boardRect.height);
|
||||
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
||||
svg.setAttribute("width", String(width));
|
||||
svg.setAttribute("height", String(height));
|
||||
warningSvg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
||||
warningSvg.setAttribute("width", String(width));
|
||||
warningSvg.setAttribute("height", String(height));
|
||||
|
||||
const makeSvg = (name, attributes = {}) => {
|
||||
const element = document.createElementNS("http://www.w3.org/2000/svg", name);
|
||||
@ -1609,6 +1621,60 @@
|
||||
return rowRect.top - boardRect.top + rowRect.height / 2;
|
||||
};
|
||||
|
||||
const resetMarkerOffsets = () => {
|
||||
board.querySelectorAll("[data-plot-event-node]").forEach((marker) => {
|
||||
marker.style.removeProperty("--thread-marker-offset-x");
|
||||
});
|
||||
};
|
||||
|
||||
const markerSceneId = (marker) => marker.closest("[data-plot-line-slot]")?.dataset.sceneId || "";
|
||||
const safeMarkerOffset = (marker) => {
|
||||
const slot = marker.closest("[data-plot-line-slot]");
|
||||
if (!slot) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const slotRect = slot.getBoundingClientRect();
|
||||
const markerRect = marker.getBoundingClientRect();
|
||||
return Math.max(0, Math.min(22, (slotRect.width - markerRect.width) / 2 - 6));
|
||||
};
|
||||
|
||||
const applySameSceneSplitOffsets = () => {
|
||||
resetMarkerOffsets();
|
||||
const rowsById = new Map([...board.querySelectorAll("[data-plot-line-row]")]
|
||||
.filter((row) => row.dataset.plotLineId)
|
||||
.map((row) => [row.dataset.plotLineId, row]));
|
||||
const offsets = new Map();
|
||||
const addOffset = (marker, direction) => {
|
||||
const offset = safeMarkerOffset(marker);
|
||||
if (offset > 0) {
|
||||
offsets.set(marker, direction * offset);
|
||||
}
|
||||
};
|
||||
|
||||
board.querySelectorAll("[data-plot-event-node]").forEach((marker) => {
|
||||
const type = (marker.dataset.plotEventType || "").toLowerCase();
|
||||
if (type !== "branch" && type !== "split") {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetIds = type === "branch"
|
||||
? [marker.dataset.targetPlotLineId || ""].filter(Boolean)
|
||||
: (marker.dataset.targetPlotLineIds || "").split(",").map((value) => value.trim()).filter(Boolean);
|
||||
targetIds.forEach((targetId) => {
|
||||
const firstTargetMarker = rowsById.get(targetId)?.querySelector("[data-plot-event-node]");
|
||||
if (firstTargetMarker && markerSceneId(marker) === markerSceneId(firstTargetMarker)) {
|
||||
addOffset(marker, -1);
|
||||
addOffset(firstTargetMarker, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
offsets.forEach((offset, marker) => marker.style.setProperty("--thread-marker-offset-x", `${offset}px`));
|
||||
};
|
||||
|
||||
applySameSceneSplitOffsets();
|
||||
|
||||
const pointFor = (row, marker) => {
|
||||
const slot = marker.closest("[data-plot-line-slot]");
|
||||
if (!slot) {
|
||||
@ -1734,10 +1800,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
svg.append(group);
|
||||
warningSvg.append(group);
|
||||
};
|
||||
|
||||
const drawConnector = (source, targetRow, colour, type) => {
|
||||
const drawConnector = (source, targetRow, colour, type, targetPlotLineId = "") => {
|
||||
if (!targetRow) {
|
||||
if (window.console?.warn) {
|
||||
window.console.warn(`Plot line ${type} connector skipped because target plot line data is missing.`);
|
||||
@ -1745,15 +1811,20 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const target = { x: source.x, y: rowCenterY(targetRow) };
|
||||
const flowX = Math.max(28, Math.min(46, Math.abs(target.y - source.y) * 0.28));
|
||||
const targetPoint = rowDataById.get(targetPlotLineId)?.points[0];
|
||||
const target = targetPoint || { x: source.x + 42, y: rowCenterY(targetRow), radius: 10 };
|
||||
const sourceRadius = Math.max(8, source.radius || 12);
|
||||
const startX = source.x + sourceRadius;
|
||||
const targetX = startX + flowX;
|
||||
const midX = startX + flowX / 2;
|
||||
const targetRadius = Math.max(8, target.radius || 12);
|
||||
const direction = target.x >= source.x ? 1 : -1;
|
||||
const startX = source.x + direction * sourceRadius;
|
||||
const endX = target.x - direction * targetRadius;
|
||||
const flowX = Math.max(24, Math.min(52, Math.abs(target.y - source.y) * 0.28 + Math.abs(endX - startX) * 0.2));
|
||||
const controlOffset = direction * flowX;
|
||||
const firstControlX = startX + controlOffset;
|
||||
const secondControlX = endX - controlOffset;
|
||||
svg.append(makeSvg("path", {
|
||||
class: `plot-line-svg-connector plot-line-svg-connector-${type}`,
|
||||
d: `M ${startX} ${source.y} C ${midX} ${source.y}, ${midX} ${target.y}, ${targetX} ${target.y}`,
|
||||
d: `M ${startX} ${source.y} C ${firstControlX} ${source.y}, ${secondControlX} ${target.y}, ${endX} ${target.y}`,
|
||||
stroke: colour
|
||||
}));
|
||||
};
|
||||
@ -1818,12 +1889,12 @@
|
||||
rowData.forEach(({ points, colour }) => {
|
||||
points.forEach((point) => {
|
||||
if (point.type === "branch" || point.type === "merge") {
|
||||
drawConnector(point, rowLookup.get(point.targetPlotLineId), colour, point.type);
|
||||
drawConnector(point, rowLookup.get(point.targetPlotLineId), colour, point.type, point.targetPlotLineId);
|
||||
}
|
||||
|
||||
if (point.type === "split" && point.targetPlotLineIds.length >= 2) {
|
||||
point.targetPlotLineIds.forEach((targetPlotLineId) => {
|
||||
drawConnector(point, rowLookup.get(targetPlotLineId), colour, point.type);
|
||||
drawConnector(point, rowLookup.get(targetPlotLineId), colour, point.type, targetPlotLineId);
|
||||
});
|
||||
} else if (point.type === "split" && point.targetPlotLineIds.length > 0 && window.console?.warn) {
|
||||
window.console.warn("Plot line split connector skipped because fewer than two targets are available.");
|
||||
|
||||
2
PlotLine/wwwroot/js/site.min.js
vendored
2
PlotLine/wwwroot/js/site.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user