feat(grid): stop bars at the last symbol and shade today's column
- bar line begins/ends at a glyph centre — no stub past the first/last symbol - glyph halos so the bar passes behind, not through, the symbols - today's week is a shaded column instead of a vertical rule - widen week columns so date labels stop colliding - drop "slip" wording from the legend
This commit is contained in:
@@ -78,7 +78,9 @@ function markerAt(row: FeatureRow, w: WeekId): MarkerKind | null {
|
||||
}
|
||||
|
||||
interface Cell {
|
||||
inBar: boolean
|
||||
// how far the bar line runs within this cell: none, half-from-center-right,
|
||||
// half-from-left-to-center, or full width
|
||||
line: 'none' | 'right' | 'left' | 'full'
|
||||
isStart: boolean
|
||||
glyph: string
|
||||
glyphCls: string
|
||||
@@ -90,8 +92,16 @@ const matrix = computed<Cell[][]>(() =>
|
||||
weeks.value.map((w) => {
|
||||
const m = markerAt(row, w)
|
||||
const inBar = w >= row.startWeek && w <= row.barEndWeek
|
||||
let line: Cell['line'] = 'none'
|
||||
if (inBar) {
|
||||
const isStart = w === row.startWeek
|
||||
const isEnd = w === row.barEndWeek
|
||||
// line runs center→edge so it begins and ends at a glyph centre,
|
||||
// never stubbing past the first/last symbol
|
||||
line = isStart && isEnd ? 'none' : isStart ? 'right' : isEnd ? 'left' : 'full'
|
||||
}
|
||||
return {
|
||||
inBar,
|
||||
line,
|
||||
isStart: inBar && w === row.startWeek,
|
||||
glyph: m ? GLYPH[m] : '',
|
||||
glyphCls: m ? MARKER_CLASS[m] : '',
|
||||
@@ -157,7 +167,7 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
class="cell"
|
||||
:class="colClass(ci)"
|
||||
>
|
||||
<i v-if="cell.inBar" class="bar" :class="TONE_TEXT[tone(row)]"></i>
|
||||
<i v-if="cell.line !== 'none'" class="bar" :class="[cell.line, TONE_TEXT[tone(row)]]"></i>
|
||||
<i v-if="cell.isStart" class="riser" :class="TONE_TEXT[tone(row)]"></i>
|
||||
<span v-if="cell.glyph" class="glyph" :class="cell.glyphCls">{{ cell.glyph }}</span>
|
||||
</div>
|
||||
@@ -178,18 +188,18 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
<!-- legend -->
|
||||
<div class="legend">
|
||||
<span><b class="text-base-content/50">◯</b> original estimate</span>
|
||||
<span><b class="text-warning">△</b> re-estimate (slip)</span>
|
||||
<span><b class="text-warning">△</b> re-estimate</span>
|
||||
<span><b class="text-success">◉</b> on time</span>
|
||||
<span><b class="text-error">▲</b> late</span>
|
||||
<span><b>┣━</b> feature bar</span>
|
||||
<span><b class="text-error">◆</b> milestone</span>
|
||||
<span>vertical rule = now</span>
|
||||
<span><b class="now-swatch"></b> today</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.plan-grid {
|
||||
--wk: 2.5rem;
|
||||
--wk: 3.5rem;
|
||||
display: grid;
|
||||
width: max-content;
|
||||
min-width: 100%;
|
||||
@@ -219,6 +229,7 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
line-height: 1.15;
|
||||
padding-inline: 0.15rem;
|
||||
}
|
||||
.wklabel {
|
||||
font-variant-numeric: tabular-nums;
|
||||
@@ -271,20 +282,31 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
}
|
||||
.bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
height: 2px;
|
||||
transform: translateY(-50%);
|
||||
background: currentColor;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.bar.full {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.bar.left {
|
||||
left: 0;
|
||||
right: 50%;
|
||||
} /* enters from the left, stops at the glyph */
|
||||
.bar.right {
|
||||
left: 50%;
|
||||
right: 0;
|
||||
} /* starts at the first glyph, runs right */
|
||||
.riser {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
left: 50%;
|
||||
top: 28%;
|
||||
bottom: 28%;
|
||||
width: 2px;
|
||||
transform: translateX(-50%);
|
||||
background: currentColor;
|
||||
opacity: 0.55;
|
||||
}
|
||||
@@ -293,6 +315,10 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
z-index: 1;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1;
|
||||
padding: 0 0.18rem;
|
||||
/* halo so the bar reads as passing behind, not through, the symbol */
|
||||
background: var(--color-base-100);
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
|
||||
.learncell {
|
||||
@@ -320,8 +346,12 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.col-now {
|
||||
border-left: 2px solid var(--color-primary);
|
||||
.cell.col-now {
|
||||
background: color-mix(in oklch, var(--color-primary) 9%, var(--color-base-100));
|
||||
}
|
||||
.cell.col-now .glyph {
|
||||
/* match the column tint so the halo doesn't punch a hole in it */
|
||||
background: color-mix(in oklch, var(--color-primary) 9%, var(--color-base-100));
|
||||
}
|
||||
.col-ms {
|
||||
border-left: 2px dashed color-mix(in oklch, var(--color-base-content) 35%, transparent);
|
||||
@@ -338,4 +368,13 @@ function milestoneTitle(ms: Plan['milestones']): string {
|
||||
.legend b {
|
||||
font-weight: 700;
|
||||
}
|
||||
.now-swatch {
|
||||
display: inline-block;
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
vertical-align: -0.1rem;
|
||||
border-radius: 0.15rem;
|
||||
background: color-mix(in oklch, var(--color-primary) 9%, var(--color-base-100));
|
||||
border: 1px solid color-mix(in oklch, var(--color-primary) 30%, var(--color-base-100));
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user