@@ -3,21 +3,25 @@ import type { CollectionEntry } from "astro:content";
3
3
import Head from " @components/Head/index.astro" ;
4
4
import BaseLayout from " ./BaseLayout.astro" ;
5
5
import GridItemLibrary from " @components/GridItem/Library.astro" ;
6
+ import LibraryListing from " @components/LibraryListing/index.astro" ;
6
7
import { setJumpToState , type JumpToLink } from " ../globals/state" ;
7
8
import { getCurrentLocale , getUiTranslator } from " ../i18n/utils" ;
8
9
import { categories } from " ../content/libraries/config" ;
10
+ import Button from " @components/Button/index.astro" ;
11
+ import _ from " lodash" ;
9
12
10
13
interface Props {
11
14
entries: CollectionEntry <" libraries" >[];
12
15
page: CollectionEntry <" pages" >;
13
16
title: string ;
17
+ full? : boolean ;
14
18
}
15
19
type LibraryEntry = CollectionEntry <" libraries" >;
16
20
17
21
const currentLocale = getCurrentLocale (Astro .url .pathname );
18
22
const t = await getUiTranslator (currentLocale );
19
23
20
- const { entries, page } = Astro .props ;
24
+ const { entries, page, full } = Astro .props ;
21
25
const { Content } = await page .render ();
22
26
23
27
function strCompare(a : string , b : string ) {
@@ -30,19 +34,56 @@ function strCompare(a: string, b: string) {
30
34
return 0 ;
31
35
}
32
36
33
- const sections = categories
37
+ let sections = categories
34
38
.map ((slug ) => {
35
39
const name = t (" libraryCategories" , slug );
36
- const sectionEntries = entries
40
+ let sectionEntries = entries
37
41
.filter ((e : LibraryEntry ) => e .data .category === slug )
38
42
.sort ((a : LibraryEntry , b : LibraryEntry ) =>
39
43
strCompare (a .data .name .toLowerCase (), b .data .name .toLowerCase ())
40
44
);
41
45
46
+
42
47
return { slug , name , sectionEntries };
43
48
})
44
49
.filter ((section ) => section .sectionEntries .length > 0 );
45
50
51
+ if (! full ) {
52
+ // On the featured libraries page, we want to show as close to 4 entries
53
+ // per section as possible, while also trying to give all contributors
54
+ // approximately equal footing of being featured. To do this, we don't
55
+ // let contributors show up >3x on the featured page, and we try a
56
+ // Monte Carlo approach to try to get as close to this as possible.
57
+ const targetEntriesPerSection = 4
58
+
59
+ let minScore = 1000
60
+ let bestSections = sections
61
+ for (let attempt = 0 ; attempt < 100 ; attempt ++ ) {
62
+ const entriesByAuthor = _ .groupBy (entries , (e : LibraryEntry ) => e .data .author [0 ].name )
63
+ const toRemove = new Set ()
64
+ for (const key in entriesByAuthor ) {
65
+ if (entriesByAuthor [key ].length > 3 ) {
66
+ for (const entry of _ .shuffle (entriesByAuthor [key ]).slice (3 )) {
67
+ toRemove .add (entry .id )
68
+ }
69
+ }
70
+ }
71
+ const candidateSections = sections .map ((s ) => ({
72
+ ... s ,
73
+ sectionEntries: s .sectionEntries .filter ((e ) => ! toRemove .has (e .id )).slice (0 , targetEntriesPerSection ),
74
+ allEntries: s .sectionEntries ,
75
+ }));
76
+ const score = candidateSections
77
+ .map ((s ) => Math .abs (s .sectionEntries .length - targetEntriesPerSection ))
78
+ .reduce ((acc , next ) => acc + next , 0 );
79
+ if (score < minScore ) {
80
+ minScore = score ;
81
+ bestSections = candidateSections ;
82
+ }
83
+ }
84
+ sections = bestSections ;
85
+ }
86
+
46
87
const pageJumpToLinks = categories .map ((category ) => ({
47
88
url: ` /libraries#${category } ` ,
48
89
label: t (" libraryCategories" , category ),
@@ -66,17 +107,33 @@ setJumpToState({
66
107
<Content />
67
108
</div >
68
109
110
+ <div class =" flex" >
111
+ <Button selected ={ ! full } href =" /libraries" >{ t (" LibrariesLayout" , " Featured" )} </Button >
112
+ <Button selected ={ full } href =" /libraries/directory" >{ t (" LibrariesLayout" , " Everything" )} </Button >
113
+ </div >
114
+
69
115
{
70
- sections .map (({ slug , name , sectionEntries }) => (
116
+ sections .map (({ slug , name , sectionEntries , allEntries }) => (
71
117
<section >
72
118
<h2 id = { slug } >{ name } </h2 >
73
- <ul class = " content-grid-simple" >
74
- { sectionEntries .map ((entry : LibraryEntry ) => (
75
- <li >
76
- <GridItemLibrary item = { entry } />
77
- </li >
78
- ))}
79
- </ul >
119
+ { full ? (
120
+ <>
121
+ { sectionEntries .map ((entry : LibraryEntry ) => (
122
+ <LibraryListing item = { entry } />
123
+ ))}
124
+ </>
125
+ ) : (
126
+ <>
127
+ <ul class = " content-grid-simple" >
128
+ { sectionEntries .map ((entry : LibraryEntry ) => (
129
+ <li >
130
+ <GridItemLibrary item = { entry } />
131
+ </li >
132
+ ))}
133
+ </ul >
134
+ <Button href = { ` /libraries/directory/#${slug } ` } >{ t (" LibrariesLayout" , " View All" )} ({ allEntries .length } )</Button >
135
+ </>
136
+ )}
80
137
</section >
81
138
))
82
139
}
0 commit comments