11<script setup lang="ts">
2- import { computed , ref } from " vue" ;
2+ import { computed } from " vue" ;
33import type { CellSummary } from " ../../../shared/compute" ;
4- import { TOOLCHAIN_LABELS , type CellState , type CompatibilityDocument , type ToolchainKey } from " ../../../shared/types" ;
5- import { STATE_HELP } from " ../states" ;
6- import BetaChip from " ./BetaChip.vue" ;
7- import Chip from " ./Chip.vue" ;
8- import StateIcon from " ./StateIcon.vue" ;
9-
10- const INITIAL_NODES = 5 ;
4+ import { TOOLCHAIN_LABELS , type CompatibilityDocument , type ToolchainKey } from " ../../../shared/types" ;
5+ import Timeline , { type TimelineItem } from " ./Timeline.vue" ;
116
127const props = defineProps <{ summary: CellSummary ; toolchain: ToolchainKey ; document: CompatibilityDocument }>();
138
14- const showAll = ref (false );
15-
169const label = computed (() => TOOLCHAIN_LABELS [props .toolchain ]);
1710const sourceText = computed (() =>
1811 props .summary .source === " manifest"
@@ -21,57 +14,14 @@ const sourceText = computed(() =>
2114 ? " maintainer override"
2215 : " nothing published" ,
2316);
24- const dates = computed (() =>
25- Object .fromEntries (props .document .toolchains [props .toolchain ].map ((tool ) => [tool .version , tool .date ])),
26- );
27-
28- /** Oldest first, so time runs left to right; the newest few unless expanded. */
29- const nodes = computed (() => {
30- const all = [... props .summary .breakdown ].reverse ();
31- return showAll .value ? all : all .slice (- INITIAL_NODES );
17+ const items = computed <TimelineItem []>(() => {
18+ const dates = new Map (props .document .toolchains [props .toolchain ].map ((tool ) => [tool .version , tool .date ]));
19+ return props .summary .breakdown .map ((item ) => ({ ... item , date: dates .get (item .version ) }));
3220});
33- const hiddenCount = computed (() => Math .max (0 , props .summary .breakdown .length - INITIAL_NODES ));
34-
35- /** Only nodes that need an explanation; plain declared, verified and unknown states speak for themselves. */
36- const notes = computed (() =>
37- [... nodes .value ].reverse ().filter ((item ) => {
38- switch (item .cell .state ) {
39- case " declared" :
40- return false ;
41- case " verified" :
42- return item .cell .reason !== " verified by CI" ;
43- case " unverified" :
44- return item .cell .reason !== " no requirement published" && item .cell .reason !== " no data" ;
45- default :
46- return true ;
47- }
48- }),
49- );
50-
51- const SEGMENT: Record <CellState , string > = {
52- verified: " bg-ok/55" ,
53- declared: " bg-ok/55" ,
54- unverified: " bg-unverified/40" ,
55- advisory: " bg-advisory/60" ,
56- unsupported: " bg-bad/55" ,
57- };
58- const LABEL: Record <CellState , string > = {
59- verified: " text-ok" ,
60- declared: " " ,
61- unverified: " " ,
62- advisory: " text-advisory" ,
63- unsupported: " text-bad" ,
64- };
65-
66- function shortDate(version : string ): string {
67- const date = dates .value [version ];
68- return date ? date .slice (0 , 7 ) : " " ;
69- }
7021 </script >
7122
7223<template >
73- <!-- isolate: the timeline raises its icons above the track, and that must not reach the grid's sticky headers. -->
74- <div class =" isolate" >
24+ <div >
7525 <p class =" text-sm" >
7626 <strong >{{ label }}</strong >
7727 <template v-if =" summary .rawRange " >
@@ -82,62 +32,6 @@ function shortDate(version: string): string {
8232 <span v-if =" summary.verified.length" class =" text-ok" > · verified by CI with {{ summary.verified.join(", ") }}</span >
8333 </p >
8434
85- <div class =" mt-4 flex items-stretch" role =" list" >
86- <div v-if =" hiddenCount" class =" mr-3 flex shrink-0 flex-col items-center" role =" presentation" >
87- <span class =" h-6" ></span >
88- <span class =" flex h-6 items-center" >
89- <button
90- type =" button"
91- class =" rounded-full border border-neutral-300 bg-neutral-100 px-2.5 py-0.5 text-xs font-medium whitespace-nowrap hover:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:bg-neutral-700"
92- @click =" showAll = !showAll"
93- >
94- {{ showAll ? "Hide older" : `${hiddenCount} older` }}
95- </button >
96- </span >
97- <span class =" h-5" ></span >
98- </div >
99- <div
100- v-for =" (item, index) in nodes"
101- :key =" item.version"
102- class =" flex min-w-0 flex-1 flex-col items-center text-center"
103- role =" listitem"
104- :title =" `${label} ${item.version}: ${STATE_HELP[item.cell.state]} ${item.cell.reason}`"
105- >
106- <span class =" flex h-6 items-center gap-1 text-sm leading-6 font-semibold tabular-nums whitespace-nowrap" :class =" LABEL[item.cell.state]" >
107- {{ item.version }}
108- <BetaChip v-if =" item .prerelease " />
109- <Chip v-else-if =" item .lts " :title =" ` ${label } ${item .version } is a long-term-support line ` " >LTS</Chip >
110- </span >
111- <span class =" relative flex h-6 w-full items-center justify-center" >
112- <span
113- class =" absolute top-1/2 h-1 -translate-y-1/2"
114- :class =" [SEGMENT[item.cell.state], index === 0 ? 'left-1/2 rounded-l-full' : 'left-0', index === nodes.length - 1 ? 'right-1/2 rounded-r-full' : 'right-0']"
115- ></span >
116- <StateIcon
117- :state =" item .cell .state "
118- :size =" 16 "
119- class="relative z-10 rounded-full bg-white ring-[3px] ring-white dark:bg-neutral-950 dark:ring-neutral-950 "
120- />
121- </span >
122- <span class =" h-5 text-[11px] leading-5 text-neutral-500 dark:text-neutral-400 tabular-nums" >{{ shortDate(item.version) }}</span >
123- </div >
124- </div >
125-
126- <ul v-if =" notes.length" class =" mt-4 max-w-4xl space-y-1.5 text-[13px]" >
127- <li v-for =" item in notes" :key =" item.version" class =" flex items-start gap-2" >
128- <StateIcon :state =" item .cell .state " :size =" 14 " class="mt-0.5 " />
129- <span >
130- <strong :class =" LABEL[item.cell.state]" >{{ label }} {{ item.version }}<template v-if =" item .prerelease " > beta</template ></strong >
131- <span class =" text-neutral-500 dark:text-neutral-400" > · </span >
132- <template v-if =" item .cell .advisory " >
133- {{ item.cell.advisory.message }}
134- <em v-if =" item.cell.advisory.fix" class =" ml-1" >{{ item.cell.advisory.fix }}</em >
135- <a v-if =" item.cell.advisory.url" :href =" item.cell.advisory.url" class =" ml-1 text-declared hover:underline" >details</a >
136- <code class =" ml-2 text-[11px] text-neutral-400" >{{ item.cell.advisory.id }}</code >
137- </template >
138- <template v-else >{{ item.cell.reason }}</template >
139- </span >
140- </li >
141- </ul >
35+ <Timeline class="mt-4" :items =" items " :label =" label " />
14236 </div >
14337</template >
0 commit comments