@@ -3,21 +3,25 @@ import type { CollectionEntry } from "astro:content";
33import Head from " @components/Head/index.astro" ;
44import BaseLayout from " ./BaseLayout.astro" ;
55import GridItemLibrary from " @components/GridItem/Library.astro" ;
6+ import LibraryListing from " @components/LibraryListing/index.astro" ;
67import { setJumpToState , type JumpToLink } from " ../globals/state" ;
78import { getCurrentLocale , getUiTranslator } from " ../i18n/utils" ;
89import { categories } from " ../content/libraries/config" ;
10+ import Button from " @components/Button/index.astro" ;
11+ import _ from " lodash" ;
912
1013interface Props {
1114 entries: CollectionEntry <" libraries" >[];
1215 page: CollectionEntry <" pages" >;
1316 title: string ;
17+ full? : boolean ;
1418}
1519type LibraryEntry = CollectionEntry <" libraries" >;
1620
1721const currentLocale = getCurrentLocale (Astro .url .pathname );
1822const t = await getUiTranslator (currentLocale );
1923
20- const { entries, page } = Astro .props ;
24+ const { entries, page, full } = Astro .props ;
2125const { Content } = await page .render ();
2226
2327function strCompare(a : string , b : string ) {
@@ -30,19 +34,56 @@ function strCompare(a: string, b: string) {
3034 return 0 ;
3135}
3236
33- const sections = categories
37+ let sections = categories
3438 .map ((slug ) => {
3539 const name = t (" libraryCategories" , slug );
36- const sectionEntries = entries
40+ let sectionEntries = entries
3741 .filter ((e : LibraryEntry ) => e .data .category === slug )
3842 .sort ((a : LibraryEntry , b : LibraryEntry ) =>
3943 strCompare (a .data .name .toLowerCase (), b .data .name .toLowerCase ())
4044 );
4145
46+
4247 return { slug , name , sectionEntries };
4348 })
4449 .filter ((section ) => section .sectionEntries .length > 0 );
4550
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+
4687const pageJumpToLinks = categories .map ((category ) => ({
4788 url: ` /libraries#${category } ` ,
4889 label: t (" libraryCategories" , category ),
@@ -66,17 +107,33 @@ setJumpToState({
66107 <Content />
67108 </div >
68109
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+
69115 {
70- sections .map (({ slug , name , sectionEntries }) => (
116+ sections .map (({ slug , name , sectionEntries , allEntries }) => (
71117 <section >
72118 <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+ )}
80137 </section >
81138 ))
82139 }
0 commit comments