Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ const showLogo = ref(true);

const { data: profile } = await useFetch("/api/profile");

const titles = computed(() => [
profile.value?.bio,
"Open Source Contributor",
"Web3 & Blockchain Enthusiast",
]);
const titles = computed(() => profile.value?.bio.split(","));
</script>

<template>
Expand Down
47 changes: 47 additions & 0 deletions components/AppProjects.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,54 @@ const repositoryStore = useRepositoryStore();
</Accordion>
</template>

<!-- paging / filtering info -->
<div
class="flex flex-wrap justify-center items-center opacity-50 text-sm mb-4 gap-2"
>
<!-- paging info -->
<div class="max-sm:w-full text-center">
Showing
<Countdown :value="repositoryStore.repositories.length" /> of
<Countdown :value="repositoryStore.total" />
projects
<span v-if="repositoryStore.hasFilter">with</span>
</div>

<!-- filtering info -->
<Button
v-if="languageStore.selected"
@click="languageStore.selected = null"
outline
micro
>
{{ languageStore.selected }} <Icon name="x-circle-fill" />
</Button>

<Button
v-if="topicStore.selected"
@click="topicStore.selected = null"
outline
micro
>
{{ topicStore.selected }} <Icon name="x-circle-fill" />
</Button>
</div>

<RepositoryList :repos="repositoryStore.repositories" />

<!-- load more -->
<!-- bugfix: hydration mismatch can be avoided by wrapping this in a client-only tag -->
<client-only>
<Button
v-if="repositoryStore.hasMore"
@click="repositoryStore.loadMore()"
variant="primary"
class="w-full my-4 mx-auto"
outline
>
<Icon name="caret-down" /> Load More
</Button>
</client-only>
</Drawer>
</main>
</template>
2 changes: 1 addition & 1 deletion components/project/TopicFilterNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Topic } from "server/types/repo";
const emit = defineEmits(["update:modelValue"]);

const props = defineProps<{
modelValue: string;
modelValue: string | null;
topics: Topic[];
}>();

Expand Down
2 changes: 2 additions & 0 deletions components/ui/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defineProps<{
large?: boolean;
small?: boolean;
tiny?: boolean;
micro?: boolean;
}>();
</script>

Expand All @@ -28,6 +29,7 @@ defineProps<{
'btn-lg': large,
'btn-sm': small,
'btn-xs': tiny,
'btn-xs !h-5 !min-h-min': micro,
}"
@click="$emit('click')"
>
Expand Down
2 changes: 1 addition & 1 deletion components/ui/Collapse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AccordionInjection } from "types/ui";

const props = defineProps<{
title: string;
open: boolean;
open?: boolean;
}>();

const open = ref(props.open);
Expand Down
11 changes: 11 additions & 0 deletions components/ui/Countdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
defineProps<{
value: number;
}>();
</script>

<template>
<span class="countdown font-mono">
<span :style="{ '--value': value }" />
</span>
</template>
4 changes: 2 additions & 2 deletions components/ui/Divider.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
defineProps<{
label: string;
horizontal: boolean;
label?: string;
horizontal?: boolean;
}>();
</script>

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/api/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ const collectLanguages = (repos: Repo[]): Language[] => {
// add rate to each language
return Object.values(list).map((language) => ({
...language,
rate: (language.lines / lineSum) * 100,
rate: calculateRate(language.lines, lineSum),
}));
};
2 changes: 1 addition & 1 deletion server/api/repos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const normalizeLanguages = (data: LanguageData): Language[] => {
return entries.map(([name, lines]) => ({
name,
lines,
rate: ((lines / lineSum) * 100).toFixed(1) as any,
rate: calculateRate(lines, lineSum),
color: COLORS[name] ?? null,
}));
};
2 changes: 2 additions & 0 deletions server/utils/calculateRate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const calculateRate = (value: number, total: number) =>
+((value / total) * 100).toFixed(1);
34 changes: 32 additions & 2 deletions store/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ export const useRepositoryStore = defineStore('repository', () => {
const topicStore = useTopicStore();
const languageStore = useLanguageStore();

// pagination states
const page = ref(1);
const perPage = ref(5);
const total = computed(() => filteredRepositories.value.length);
const hasMore = computed(() => total.value > page.value * perPage.value);
const hasFilter = computed(
() => !!languageStore.selected || !!topicStore.selected
);

// getters
const repositories = computed(() => {
const filteredRepositories = computed(() => {
let repos = _repositories.value;

// filter by language
Expand All @@ -21,18 +30,28 @@ export const useRepositoryStore = defineStore('repository', () => {
(lang) => lang.name === languageStore.selected
)
);

// reset page on filter
page.value = 1;
}

// filter by topics
if (topicStore.selected) {
repos = repos.filter((repo) =>
repo.topics.includes(topicStore.selected as any)
);

// reset page on filter
page.value = 1;
}

return repos;
});

const repositories = computed(() =>
filteredRepositories.value.slice(0, page.value * perPage.value)
);

// actions
async function load() {
loading.value = true;
Expand All @@ -41,9 +60,20 @@ export const useRepositoryStore = defineStore('repository', () => {
loading.value = false;
}

function loadMore() {
page.value++;
}

// init
load();

// public api
return { load, loading, repositories };
return {
loadMore,
loading,
repositories,
hasFilter,
hasMore,
total,
};
});