Skip to content

Commit

Permalink
feat(modules): dynamic sorting for module installation list (#470)
Browse files Browse the repository at this point in the history
  • Loading branch information
arashsheyda authored Oct 16, 2023
1 parent 87ec2ff commit 4478015
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 13 deletions.
8 changes: 8 additions & 0 deletions packages/devtools-kit/src/_types/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface ModuleStaticInfo {
learn_more: string
category: string
type: ModuleType
stats: ModuleStats
maintainers: MaintainerInfo[]
contributors: GitHubContributor[]
compatibility: ModuleCompatibility
Expand All @@ -132,6 +133,13 @@ export interface ModuleCompatibility {
requires: { bridge?: boolean | 'optional' }
}

export interface ModuleStats {
downloads: number
stars: number
publishedAt: number
createdAt: number
}

export type CompatibilityStatus = 'working' | 'wip' | 'unknown' | 'not-working'
export type ModuleType = 'community' | 'official' | '3rd-party'

Expand Down
68 changes: 58 additions & 10 deletions packages/devtools/client/components/ModuleInstallList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@
// @ts-expect-error missing types
import { RecycleScroller } from 'vue-virtual-scroller'
import Fuse from 'fuse.js'
import type { ModuleStaticInfo } from '../../src/types'
type SortingFunction<T> = (a: T, b: T) => number
const emit = defineEmits(['close'])
const collection = useModulesList()
const sortingOptions = ['downloads', 'stars', 'updated', 'created'] as const
const ascendingOrder = ref(false)
const selectedSortingOption = ref<typeof sortingOptions[number]>(sortingOptions[0])
const sortingFactors: Record<typeof sortingOptions[number], SortingFunction<ModuleStaticInfo>> = {
downloads: (a, b) => a.stats.downloads - b.stats.downloads,
stars: (a, b) => a.stats.stars - b.stats.stars,
created: (a, b) => a.stats.createdAt - b.stats.createdAt,
updated: (a, b) => a.stats.publishedAt - b.stats.publishedAt,
}
const sortedItems = computed(() => collection.value?.slice()
.sort((a, b) => sortingFactors[selectedSortingOption.value](a, b) * (ascendingOrder.value ? 1 : -1)))
const search = ref('')
const fuse = computed(() => new Fuse(collection.value || [], {
const fuse = computed(() => new Fuse(sortedItems.value || [], {
keys: [
'name',
'description',
Expand All @@ -19,7 +36,7 @@ const fuse = computed(() => new Fuse(collection.value || [], {
const items = computed(() => {
if (!search.value)
return collection.value
return sortedItems.value
return fuse.value.search(search.value).map(r => r.item)
})
</script>
Expand All @@ -33,20 +50,51 @@ const items = computed(() => {
text="Install Module"
/>

<NTextInput
v-model="search"
:autofocus="true"
placeholder="Search..."
icon="carbon-search" n="primary"
mx6 px-5 py-2
/>
<NNavbar v-model:search="search" no-padding px-6 pb-5 pt-2>
<template #actions>
<NDropdown direction="end" n="sm primary">
<template #trigger="{ click }">
<div flex="~ items-center">
<NButton
:icon="ascendingOrder ? 'tabler:sort-ascending' : 'tabler:sort-descending'"
h-full rounded-r-none
@click="ascendingOrder = !ascendingOrder"
/>
<NButton
flex="~ justify-between"
min-w-30 border-l-0 rounded-l-none px-2 capitalize
hover="border-l-1"
@click="click()"
>
{{ selectedSortingOption }}
<NIcon icon="carbon:chevron-down" />
</NButton>
</div>
</template>
<div flex="~ col" w-30 of-auto>
<NButton
v-for="item of sortingOptions"
:key="item"
:border="false" p2
hover="n-checkbox-hover text-green"
@click="selectedSortingOption = item"
>
<span flex="~ justify-between" w-full text-xs capitalize op75>
{{ item }}
<NIcon v-if="selectedSortingOption === item" icon="carbon:checkmark" />
</span>
</NButton>
</div>
</NDropdown>
</template>
</NNavbar>

<div flex-auto of-auto flex="~ col gap-2" pl6 pr4>
<RecycleScroller
v-slot="{ item }"
class="scroller"
:items="items"
:item-size="160"
:item-size="200"
key-field="name"
>
<ModuleItemInstall :item="item" @start="emit('close')" />
Expand Down
15 changes: 15 additions & 0 deletions packages/devtools/client/components/ModuleItemBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ const openInEditor = useOpenInEditor()
</slot>

<slot name="items" />

<div v-if="data.stats" flex="~ gap-4 items-center">
<div flex="~ gap-1 items-center" op50>
<NIcon icon="carbon-star" text-lg />
<span>
{{ data.stats.stars }}
</span>
</div>
<div flex="~ gap-1 items-center" op50>
<NIcon icon="carbon-download" text-lg />
<span>
{{ data.stats.downloads }}
</span>
</div>
</div>
</div>
<div flex="~ col" items-end>
<div
Expand Down
6 changes: 3 additions & 3 deletions packages/devtools/client/composables/state-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ const ignoredModules = [

export function useModulesList() {
return useAsyncState('getModulesList', async () => {
const modules = await $fetch<ModuleStaticInfo[]>('https://cdn.jsdelivr.net/npm/@nuxt/modules@latest/modules.json')
return modules
.filter((m: ModuleStaticInfo) => !ignoredModules.includes(m.npm) && m.compatibility.nuxt.includes('^3'))
const m = await $fetch<{ modules: ModuleStaticInfo[] }>('https://api.nuxt.com/modules?version=3')
return m.modules
.filter((item: ModuleStaticInfo) => !ignoredModules.includes(item.npm) && item.compatibility.nuxt.includes('^3'))
})
}

Expand Down

0 comments on commit 4478015

Please sign in to comment.