Phase 6G UI and rendering fixes
This commit is contained in:
parent
b346379de2
commit
cb67c4bcd3
@ -1644,6 +1644,7 @@ public sealed class TimelineRepository(ISqlConnectionFactory connectionFactory)
|
||||
var metricPoints = (await result.ReadAsync<TimelineMetricPoint>()).ToList();
|
||||
var plotLines = (await result.ReadAsync<PlotLineItem>()).ToList();
|
||||
var threadEvents = (await result.ReadAsync<ThreadEvent>()).ToList();
|
||||
await PopulateThreadEventTargetsAsync(connection, threadEvents);
|
||||
|
||||
return new TimelineData
|
||||
{
|
||||
@ -1658,6 +1659,36 @@ public sealed class TimelineRepository(ISqlConnectionFactory connectionFactory)
|
||||
ThreadEvents = threadEvents
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task PopulateThreadEventTargetsAsync(IDbConnection connection, IReadOnlyList<ThreadEvent> threadEvents)
|
||||
{
|
||||
var threadEventIds = threadEvents.Select(x => x.ThreadEventID).ToList();
|
||||
if (threadEventIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetRows = await connection.QueryAsync<ThreadEventTargetRow>(
|
||||
"SELECT PlotEventID, PlotLineID FROM dbo.PlotEventTargetPlotLines WHERE PlotEventID IN @ThreadEventIDs;",
|
||||
new { ThreadEventIDs = threadEventIds });
|
||||
var targetsByEvent = targetRows
|
||||
.GroupBy(x => x.PlotEventID)
|
||||
.ToDictionary(x => x.Key, x => (IReadOnlyList<int>)x.Select(row => row.PlotLineID).ToList());
|
||||
|
||||
foreach (var threadEvent in threadEvents)
|
||||
{
|
||||
if (targetsByEvent.TryGetValue(threadEvent.ThreadEventID, out var targets))
|
||||
{
|
||||
threadEvent.TargetPlotLineIDs = targets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ThreadEventTargetRow
|
||||
{
|
||||
public int PlotEventID { get; set; }
|
||||
public int PlotLineID { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TimelineSettingsRepository(ISqlConnectionFactory connectionFactory) : ITimelineSettingsRepository
|
||||
|
||||
@ -734,6 +734,7 @@
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<div class="form-text">Branch starts another plot line while this one continues. Split ends this plot line and starts two or more others. Merge ends this plot line by feeding into another.</div>
|
||||
</div>
|
||||
<div class="col-12" data-plot-event-target="branch">
|
||||
<label class="form-label" for="TargetPlotLineID-@threadEvent.ThreadEventID">Target plot line</label>
|
||||
@ -849,11 +850,11 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="col-8" data-thread-event-new-thread-field>
|
||||
<label class="form-label" for="NewThreadTitle">New thread title</label>
|
||||
<input class="form-control form-control-sm" id="NewThreadTitle" name="NewThreadTitle" placeholder="Optional" />
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="col-4" data-thread-event-new-thread-field>
|
||||
<label class="form-label" for="NewThreadTypeID">Thread type</label>
|
||||
<select class="form-select form-select-sm" id="NewThreadTypeID" name="NewThreadTypeID" asp-items="Model.ThreadTypeOptions">
|
||||
<option value="">Default</option>
|
||||
@ -875,6 +876,7 @@
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<div class="form-text">Branch starts another plot line while this one continues. Split ends this plot line and starts two or more others. Merge ends this plot line by feeding into another.</div>
|
||||
</div>
|
||||
<div class="col-12" data-plot-event-target="branch">
|
||||
<label class="form-label" for="TargetPlotLineID">Target plot line</label>
|
||||
@ -931,6 +933,7 @@
|
||||
const branchSection = form.querySelector("[data-plot-event-target='branch']");
|
||||
const mergeSection = form.querySelector("[data-plot-event-target='merge']");
|
||||
const splitSection = form.querySelector("[data-plot-event-target='split']");
|
||||
const newThreadFields = form.querySelectorAll("[data-thread-event-new-thread-field]");
|
||||
const branchTarget = form.querySelector("[data-thread-event-target-single]");
|
||||
const mergeTarget = form.querySelector("[data-thread-event-target-merge]");
|
||||
const splitTargets = form.querySelector("[data-thread-event-target-split]");
|
||||
@ -950,17 +953,36 @@
|
||||
|
||||
const sync = () => {
|
||||
const type = eventTypeName();
|
||||
const isStructural = type === "branch" || type === "merge" || type === "split";
|
||||
branchSection.hidden = type !== "branch";
|
||||
mergeSection.hidden = type !== "merge";
|
||||
splitSection.hidden = type !== "split";
|
||||
branchTarget.disabled = type !== "branch";
|
||||
mergeTarget.disabled = type !== "merge";
|
||||
splitTargets.disabled = type !== "split";
|
||||
branchTarget.name = type === "branch" ? "TargetPlotLineID" : "";
|
||||
mergeTarget.name = type === "merge" ? "TargetPlotLineID" : "";
|
||||
newThreadFields.forEach(field => {
|
||||
field.hidden = isStructural;
|
||||
field.querySelectorAll("input, select, textarea").forEach(input => input.disabled = isStructural);
|
||||
});
|
||||
if (branchTarget) branchTarget.disabled = type !== "branch";
|
||||
if (mergeTarget) mergeTarget.disabled = type !== "merge";
|
||||
if (splitTargets) splitTargets.disabled = type !== "split";
|
||||
if (branchTarget) branchTarget.name = type === "branch" ? "TargetPlotLineID" : "";
|
||||
if (mergeTarget) mergeTarget.name = type === "merge" ? "TargetPlotLineID" : "";
|
||||
syncTargetOptions();
|
||||
};
|
||||
|
||||
form.addEventListener("submit", () => {
|
||||
form.querySelectorAll("[data-split-target-mirror]").forEach(input => input.remove());
|
||||
if (eventTypeName() !== "split" || !splitTargets) return;
|
||||
[...splitTargets.selectedOptions].forEach(option => {
|
||||
if (!option.value || option.disabled) return;
|
||||
const mirror = document.createElement("input");
|
||||
mirror.type = "hidden";
|
||||
mirror.name = "SelectedTargetPlotLineIDs";
|
||||
mirror.value = option.value;
|
||||
mirror.dataset.splitTargetMirror = "true";
|
||||
form.append(mirror);
|
||||
});
|
||||
});
|
||||
|
||||
plotEventType?.addEventListener("change", sync);
|
||||
sourcePlotLine?.addEventListener("change", sync);
|
||||
sourceThread?.addEventListener("change", sync);
|
||||
|
||||
@ -882,10 +882,13 @@ body.timeline-inspector-resizing {
|
||||
}
|
||||
|
||||
.plot-event-svg-node.plotline-state-abandoned circle,
|
||||
.plot-event-svg-node.plotline-state-abandoned path,
|
||||
.plot-event-svg-node.plotline-state-abandoned line {
|
||||
.plot-event-svg-node.plotline-state-abandoned path {
|
||||
fill: #9b2f36;
|
||||
stroke: #9b2f36;
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
.plot-event-svg-node.plotline-state-abandoned line {
|
||||
stroke: #ffffff;
|
||||
}
|
||||
|
||||
.plotline-state-questionable-marker path {
|
||||
|
||||
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
@ -1453,18 +1453,29 @@
|
||||
|
||||
const drawNode = (point, colour, state) => {
|
||||
const group = makeSvg("g", { class: `plot-event-svg-node plot-event-svg-node-${point.type} plotline-state-${state}` });
|
||||
const isStructural = point.type === "branch" || point.type === "split" || point.type === "merge";
|
||||
if (point.type === "twist") {
|
||||
group.append(makeSvg("path", {
|
||||
d: `M ${point.x} ${point.y - 7} L ${point.x + 7} ${point.y} L ${point.x} ${point.y + 7} L ${point.x - 7} ${point.y} Z`,
|
||||
d: `M ${point.x} ${point.y - 9} L ${point.x + 9} ${point.y} L ${point.x} ${point.y + 9} L ${point.x - 9} ${point.y} Z`,
|
||||
fill: colour
|
||||
}));
|
||||
} else if (point.type === "abandon") {
|
||||
group.append(makeSvg("line", { x1: point.x - 6, y1: point.y - 6, x2: point.x + 6, y2: point.y + 6 }));
|
||||
group.append(makeSvg("line", { x1: point.x + 6, y1: point.y - 6, x2: point.x - 6, y2: point.y + 6 }));
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: 9, fill: "#fff5f5" }));
|
||||
group.append(makeSvg("line", { x1: point.x - 7, y1: point.y - 7, x2: point.x + 7, y2: point.y + 7 }));
|
||||
group.append(makeSvg("line", { x1: point.x + 7, y1: point.y - 7, x2: point.x - 7, y2: point.y + 7 }));
|
||||
} else if (point.type === "reveal") {
|
||||
group.append(makeSvg("path", {
|
||||
d: `M ${point.x} ${point.y - 10} L ${point.x + 3} ${point.y - 3} L ${point.x + 10} ${point.y - 3} L ${point.x + 4} ${point.y + 2} L ${point.x + 6} ${point.y + 10} L ${point.x} ${point.y + 5} L ${point.x - 6} ${point.y + 10} L ${point.x - 4} ${point.y + 2} L ${point.x - 10} ${point.y - 3} L ${point.x - 3} ${point.y - 3} Z`,
|
||||
fill: colour
|
||||
}));
|
||||
} else {
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: 6, fill: colour }));
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: isStructural ? 9 : 7.5, fill: colour }));
|
||||
if (point.type === "resolve") {
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: 10, fill: "none", stroke: colour }));
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: 12, fill: "none", stroke: colour }));
|
||||
}
|
||||
|
||||
if (isStructural) {
|
||||
group.append(makeSvg("circle", { cx: point.x, cy: point.y, r: 13, fill: "none", stroke: colour }));
|
||||
}
|
||||
}
|
||||
svg.append(group);
|
||||
@ -1492,11 +1503,12 @@
|
||||
}
|
||||
|
||||
const target = { x: source.x, y: rowCenterY(targetRow) };
|
||||
const curveWidth = Math.max(34, Math.min(78, Math.abs(target.y - source.y) * 0.42));
|
||||
const controlX = source.x + curveWidth;
|
||||
const flowX = Math.max(28, Math.min(46, Math.abs(target.y - source.y) * 0.28));
|
||||
const targetX = source.x + flowX;
|
||||
const midX = source.x + flowX / 2;
|
||||
svg.append(makeSvg("path", {
|
||||
class: `plot-line-svg-connector plot-line-svg-connector-${type}`,
|
||||
d: `M ${source.x} ${source.y} C ${controlX} ${source.y}, ${controlX} ${target.y}, ${target.x} ${target.y}`,
|
||||
d: `M ${source.x} ${source.y} C ${midX} ${source.y}, ${midX} ${target.y}, ${targetX} ${target.y}`,
|
||||
stroke: colour
|
||||
}));
|
||||
};
|
||||
|
||||
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