@@ -3,20 +3,24 @@ 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 title: string ;
16+ full? : boolean ;
1317}
1418type LibraryEntry = CollectionEntry <" libraries" >;
1519
1620const currentLocale = getCurrentLocale (Astro .url .pathname );
1721const t = await getUiTranslator (currentLocale );
1822
19- const { entries } = Astro .props ;
23+ const { entries, full } = Astro .props ;
2024
2125function strCompare(a : string , b : string ) {
2226 if (a < b ) {
@@ -28,19 +32,56 @@ function strCompare(a: string, b: string) {
2832 return 0 ;
2933}
3034
31- const sections = categories
35+ let sections = categories
3236 .map ((slug ) => {
3337 const name = t (" libraryCategories" , slug );
34- const sectionEntries = entries
38+ let sectionEntries = entries
3539 .filter ((e : LibraryEntry ) => e .data .category === slug )
3640 .sort ((a : LibraryEntry , b : LibraryEntry ) =>
3741 strCompare (a .data .name .toLowerCase (), b .data .name .toLowerCase ())
3842 );
3943
40- return { slug , name , sectionEntries };
44+
45+ return { slug , name , sectionEntries , allEntries: sectionEntries };
4146 })
4247 .filter ((section ) => section .sectionEntries .length > 0 );
4348
49+ if (! full ) {
50+ // On the featured libraries page, we want to show as close to 4 entries
51+ // per section as possible, while also trying to give all contributors
52+ // approximately equal footing of being featured. To do this, we don't
53+ // let contributors show up >3x on the featured page, and we try a
54+ // Monte Carlo approach to try to get as close to this as possible.
55+ const targetEntriesPerSection = 4
56+
57+ let minScore = 1000
58+ let bestSections = sections
59+ for (let attempt = 0 ; attempt < 100 ; attempt ++ ) {
60+ const entriesByAuthor = _ .groupBy (entries , (e : LibraryEntry ) => e .data .author [0 ].name )
61+ const toRemove = new Set ()
62+ for (const key in entriesByAuthor ) {
63+ if (entriesByAuthor [key ].length > 3 ) {
64+ for (const entry of _ .shuffle (entriesByAuthor [key ]).slice (3 )) {
65+ toRemove .add (entry .id )
66+ }
67+ }
68+ }
69+ const candidateSections = sections .map ((s ) => ({
70+ ... s ,
71+ sectionEntries: s .sectionEntries .filter ((e ) => ! toRemove .has (e .id )).slice (0 , targetEntriesPerSection ),
72+ allEntries: s .sectionEntries ,
73+ }));
74+ const score = candidateSections
75+ .map ((s ) => Math .abs (s .sectionEntries .length - targetEntriesPerSection ))
76+ .reduce ((acc , next ) => acc + next , 0 );
77+ if (score < minScore ) {
78+ minScore = score ;
79+ bestSections = candidateSections ;
80+ }
81+ }
82+ sections = bestSections ;
83+ }
84+
4485const pageJumpToLinks = categories .map ((category ) => ({
4586 url: ` /libraries#${category } ` ,
4687 label: t (" libraryCategories" , category ),
@@ -60,17 +101,33 @@ setJumpToState({
60101 variant =" item"
61102 topic =" community"
62103>
104+
105+ <div class =" flex" >
106+ <Button selected ={ ! full } href =" /libraries/" >{ t (" LibrariesLayout" , " Featured" )} </Button >
107+ <Button selected ={ full } href =" /libraries/directory/" >{ t (" LibrariesLayout" , " Everything" )} </Button >
108+ </div >
63109 {
64- sections .map (({ slug , name , sectionEntries }) => (
110+ sections .map (({ slug , name , sectionEntries , allEntries }) => (
65111 <section >
66112 <h2 id = { slug } >{ name } </h2 >
67- <ul class = " content-grid-simple" >
68- { sectionEntries .map ((entry : LibraryEntry ) => (
69- <li >
70- <GridItemLibrary item = { entry } />
71- </li >
72- ))}
73- </ul >
113+ { full ? (
114+ <>
115+ { sectionEntries .map ((entry : LibraryEntry ) => (
116+ <LibraryListing item = { entry } />
117+ ))}
118+ </>
119+ ) : (
120+ <>
121+ <ul class = " content-grid-simple" >
122+ { sectionEntries .map ((entry : LibraryEntry ) => (
123+ <li >
124+ <GridItemLibrary item = { entry } />
125+ </li >
126+ ))}
127+ </ul >
128+ <Button href = { ` /libraries/directory/#${slug } ` } >{ t (" LibrariesLayout" , " View All" )} ({ allEntries .length } )</Button >
129+ </>
130+ )}
74131 </section >
75132 ))
76133 }
0 commit comments