Skip to content
Draft
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
33 changes: 24 additions & 9 deletions assets/components/ecosystem.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
/** biome-ignore-all lint/style/noNonNullAssertion: easy to debug misspelling */

// Ecosystem package registry: free-text search, one active category, one active tag.
// Categories and tags both come from the controlled vocabulary in the registry schema.
// Ecosystem package registry: free-text search, one active category, one active language, one active tag.
// Categories, languages and tags all come from the controlled vocabulary in the registry schema.
export default function init(root: HTMLElement) {
const input = root.querySelector<HTMLInputElement>("#eco-filter")!
const chipRow = root.querySelector<HTMLElement>("#eco-chips")!
const languageRow = root.querySelector<HTMLElement>("#eco-languages")!
const grid = root.querySelector<HTMLElement>("#eco-grid")!
const counter = root.querySelector<HTMLElement>("#eco-count")!
const empty = root.querySelector<HTMLElement>("#eco-empty")!
const clearButton = root.querySelector<HTMLElement>("#eco-clear")!
const cards = Array.from(grid.querySelectorAll<HTMLElement>(".eco-card"))
let activeCategory: string | null = null
let activeLanguage: string | null = null
let activeTag: string | null = null

const apply = () => {
Expand All @@ -20,21 +22,22 @@ export default function init(root: HTMLElement) {
const visible =
terms.every((term) => card.dataset.search!.includes(term)) &&
(!activeCategory || card.dataset.category === activeCategory) &&
(!activeLanguage || card.dataset.languages!.includes(`|${activeLanguage}|`)) &&
(!activeTag || card.dataset.tags!.includes(`|${activeTag}|`))
card.hidden = !visible
if (visible) shown += 1
}
counter.textContent = String(shown)
empty.hidden = shown !== 0
clearButton.hidden = !terms.length && !activeCategory && !activeTag
clearButton.hidden = !terms.length && !activeCategory && !activeLanguage && !activeTag
for (const tag of root.querySelectorAll<HTMLElement>(".eco-tag")) {
tag.classList.toggle("is-active", tag.dataset.tag === activeTag)
}
}

const syncChips = () => {
for (const chip of chipRow.querySelectorAll<HTMLElement>(".eco-chip")) {
const isActive = chip.dataset.category === activeCategory
const syncChips = (row: HTMLElement, key: "category" | "language", active: string | null) => {
for (const chip of row.querySelectorAll<HTMLElement>(".eco-chip")) {
const isActive = (chip.dataset[key] || null) === active
chip.classList.toggle("is-active", isActive)
chip.setAttribute("aria-pressed", String(isActive))
}
Expand All @@ -43,8 +46,10 @@ export default function init(root: HTMLElement) {
const reset = () => {
input.value = ""
activeCategory = null
activeLanguage = null
activeTag = null
syncChips()
syncChips(chipRow, "category", activeCategory)
syncChips(languageRow, "language", activeLanguage)
apply()
}

Expand All @@ -61,8 +66,18 @@ export default function init(root: HTMLElement) {
chipRow.addEventListener("click", (event) => {
const chip = (event.target as HTMLElement).closest<HTMLElement>(".eco-chip")
if (!chip) return
activeCategory = activeCategory === chip.dataset.category ? null : chip.dataset.category!
syncChips()
const category = chip.dataset.category || null
activeCategory = activeCategory === category ? null : category
syncChips(chipRow, "category", activeCategory)
apply()
})

languageRow.addEventListener("click", (event) => {
const chip = (event.target as HTMLElement).closest<HTMLElement>(".eco-chip")
if (!chip) return
const language = chip.dataset.language || null
activeLanguage = activeLanguage === language ? null : language
syncChips(languageRow, "language", activeLanguage)
apply()
})

Expand Down
4 changes: 4 additions & 0 deletions assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,10 @@ body {
flex-wrap: wrap;
gap: 0.4rem;
margin: 1.25rem 0 2rem;

&:has(+ .eco-chips) {
margin-bottom: 0.4rem;
}
}

.eco-chip {
Expand Down
55 changes: 52 additions & 3 deletions layouts/packages/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,20 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
{{ end }}
{{/* Registry descriptions are plain text with the odd bit of markdown and hard line breaks. */}}
{{ $desc := trim (.description | markdownify | plainify | htmlUnescape | replaceRE `\s+` " ") " " }}
{{ $search := lower (printf "%s %s %s %s %s" .name $desc (delimit $tags " ") $category .language) }}
{{ $langs := slice .language }}
{{ $bindingNames := slice }}
{{ range .bindings }}
{{ $langs = $langs | append .language }}
{{ $bindingNames = $bindingNames | append .name }}
{{ end }}
{{ $langs = uniq $langs }}
{{ $search := lower (printf "%s %s %s %s %s %s" .name $desc (delimit $tags " ") $category (delimit $langs " ") (delimit $bindingNames " ")) }}
{{ $items = $items | append (merge . (dict
"core" $isCore
"normtags" $tags
"category" $category
"language" .language
"languages" $langs
"desc" $desc
"search" $search
))
Expand All @@ -73,6 +81,23 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
{{ end }}
{{ $categoryCounts = sort $categoryCounts "count" "desc" }}

{{/* Language row: the package's own language plus every language it has a binding for. */}}
{{ $languages := slice }}
{{ range $items }}
{{ range .languages }}
{{ if not (in $languages .) }}
{{ $languages = $languages | append . }}
{{ end }}
{{ end }}
{{ end }}
{{ $languageCounts := slice }}
{{ range $languages }}
{{ $language := . }}
{{ $count := len (where $items "languages" "intersect" (slice $language)) }}
{{ $languageCounts = $languageCounts | append (dict "name" $language "count" $count) }}
{{ end }}
{{ $languageCounts = sort $languageCounts "count" "desc" }}


<div id="ecosystem-packages" data-total="{{ len $items }}">
<svg class="eco-sprite" aria-hidden="true" focusable="false">
Expand Down Expand Up @@ -137,7 +162,7 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
</p>
</div>

<div class="eco-chips" id="eco-chips">
<div class="eco-chips" id="eco-chips" role="group" aria-label="Filter by category">
<button type="button" class="eco-chip is-active" data-category="" aria-pressed="true">All</button>
{{ range $categoryCounts }}
<button type="button" class="eco-chip" data-category="{{ .name }}" aria-pressed="false">
Expand All @@ -147,6 +172,23 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
{{ end }}
</div>

<div
class="eco-chips eco-chips--language"
id="eco-languages"
role="group"
aria-label="Filter by language"
>
<button type="button" class="eco-chip is-active" data-language="" aria-pressed="true">
Any language
</button>
{{ range $languageCounts }}
<button type="button" class="eco-chip" data-language="{{ .name }}" aria-pressed="false">
{{ .name }}
<span class="eco-chip-count">{{ .count }}</span>
</button>
{{ end }}
</div>

<div class="eco-grid" id="eco-grid">
{{ range $i, $e := $items }}
{{ $home := $e.project_home }}
Expand Down Expand Up @@ -195,6 +237,12 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
)
}}
{{ end }}
{{ range $e.bindings }}
{{ $candidates = $candidates | append (dict
"icon" "eco-i-package" "label" .language "url" .url "title" .name
)
}}
{{ end }}
{{ with $e.publications }}
{{ $doi := index . 0 }}
{{ $url := cond (hasPrefix $doi "http") $doi (printf "https://doi.org/%s" $doi) }}
Expand All @@ -218,6 +266,7 @@ <h2 id="ecosystem">Ecosystem packages maintained by scverse community</h2>
data-search="{{ $e.search }}"
data-tags="|{{ delimit $e.normtags "|" }}|"
data-category="{{ $e.category }}"
data-languages="|{{ delimit $e.languages "|" }}|"
>
<div class="eco-card-head">
{{ with $e.logo }}
Expand Down Expand Up @@ -259,7 +308,7 @@ <h3 class="eco-card-name">
{{ with $links }}
<div class="eco-card-links">
{{ range . }}
<a href="{{ .url }}" target="_blank" rel="noopener">
<a href="{{ .url }}" target="_blank" rel="noopener" {{ with .title }}title="{{ . }}"{{ end }}>
<svg aria-hidden="true" focusable="false"><use href="#{{ .icon }}" /></svg>
{{ .label }}
</a>
Expand Down
3 changes: 3 additions & 0 deletions layouts/partials/package-section.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
<a href="{{ $url }}" target="_blank">Conda</a>
{{ end }}
{{ end }}
{{ range $p.bindings }}
<a href="{{ .url }}" target="_blank" title="{{ .name }}">{{ .language }}</a>
{{ end }}
{{/* The registry lists every paper a package has; the first is the one to cite. */}}
{{ with $p.publications }}
{{ $doi := index . 0 }}
Expand Down
Loading