forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2import type { ModuleReplacement } from 'module-replacements'
3
4export interface ComparisonGridColumn {
5 name: string
6 version?: string
7 /** Module replacement data for this package (if available) */
8 replacement?: ModuleReplacement | null
9}
10
11const props = defineProps<{
12 /** Column definitions for each package being compared */
13 columns: ComparisonGridColumn[]
14 /** Whether to show the "no dependency" baseline as the last column */
15 showNoDependency?: boolean
16}>()
17
18/** Total column count including the optional no-dep column */
19const totalColumns = computed(() => props.columns.length + (props.showNoDependency ? 1 : 0))
20
21/** Compute plain-text tooltip for a replacement column */
22function getReplacementTooltip(col: ComparisonGridColumn): string {
23 if (!col.replacement) return ''
24
25 return [$t('package.replacement.title'), $t('package.replacement.learn_more_above')].join(' ')
26}
27</script>
28
29<template>
30 <div class="overflow-x-auto">
31 <div
32 class="comparison-grid"
33 :class="[totalColumns === 4 ? 'min-w-[800px]' : 'min-w-[600px]', `columns-${totalColumns}`]"
34 :style="{ '--columns': totalColumns }"
35 >
36 <!-- Header row -->
37 <div class="comparison-header">
38 <div class="comparison-label" />
39
40 <!-- Package columns -->
41 <div v-for="col in columns" :key="col.name" class="comparison-cell comparison-cell-header">
42 <span class="inline-flex items-center gap-1.5 truncate">
43 <LinkBase
44 :to="packageRoute(col.name, col.version)"
45 class="text-sm truncate"
46 block
47 :title="col.version ? `${col.name}@${col.version}` : col.name"
48 >
49 {{ col.name }}<template v-if="col.version">@{{ col.version }}</template>
50 </LinkBase>
51 <TooltipApp v-if="col.replacement" :text="getReplacementTooltip(col)" position="bottom">
52 <span
53 class="i-lucide:lightbulb w-3.5 h-3.5 text-amber-500 shrink-0 cursor-help"
54 role="img"
55 :aria-label="$t('package.replacement.title')"
56 />
57 </TooltipApp>
58 </span>
59 </div>
60
61 <!-- "No dep" column (always last) -->
62 <div
63 v-if="showNoDependency"
64 class="comparison-cell comparison-cell-header comparison-cell-nodep"
65 >
66 <span
67 class="inline-flex items-center gap-1.5 text-sm font-medium text-accent italic truncate"
68 >
69 {{ $t('compare.no_dependency.label') }}
70 <TooltipApp interactive position="bottom">
71 <span
72 class="i-lucide:lightbulb w-3.5 h-3.5 text-amber-500 shrink-0 cursor-help"
73 role="img"
74 :aria-label="$t('compare.no_dependency.tooltip_title')"
75 />
76 <template #content>
77 <p class="text-sm font-medium text-fg mb-1">
78 {{ $t('compare.no_dependency.tooltip_title') }}
79 </p>
80 <p class="text-xs text-fg-muted">
81 <i18n-t keypath="compare.no_dependency.tooltip_description" tag="span">
82 <template #link>
83 <LinkBase to="https://e18e.dev/docs/replacements/">{{
84 $t('compare.no_dependency.e18e_community')
85 }}</LinkBase>
86 </template>
87 </i18n-t>
88 </p>
89 </template>
90 </TooltipApp>
91 </span>
92 </div>
93 </div>
94
95 <!-- Facet rows -->
96 <slot />
97 </div>
98 </div>
99</template>
100
101<style scoped>
102.comparison-grid {
103 display: grid;
104 gap: 0;
105}
106
107.comparison-grid.columns-2 {
108 grid-template-columns: minmax(120px, 180px) repeat(2, 1fr);
109}
110
111.comparison-grid.columns-3 {
112 grid-template-columns: minmax(120px, 160px) repeat(3, 1fr);
113}
114
115.comparison-grid.columns-4 {
116 grid-template-columns: minmax(100px, 140px) repeat(4, 1fr);
117}
118
119.comparison-header {
120 display: contents;
121}
122
123.comparison-header > .comparison-label {
124 padding: 0.75rem 1rem;
125 border-bottom: 1px solid var(--color-border);
126}
127
128.comparison-header > .comparison-cell-header {
129 padding: 0.75rem 1rem;
130 background: var(--color-bg-subtle);
131 border-bottom: 1px solid var(--color-border);
132 text-align: center;
133}
134
135/* "No dep" column styling */
136.comparison-header > .comparison-cell-header.comparison-cell-nodep {
137 background: linear-gradient(
138 135deg,
139 var(--color-bg-subtle) 0%,
140 color-mix(in srgb, var(--color-accent) 8%, var(--color-bg-subtle)) 100%
141 );
142 border-bottom-color: color-mix(in srgb, var(--color-accent) 30%, var(--color-border));
143}
144
145/* First header cell rounded top-start */
146.comparison-header > .comparison-cell-header:first-of-type {
147 border-start-start-radius: 0.5rem;
148}
149
150/* Last header cell rounded top-end */
151.comparison-header > .comparison-cell-header:last-of-type {
152 border-start-end-radius: 0.5rem;
153}
154</style>