Skip to content

Commit 8b6a708

Browse files
authored
feat: wire Collection Contributors/Popularity/Development tabs to real widgets IN-1191 (#2013)
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
1 parent f3ff95a commit 8b6a708

138 files changed

Lines changed: 1528 additions & 650 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/app/components/modules/collection/components/details/collection-menu.vue

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,98 @@ Copyright (c) 2025 The Linux Foundation and each contributor.
33
SPDX-License-Identifier: MIT
44
-->
55
<template>
6-
<div class="bg-white">
6+
<div class="bg-white border-b border-neutral-200">
77
<div class="container">
8-
<div class="flex items-center gap-3 overflow-x-auto py-5">
9-
<lfx-menu-button
10-
v-for="link of lfCollectionAggregateLinks"
11-
:key="link.key"
12-
:to="{ name: link.routeName, params: { slug: props.slug } }"
13-
:exact="true"
14-
>
15-
<template #default="{ isActive }">
16-
<lfx-icon
17-
:name="link.icon"
18-
:type="isActive ? 'solid' : 'light'"
19-
/>
20-
{{ link.label }}
21-
</template>
22-
</lfx-menu-button>
8+
<div class="flex items-center justify-between gap-3 py-5">
9+
<!-- Desktop: full tab row. Matches project-menu.vue's lg: breakpoint (1024px) so
10+
collection and project pages collapse to the dropdown fallback at the same width. -->
11+
<div class="lg:flex hidden items-center gap-3">
12+
<lfx-menu-button
13+
v-for="link of lfCollectionAggregateLinks"
14+
:key="link.key"
15+
:to="linkTo(link)"
16+
:exact="true"
17+
>
18+
<template #default="{ isActive }">
19+
<lfx-icon
20+
:name="link.icon"
21+
:type="isActive ? 'solid' : 'light'"
22+
/>
23+
{{ link.label }}
24+
</template>
25+
</lfx-menu-button>
26+
</div>
27+
<!-- Tablet/mobile: dropdown fallback, same pattern as project-menu.vue -->
28+
<div class="lg:hidden block min-w-0">
29+
<lfx-dropdown
30+
placement="bottom-start"
31+
width="15rem"
32+
>
33+
<template #trigger>
34+
<lfx-dropdown-selector>
35+
<lfx-icon
36+
:name="activeLink?.icon || ''"
37+
:size="16"
38+
class="text-brand-500 font-black"
39+
/>
40+
{{ activeLink?.label }}
41+
</lfx-dropdown-selector>
42+
</template>
43+
44+
<router-link
45+
v-for="link of lfCollectionAggregateLinks"
46+
:key="link.key"
47+
:to="linkTo(link)"
48+
>
49+
<lfx-dropdown-item
50+
:value="link.key"
51+
:label="link.label"
52+
/>
53+
</router-link>
54+
</lfx-dropdown>
55+
</div>
56+
<lfx-project-date-range-picker
57+
v-if="showDateRangePicker"
58+
class="shrink-0"
59+
/>
2360
</div>
2461
</div>
2562
</div>
2663
</template>
2764

2865
<script setup lang="ts">
66+
import { computed } from 'vue';
67+
import { useRoute } from 'nuxt/app';
2968
import LfxIcon from '~/components/uikit/icon/icon.vue';
3069
import LfxMenuButton from '~/components/uikit/menu-button/menu-button.vue';
70+
import LfxDropdown from '~/components/uikit/dropdown/dropdown.vue';
71+
import LfxDropdownSelector from '~/components/uikit/dropdown/dropdown-selector.vue';
72+
import LfxDropdownItem from '~/components/uikit/dropdown/dropdown-item.vue';
73+
import LfxProjectDateRangePicker from '~/components/modules/project/components/shared/header/date-range-picker.vue';
3174
import { lfCollectionAggregateLinks } from '~/components/modules/collection/config/collection-links';
3275
3376
const props = defineProps<{
3477
slug: string;
78+
showDateRangePicker?: boolean;
3579
}>();
80+
81+
const route = useRoute();
82+
const activeLink = computed(
83+
() => lfCollectionAggregateLinks.find((link) => link.routeName === route.name) || lfCollectionAggregateLinks[0],
84+
);
85+
86+
// Preserve the current query (date range: timeRange/start/end, plus onlyLFProjects) when
87+
// switching tabs, so the selected date range doesn't reset to the default on every tab change.
88+
// `widget` is dropped because it's a per-tab scroll target, not shared across tabs - mirrors
89+
// project-menu.vue's linkUrl().
90+
const linkTo = (link: (typeof lfCollectionAggregateLinks)[number]) => ({
91+
name: link.routeName,
92+
params: { slug: props.slug },
93+
query: {
94+
...route.query,
95+
widget: undefined,
96+
},
97+
});
3698
</script>
3799

38100
<script lang="ts">

frontend/app/components/modules/collection/components/details/collection-metrics-row.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ SPDX-License-Identifier: MIT
88
<template v-if="loading">
99
<lfx-skeleton
1010
v-for="i in 3"
11-
:key="i"
11+
:key="`skeleton-${i}`"
1212
height="2rem"
1313
width="9rem"
1414
class="rounded-full"
1515
/>
1616
</template>
1717
<template v-else>
1818
<lfx-chip
19+
key="projects-repositories"
1920
type="bordered"
2021
size="small"
2122
class="flex items-center gap-1"
@@ -32,6 +33,7 @@ SPDX-License-Identifier: MIT
3233
</lfx-chip>
3334

3435
<lfx-chip
36+
key="contributors"
3537
type="bordered"
3638
size="small"
3739
class="flex items-center gap-1"
@@ -48,6 +50,7 @@ SPDX-License-Identifier: MIT
4850
</lfx-chip>
4951

5052
<lfx-chip
53+
key="avg-health"
5154
type="bordered"
5255
size="small"
5356
class="flex items-center gap-1"

frontend/app/components/modules/collection/components/details/header.vue

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ Copyright (c) 2025 The Linux Foundation and each contributor.
33
SPDX-License-Identifier: MIT
44
-->
55
<template>
6-
<div :style="headerBackgroundStyle">
6+
<div
7+
:style="headerBackgroundStyle"
8+
class="z-50"
9+
>
710
<section
811
class="container pt-3 md:pt-5 pb-3"
9-
:class="scrollTop > 50 ? 'md:pb-5' : 'md:pb-12'"
12+
:class="scrollTop > 250 ? 'md:pb-5' : 'md:pb-12'"
1013
>
1114
<!-- Mobile top row: back link + action icons -->
1215
<div
1316
class="flex md:hidden items-center justify-between transition-all"
14-
:class="scrollTop > 50 ? 'mb-0' : 'mb-4'"
17+
:class="scrollTop > 250 ? 'mb-0' : 'mb-4'"
1518
>
1619
<nuxt-link
1720
:to="{ name: collectionTab?.route }"
@@ -23,7 +26,7 @@ SPDX-License-Identifier: MIT
2326
/>
2427
<span
2528
class="text-sm font-medium transition-all"
26-
:class="scrollTop > 50 ? 'hidden' : 'block'"
29+
:class="scrollTop > 250 ? 'hidden' : 'block'"
2730
>
2831
{{ collectionTab?.detailsLabel }}
2932
</span>
@@ -90,7 +93,7 @@ SPDX-License-Identifier: MIT
9093

9194
<!-- Desktop back link (expanded state: its own row above the title) -->
9295
<div
93-
v-if="scrollTop <= 50"
96+
v-if="scrollTop <= 250"
9497
class="transition-all hidden md:block mb-6"
9598
>
9699
<nuxt-link
@@ -114,12 +117,12 @@ SPDX-License-Identifier: MIT
114117
<div
115118
class="flex-grow flex w-full"
116119
:class="
117-
scrollTop > 50 ? 'flex-row gap-3 items-center' : 'flex-col md:flex-row md:items-stretch gap-3 md:gap-8'
120+
scrollTop > 250 ? 'flex-row gap-3 items-center' : 'flex-col md:flex-row md:items-stretch gap-3 md:gap-8'
118121
"
119122
>
120123
<!-- Compact-only: back arrow inline with title, vertically centered -->
121124
<nuxt-link
122-
v-if="scrollTop > 50"
125+
v-if="scrollTop > 250"
123126
:to="{ name: collectionTab?.route }"
124127
class="hidden md:flex items-center shrink-0"
125128
>
@@ -132,7 +135,7 @@ SPDX-License-Identifier: MIT
132135
<div
133136
v-if="loading || props.collection?.logoUrl"
134137
class="shrink-0 flex items-center justify-start"
135-
:class="scrollTop > 50 ? 'h-8 md:h-10' : 'h-12 md:h-30'"
138+
:class="scrollTop > 250 ? 'h-8 md:h-10' : 'h-12 md:h-30'"
136139
>
137140
<img
138141
v-if="props.collection?.logoUrl"
@@ -142,18 +145,18 @@ SPDX-License-Identifier: MIT
142145
height="120"
143146
fetchpriority="high"
144147
decoding="async"
145-
:class="scrollTop > 50 ? 'h-8 w-8 md:h-10 md:w-10' : 'h-12 md:h-30 w-auto'"
148+
:class="scrollTop > 250 ? 'h-8 w-8 md:h-10 md:w-10' : 'h-12 md:h-30 w-auto'"
146149
/>
147150
<lfx-skeleton
148151
v-else
149-
:class="scrollTop > 50 ? 'h-8 w-8 md:h-10 md:w-10' : 'h-12 w-12 md:h-30 md:w-30'"
152+
:class="scrollTop > 250 ? 'h-8 w-8 md:h-10 md:w-10' : 'h-12 w-12 md:h-30 md:w-30'"
150153
class="rounded-md"
151154
/>
152155
</div>
153156
<div class="w-full flex flex-col justify-center min-w-0">
154157
<!-- Mobile only: visibility badge above title for my-collections -->
155158
<div
156-
v-if="props.type === CollectionTypeEnum.MY_COLLECTIONS && props.collection && scrollTop <= 50"
159+
v-if="props.type === CollectionTypeEnum.MY_COLLECTIONS && props.collection && scrollTop <= 250"
157160
class="flex md:hidden items-center gap-1.5 mb-2"
158161
>
159162
<lfx-icon
@@ -172,13 +175,13 @@ SPDX-License-Identifier: MIT
172175
v-if="loading"
173176
width="80%"
174177
class="rounded-sm"
175-
:class="scrollTop > 50 ? 'h-6 md:h-9' : 'h-9 md:h-13'"
178+
:class="scrollTop > 250 ? 'h-6 md:h-9' : 'h-9 md:h-13'"
176179
/>
177180
<h1
178181
v-else-if="props.collection"
179182
class="font-secondary font-light transition-all text-neutral-900"
180183
:class="
181-
scrollTop > 50
184+
scrollTop > 250
182185
? 'text-lg md:text-2xl md:leading-9 truncate'
183186
: 'text-3xl md:text-4xl md:leading-[56px]'
184187
"
@@ -194,7 +197,7 @@ SPDX-License-Identifier: MIT
194197
class="hidden md:flex transition-all ease-linear items-center gap-4 lg:w-auto shrink-0 mt-4 lg:mt-0"
195198
>
196199
<lfx-toggle
197-
v-if="scrollTop > 50"
200+
v-if="scrollTop > 250 && isProjectsTab"
198201
v-model="isOnlyLFProjects"
199202
>
200203
Only Linux Foundation projects
@@ -227,7 +230,7 @@ SPDX-License-Identifier: MIT
227230
class="!rounded-full shadow-sm"
228231
/>
229232
<lfx-button
230-
v-if="scrollTop <= 50"
233+
v-if="scrollTop <= 250"
231234
type="outline"
232235
class="!rounded-full shadow-sm"
233236
@click="handleShare"
@@ -272,7 +275,7 @@ SPDX-License-Identifier: MIT
272275
<!-- Loading placeholder keeps meta-row height reserved so data resolution doesn't reflow the page -->
273276
<div
274277
v-if="loading"
275-
:class="scrollTop > 50 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-3 md:mt-10'"
278+
:class="scrollTop > 250 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-3 md:mt-10'"
276279
class="flex items-center gap-2 w-full transition-all ease-linear"
277280
>
278281
<lfx-skeleton
@@ -284,7 +287,7 @@ SPDX-License-Identifier: MIT
284287
<!-- Owner + project count + LF toggle (desktop only for toggle) -->
285288
<div
286289
v-if="!loading && props.collection"
287-
:class="scrollTop > 50 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-2'"
290+
:class="scrollTop > 250 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-2'"
288291
class="flex items-center gap-2 justify-between w-full flex-wrap transition-all ease-linear"
289292
>
290293
<div class="flex items-center gap-1 md:gap-2 flex-wrap">
@@ -319,7 +322,7 @@ SPDX-License-Identifier: MIT
319322

320323
<!-- Description paragraph -->
321324
<div
322-
:class="scrollTop > 50 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-1 md:mt-3'"
325+
:class="scrollTop > 250 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-1 md:mt-3'"
323326
class="w-full transition-all ease-linear"
324327
>
325328
<div
@@ -348,15 +351,15 @@ SPDX-License-Identifier: MIT
348351
<!-- Aggregate metrics row + LF toggle (desktop only for toggle) -->
349352
<div
350353
v-if="showMetricsRow"
351-
:class="scrollTop > 50 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-10'"
354+
:class="scrollTop > 250 ? 'h-0 opacity-0 invisible pt-0' : 'h-auto opacity-100 visible mt-10'"
352355
class="w-full flex items-center justify-between gap-2 flex-wrap transition-all ease-linear"
353356
>
354357
<lfx-collection-metrics-row
355358
:metrics="props.metrics"
356359
:loading="props.metricsLoading"
357360
/>
358361
<lfx-toggle
359-
v-if="scrollTop <= 50"
362+
v-if="scrollTop <= 250 && isProjectsTab"
360363
v-model="isOnlyLFProjects"
361364
class="!hidden md:!flex"
362365
>
@@ -385,7 +388,7 @@ SPDX-License-Identifier: MIT
385388
<script lang="ts" setup>
386389
import { computed, ref } from 'vue';
387390
import { storeToRefs } from 'pinia';
388-
import { useRouter } from 'nuxt/app';
391+
import { useRoute, useRouter } from 'nuxt/app';
389392
import { useQueryClient } from '@tanstack/vue-query';
390393
import { collectionTabs, headerBackground, CollectionTypeEnum } from '../../config/collection-type-config';
391394
import LfxCollectionMetricsRow from './collection-metrics-row.vue';
@@ -425,8 +428,14 @@ const authStore = useAuthStore();
425428
const { user } = storeToRefs(authStore);
426429
427430
const router = useRouter();
431+
const route = useRoute();
428432
const { openShareModal } = useShareStore();
429433
434+
// The LF-projects toggle filters the Projects tab's table - it has no effect on the
435+
// Contributors/Popularity/Development tabs, which show collection-wide aggregate widgets
436+
// instead of a filterable project list, so it's hidden there.
437+
const isProjectsTab = computed(() => route.name === LfxRoutes.COLLECTION);
438+
430439
const props = defineProps<{
431440
collection?: Collection;
432441
loading?: boolean;

frontend/app/components/modules/collection/config/collection-links.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ export interface CollectionLinkConfig {
1111

1212
export const lfCollectionAggregateLinks: CollectionLinkConfig[] = [
1313
{ key: 'projects', icon: 'layer-group', label: 'Projects', routeName: LfxRoutes.COLLECTION },
14-
// {
15-
// key: 'contributors',
16-
// icon: 'people-group',
17-
// label: 'Contributors',
18-
// routeName: LfxRoutes.COLLECTION_CONTRIBUTORS,
19-
// },
20-
// {
21-
// key: 'popularity',
22-
// icon: 'fire',
23-
// label: 'Popularity',
24-
// routeName: LfxRoutes.COLLECTION_POPULARITY,
25-
// },
26-
// {
27-
// key: 'development',
28-
// icon: 'code',
29-
// label: 'Development',
30-
// routeName: LfxRoutes.COLLECTION_DEVELOPMENT,
31-
// },
14+
{
15+
key: 'contributors',
16+
icon: 'people-group',
17+
label: 'Contributors',
18+
routeName: LfxRoutes.COLLECTION_CONTRIBUTORS,
19+
},
20+
{
21+
key: 'popularity',
22+
icon: 'fire',
23+
label: 'Popularity',
24+
routeName: LfxRoutes.COLLECTION_POPULARITY,
25+
},
26+
{
27+
key: 'development',
28+
icon: 'code',
29+
label: 'Development',
30+
routeName: LfxRoutes.COLLECTION_DEVELOPMENT,
31+
},
3232
];

0 commit comments

Comments
 (0)