11import { Layout } from '../../design/common/layout'
2- import { RepoCard } from '../../design/components/repo -card'
3- import { Select } from '../../design/components/select '
2+ import { FeaturedCard } from '../../design/components/featured -card'
3+ import { Tag } from '../../design/components/tag '
44import type { Catalog , CatalogEntry } from '../../catalog/types'
55import { url } from '../../build/config'
66import type { SiteConfig } from '../../build/config'
77
88declare module 'hono/jsx' {
99 namespace JSX {
1010 interface IntrinsicElements {
11- 'catalog-filter ' : { children ?: any }
11+ 'side-nav ' : { children ?: any }
1212 }
1313 }
1414}
1515
16+ const CATEGORY_LABEL : Record < CatalogEntry [ 'category' ] , string > = {
17+ product : 'Product' ,
18+ tool : 'Tool' ,
19+ workshop : 'Workshop' ,
20+ prototype : 'Prototype' ,
21+ fork : 'Fork' ,
22+ uncategorized : 'Uncategorized' ,
23+ }
24+
25+ type Section = { id : string ; label : string ; entries : CatalogEntry [ ] }
26+
1627export function WorkIndex ( {
1728 catalog,
1829 config,
@@ -21,72 +32,86 @@ export function WorkIndex({
2132 config : SiteConfig
2233} ) {
2334 const visible = catalog . filter ( ( e ) => ! e . hidden )
24- const sorted = [ ...visible ] . sort ( defaultSort )
35+ const featured = visible . filter ( ( e ) => e . featured )
36+ const rest = visible . filter ( ( e ) => ! e . featured )
37+
38+ const sections : Section [ ] = [
39+ { id : 'active' , label : 'Active' , entries : rest . filter ( ( e ) => e . tier === 'active' ) } ,
40+ { id : 'available' , label : 'Available' , entries : rest . filter ( ( e ) => e . tier === 'unreviewed' || e . tier === 'as-is' ) } ,
41+ { id : 'archived' , label : 'Archived' , entries : rest . filter ( ( e ) => e . tier === 'archived' ) } ,
42+ ] . filter ( ( s ) => s . entries . length > 0 )
43+
44+ // Sort entries within each section by pushedAt descending
45+ for ( const section of sections ) {
46+ section . entries . sort ( ( a , b ) => b . pushedAt . localeCompare ( a . pushedAt ) )
47+ }
48+
49+ const navItems = [
50+ ...( featured . length > 0 ? [ { href : '#featured' , label : 'Featured' } ] : [ ] ) ,
51+ ...sections . map ( ( s ) => ( { href : `#${ s . id } ` , label : `${ s . label } (${ s . entries . length } )` } ) ) ,
52+ ]
2553
2654 return (
2755 < Layout title = "Work" config = { config } >
2856 < h1 > Our work</ h1 >
2957 < p class = "work-index__intro" >
3058 Flexion's public portfolio — tools we've built, prototypes we've shipped, and
31- open-source projects we actively contribute to. Use the filters to explore by
32- tier or category. See our{ ' ' }
59+ open-source projects we actively contribute to. See our{ ' ' }
3360 < a href = { url ( '/work/health/' , config . basePath ) } > stewardship scorecard</ a > for
3461 how each repo measures up.
3562 </ p >
36- < catalog-filter >
37- < form class = "catalog-filter" >
38- < fieldset >
39- < legend > Filter</ legend >
40- < Select name = "tier" label = "Tier" >
41- < option value = "" > All</ option >
42- < option value = "active" > Active</ option >
43- < option value = "unreviewed" > Unreviewed</ option >
44- < option value = "as-is" > As-is</ option >
45- < option value = "archived" > Archived</ option >
46- </ Select >
47- < Select name = "category" label = "Category" >
48- < option value = "" > All</ option >
49- < option value = "product" > Product</ option >
50- < option value = "tool" > Tool</ option >
51- < option value = "workshop" > Workshop</ option >
52- < option value = "prototype" > Prototype</ option >
53- < option value = "fork" > Fork</ option >
54- < option value = "uncategorized" > Uncategorized</ option >
55- </ Select >
56- </ fieldset >
57- </ form >
58- < ul class = "work-index__list" >
59- { sorted . map ( ( entry , i ) => (
60- < >
61- { entry . featured && i === 0 ? (
62- < li class = "work-index__section-heading" data-featured = "true" role = "presentation" aria-hidden = "true" >
63- < h2 > Featured</ h2 >
64- </ li >
65- ) : null }
66- { ! entry . featured && ( i === 0 || sorted [ i - 1 ] . featured ) ? (
67- < li class = "work-index__section-heading" role = "presentation" aria-hidden = "true" >
68- < h2 > All projects</ h2 >
63+
64+ { featured . length > 0 ? (
65+ < section id = "featured" class = "work-index__featured" aria-labelledby = "featured-heading" >
66+ < h2 id = "featured-heading" > Featured</ h2 >
67+ < div class = "work-index__featured-grid" >
68+ { featured . map ( ( entry ) => (
69+ < FeaturedCard entry = { entry } basePath = { config . basePath } />
70+ ) ) }
71+ </ div >
72+ </ section >
73+ ) : null }
74+
75+ < div class = "l-sidebar" >
76+ < side-nav >
77+ < nav class = "side-nav" aria-label = "Work sections" >
78+ < ul class = "side-nav__list" >
79+ { navItems . map ( ( { href, label } ) => (
80+ < li class = "side-nav__item" >
81+ < a class = "side-nav__link" href = { href } > { label } </ a >
6982 </ li >
70- ) : null }
71- < li data-tier = { entry . tier } data-category = { entry . category } data-featured = { entry . featured ? 'true' : undefined } >
72- < RepoCard entry = { entry } basePath = { config . basePath } />
73- </ li >
74- </ >
83+ ) ) }
84+ </ ul >
85+ </ nav >
86+ </ side-nav >
87+
88+ < div class = "l-stack" data-space = "lg" >
89+ { sections . map ( ( section ) => (
90+ < section id = { section . id } aria-labelledby = { `${ section . id } -heading` } >
91+ < h2 id = { `${ section . id } -heading` } > { section . label } </ h2 >
92+ < ul class = "work-list" >
93+ { section . entries . map ( ( entry ) => (
94+ < li class = "work-list__item" >
95+ < div class = "work-list__header" >
96+ < a class = "work-list__name" href = { url ( `/work/${ entry . name } /` , config . basePath ) } >
97+ { entry . name }
98+ </ a >
99+ < Tag variant = { `category-${ entry . category } ` } >
100+ { CATEGORY_LABEL [ entry . category ] }
101+ </ Tag >
102+ </ div >
103+ { entry . overlay ?. summary || entry . description ? (
104+ < p class = "work-list__desc" >
105+ { entry . overlay ?. summary ?? entry . description }
106+ </ p >
107+ ) : null }
108+ </ li >
109+ ) ) }
110+ </ ul >
111+ </ section >
75112 ) ) }
76- </ ul >
77- </ catalog-filter >
113+ </ div >
114+ </ div >
78115 </ Layout >
79116 )
80117}
81-
82- function defaultSort ( a : CatalogEntry , b : CatalogEntry ) : number {
83- if ( a . featured !== b . featured ) return a . featured ? - 1 : 1
84- const tierRank : Record < CatalogEntry [ 'tier' ] , number > = {
85- active : 0 ,
86- unreviewed : 1 ,
87- 'as-is' : 2 ,
88- archived : 3 ,
89- }
90- if ( tierRank [ a . tier ] !== tierRank [ b . tier ] ) return tierRank [ a . tier ] - tierRank [ b . tier ]
91- return b . pushedAt . localeCompare ( a . pushedAt )
92- }
0 commit comments