11<template >
22 <h2 class =" section_title" >Projects</h2 >
3- <p class =" section_description" >Built in public. Shared for everyone. Explore the repositories powering my experiments and tools.</p >
4- <br />
3+ <p class =" section_description" >Built in public. Shared for everyone. Explore the repositories powering my experiments
4+ and tools.</p >
5+ <br />
56 <div >
67 <div v-if =" loading" class =" loading" >Loading projects...</div >
78 <div v-else-if =" error" class =" error" >Error: {{ error }}</div >
8- <div v-else class =" projects-grid" >
9- <a
10- v-for =" project in projects"
11- :key =" project.name"
12- class =" vp-home-feature"
13- :href =" project.homepage"
14- target =" _blank"
15- rel =" noopener noreferrer" >
9+ <div v-else :style =" projectsGridStyles" class =" projects-grid" >
10+ <a v-for =" project in projects" :key =" project.name" class =" vp-home-feature" :href =" project.homepage"
11+ target =" _blank" rel =" noopener noreferrer" >
1612 <article class =" box" >
1713 <h2 class =" title" v-html =" project.name" />
1814 <p class =" details" v-html =" project.description" />
1915 <div class =" tags" >
20- <span v-for =" tag in project.topics" :key =" tag" class =" tag" >{{ tag }}</span >
16+ <span v-for =" tag in project.topics" :key =" tag" class =" tag" >{{
17+ tag
18+ }}</span >
2119 </div >
2220 <div class =" link-text" >
2321 <p class =" link-text-value" >
3129</template >
3230
3331<script setup lang="ts">
34- import { computed , onMounted , ref } from ' vue'
32+ import { computed , onMounted , ref } from " vue" ;
3533
3634interface Repo {
37- name: string
38- homepage: string
39- description: string
40- topics: string []
35+ name: string ;
36+ homepage: string ;
37+ description: string ;
38+ topics: string [];
4139}
4240
4341const props = defineProps <{
44- org: string
45- }>()
42+ org: string ;
43+ }>();
4644
47- const projects = ref <Repo []>([])
48- const loading = ref (true )
49- const error = ref (' ' )
45+ const projects = ref <Repo []>([]);
46+ const loading = ref (true );
47+ const error = ref (" " );
5048
51- const mainRepoName = computed (() => ` ${props .org .toLowerCase ()}.github.io ` )
49+ const mainRepoName = computed (() => ` ${props .org .toLowerCase ()}.github.io ` );
5250
5351async function fetchProjects() {
5452 try {
55- const response = await fetch (` https://api.github.com/orgs/${props .org }/repos ` , {
56- headers: {
57- Accept: ' application/vnd.github+json' ,
58- ' X-GitHub-Api-Version' : ' 2022-11-28' ,
53+ const response = await fetch (
54+ ` https://api.github.com/orgs/${props .org }/repos ` ,
55+ {
56+ headers: {
57+ Accept: " application/vnd.github+json" ,
58+ " X-GitHub-Api-Version" : " 2022-11-28" ,
59+ },
5960 },
60- })
61+ );
6162
6263 if (! response .ok ) {
63- throw new Error (' Failed to fetch projects' )
64+ throw new Error (" Failed to fetch projects" );
6465 }
6566
66- const repos = await response .json ()
67+ const repos = await response .json ();
6768
6869 projects .value = repos
6970 .filter ((repo : any ) => repo .homepage && repo .name .toLowerCase () !== mainRepoName .value )
@@ -72,22 +73,62 @@ async function fetchProjects() {
7273 homepage: repo .homepage ,
7374 description: repo .description ,
7475 topics: repo .topics || [],
75- }))
76+ }));
7677 } catch (err : any ) {
77- error .value = err .message || ' Unknown error'
78+ error .value = err .message || " Unknown error" ;
7879 } finally {
79- loading .value = false
80+ loading .value = false ;
8081 }
8182}
8283
83- onMounted (fetchProjects )
84+ onMounted (fetchProjects );
85+
86+ // --- New Logic for Grid Layout ---
87+ const projectsGridStyles = computed (() => {
88+ const projectCount = projects .value .length ;
89+ let numColumns;
90+
91+ if (projectCount === 0 ) {
92+ numColumns = 1 ; // No projects, perhaps just a single column for emptiness
93+ } else if (projectCount === 1 ) {
94+ numColumns = 1 ;
95+ } else if (projectCount === 2 ) {
96+ numColumns = 2 ;
97+ } else if (projectCount === 3 ) {
98+ numColumns = 3 ;
99+ } else if (projectCount === 4 ) {
100+ numColumns = 2 ; // Specific for 2x2
101+ } else {
102+ // For projectCount > 4
103+ if (projectCount % 4 === 0 || projectCount === 7 ) {
104+ numColumns = 4 ;
105+ } else if (projectCount % 3 === 0 ) {
106+ numColumns = 3 ;
107+ } else {
108+ // Fallback for cases like 5, 10, 11 where 4 columns is desired
109+ numColumns = 4 ;
110+ }
111+ }
112+
113+ return {
114+ display: " grid" ,
115+ gridTemplateColumns: ` repeat(${numColumns }, 1fr) ` ,
116+ gap: " 20px" ,
117+ };
118+ });
84119 </script >
85120
86121<style scoped>
87122.projects-grid {
88- display : grid ;
89- grid-template-columns : repeat (auto-fit , minmax (280px , 1fr ));
90- gap : 20px ;
123+ /*
124+ The grid properties (display, grid-template-columns, gap)
125+ are now dynamically applied via v-bind:style.
126+ You can remove the `grid-template-columns` and `gap` from here
127+ if you want them fully controlled by the JS logic.
128+ Keeping `display: grid;` here as a base is fine,
129+ but since it's in the dynamic style, it's redundant.
130+ Let's remove the redundant parts from here for clarity.
131+ */
91132}
92133
93134.loading ,
@@ -178,20 +219,20 @@ onMounted(fetchProjects)
178219}
179220
180221.section_title {
181- font-size : 28px ;
182- font-weight : 900 ;
183- margin-bottom : 20px ;
184- text-align : center ;
185- transition : color var (--vp-t-color );
186- color : var (--vp-c-text-1 );
222+ font-size : 28px ;
223+ font-weight : 900 ;
224+ margin-bottom : 20px ;
225+ text-align : center ;
226+ transition : color var (--vp-t-color );
227+ color : var (--vp-c-text-1 );
187228}
188229
189230.section_description {
190- font-size : 18px ;
191- font-weight : 400 ;
192- margin-bottom : 20px ;
193- text-align : center ;
194- transition : color var (--vp-t-color );
195- color : var (--vp-c-text-1 );
196- }
197- </style >
231+ font-size : 18px ;
232+ font-weight : 400 ;
233+ margin-bottom : 20px ;
234+ text-align : center ;
235+ transition : color var (--vp-t-color );
236+ color : var (--vp-c-text-1 );
237+ }
238+ </style >
0 commit comments