Stack branch labels when a row is tight; fan them out on hover or selection
This commit is contained in:
+25
-36
@@ -6,13 +6,15 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { buildRows } from "./lib/collapse";
|
||||
import { isRefVisible, lineOwners, reachable, refKind, shortName } from "./lib/filter";
|
||||
import { layout } from "./lib/layout";
|
||||
import { textWidth } from "./lib/measure";
|
||||
import { colorOf } from "./lib/palette";
|
||||
import type { FilterState, Ref, RefKind, RepoData } from "./lib/types";
|
||||
import GraphCell from "./components/GraphCell.vue";
|
||||
import RefIcon from "./components/RefIcon.vue";
|
||||
import LabelStack from "./components/LabelStack.vue";
|
||||
import Sidebar from "./components/Sidebar.vue";
|
||||
|
||||
const ROW_H = 28;
|
||||
const MESSAGE_FONT = '13px "Inter", "Noto Sans", system-ui, sans-serif';
|
||||
|
||||
const repo = shallowRef<RepoData | null>(null);
|
||||
const error = ref<string | null>(null);
|
||||
@@ -115,6 +117,10 @@ watch(rows, (now, before) => {
|
||||
const scroller = ref<HTMLElement>();
|
||||
const scrollTop = ref(0);
|
||||
const viewHeight = ref(600);
|
||||
const viewWidth = ref(1000);
|
||||
// The message column is what is left after the graph and the fixed columns:
|
||||
// author 140, date 118, hash 80, four 14px gaps and 12px right padding.
|
||||
const subjectSpace = computed(() => Math.max(700, viewWidth.value) - graphWidth.value - 338 - 56 - 12);
|
||||
const firstRow = computed(() => Math.max(0, Math.floor(scrollTop.value / ROW_H) - 10));
|
||||
const lastRow = computed(() => Math.min(rows.value.length, Math.ceil((scrollTop.value + viewHeight.value) / ROW_H) + 10));
|
||||
const pageRows = computed(() => Math.max(1, Math.floor(viewHeight.value / ROW_H) - 1));
|
||||
@@ -216,7 +222,10 @@ onMounted(() => {
|
||||
window.addEventListener("keydown", onKey);
|
||||
// Pick up new commits when coming back to the window.
|
||||
window.addEventListener("focus", load);
|
||||
resizeObserver = new ResizeObserver(() => (viewHeight.value = scroller.value?.clientHeight ?? 600));
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
viewHeight.value = scroller.value?.clientHeight ?? 600;
|
||||
viewWidth.value = scroller.value?.clientWidth ?? 1000;
|
||||
});
|
||||
if (scroller.value) resizeObserver.observe(scroller.value);
|
||||
load();
|
||||
});
|
||||
@@ -295,18 +304,14 @@ onUnmounted(() => {
|
||||
>
|
||||
··· {{ row.hiddenCount }} commits
|
||||
</button>
|
||||
<span class="labels">
|
||||
<span
|
||||
v-for="l in labels.get(row.commit.hash) ?? []"
|
||||
:key="l.name"
|
||||
class="label"
|
||||
:class="{ head: l.isHead }"
|
||||
:title="l.name"
|
||||
:style="{ '--c': colorOf(graph.rows[firstRow + i - 1].color) }"
|
||||
>
|
||||
<RefIcon :kind="l.kind" /><span class="label-text">{{ l.short }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<LabelStack
|
||||
v-if="labels.has(row.commit.hash)"
|
||||
:labels="labels.get(row.commit.hash)!"
|
||||
:color="colorOf(graph.rows[firstRow + i - 1].color)"
|
||||
:message-width="textWidth(row.commit.subject, MESSAGE_FONT)"
|
||||
:space="subjectSpace - (row.hiddenCount ? 100 : 0)"
|
||||
:selected="firstRow + i - 1 === selected"
|
||||
/>
|
||||
</div>
|
||||
<div class="author">{{ row.commit.author }}</div>
|
||||
<div class="date">{{ formatTime(row.commit.time) }}</div>
|
||||
@@ -371,12 +376,13 @@ onUnmounted(() => {
|
||||
padding-right: 12px;
|
||||
cursor: default;
|
||||
}
|
||||
.row.alt { background: var(--bg-row-alt); }
|
||||
.row:hover { background: var(--bg-hover); }
|
||||
.row.sel { background: var(--bg-select); }
|
||||
.row { --row-bg: var(--bg); background: var(--row-bg); }
|
||||
.row.alt { --row-bg: var(--bg-row-alt); }
|
||||
.row:hover { --row-bg: var(--bg-hover); }
|
||||
.row.sel { --row-bg: var(--bg-select); }
|
||||
.graph-col { flex: none; height: 100%; overflow: hidden; }
|
||||
.subject { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; }
|
||||
.text { flex: 1 1 auto; min-width: 8em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.subject { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; overflow: hidden; }
|
||||
.text { flex: 1 4 auto; min-width: 4em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.more {
|
||||
flex: none;
|
||||
border: 1px dashed currentColor;
|
||||
@@ -385,23 +391,6 @@ onUnmounted(() => {
|
||||
font-size: 11px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.labels { margin-left: auto; display: flex; gap: 5px; flex: 0 1 auto; min-width: 0; justify-content: flex-end; }
|
||||
.label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0 7px;
|
||||
border-radius: 999px;
|
||||
font-size: 11.5px;
|
||||
color: var(--c);
|
||||
border: 1px solid color-mix(in srgb, var(--c) 55%, transparent);
|
||||
background: color-mix(in srgb, var(--c) 14%, transparent);
|
||||
white-space: nowrap;
|
||||
max-width: 230px;
|
||||
flex: none;
|
||||
}
|
||||
.label-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
||||
.label.head { color: #10121c; background: var(--c); font-weight: 600; }
|
||||
.author { flex: none; width: 140px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.date { flex: none; width: 118px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
|
||||
.hash { flex: none; width: 80px; font-family: ui-monospace, monospace; color: #9aa3c7; }
|
||||
|
||||
Reference in New Issue
Block a user