site: rework guides filters and tags taxonomy

- Rework the filtering system for guides to drop the use of the
  "products", "subjects", and "levels" taxonomies in favor of "tags" and
  "languages"
- This change means that the existing taxonomy functionality integrates
  better with the guides filtering, and there are fewer parameters to
  keep in mind when adding metadata to a guide
  - Only two taxonomies instead of three
  - Only one of those taxonomies are guides-specific (languages)
  - The other taxonomy (tags) works for all content
- Updates how tags and tag pages are rendered in general

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
David Karlsson 2024-10-24 18:09:27 +02:00
Родитель 60bf7c8406
Коммит 56e58ae5bf
84 изменённых файлов: 329 добавлений и 318 удалений

Просмотреть файл

@ -1,6 +1,6 @@
---
title: Guides
keywords: Docker guides
title: Docker guides
linkTitle: Guides
description: Explore the Docker guides
params:
icon: developer_guide

Просмотреть файл

@ -2,10 +2,11 @@
title: Set up your company for success with Docker
linkTitle: Admin set up
summary: Get the most out of Docker by streamlining workflows, standardizing development environments, and ensuring smooth deployments across your company.
description: Learn how to onboard your company and take advantage of all of the Docker products and features.
levels: [intermediate]
description: Learn how to onboard your company and take advantage of all of the Docker products and features.
tags: [admin]
params:
featured: true
time: 20 minutes
image:
resource_links:
- title: Overview of Administration in Docker
@ -66,4 +67,4 @@ By configuring Docker products to suit your companys needs, you can optimize
## Tools integration
Okta, Entra ID SAML 2.0, Azure Connect (OIDC), MDM solutions like Intune
Okta, Entra ID SAML 2.0, Azure Connect (OIDC), MDM solutions like Intune

Просмотреть файл

@ -5,9 +5,8 @@ description: >
Learn how to manage simple and complex build configurations with Buildx Bake.
summary: >
Learn to automate Docker builds and testing with declarative configurations using Buildx Bake.
tags: [devops]
languages: [go]
subjects: [devops]
levels: [advanced]
params:
time: 30 minutes
featured: true

Просмотреть файл

@ -5,7 +5,6 @@ title: Bun language-specific guide
summary: |
Learn how to containerize JavaScript applications with the Bun runtime.
linkTitle: Bun
levels: [beginner]
languages: [js]
params:
time: 10 minutes

Просмотреть файл

@ -5,7 +5,7 @@ summary: |
Containers don't have to be just for your app. Learn how to run your app's dependent services and other debugging tools to enhance your development environment.
description: |
Use containers in your local development loop to develop and test faster… even if your main app isn't running in containers.
levels: [beginner]
tags: [app-dev]
params:
image: images/learning-paths/container-supported-development.png
time: 20 minutes
@ -73,4 +73,4 @@ Once you start using containers in your development environment, it becomes much
{{< youtube-embed TCZX15aKSu4 >}}
<div id="lp-survey-anchor"></div>
<div id="lp-survey-anchor"></div>

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/cpp/
- /guides/language/cpp/
languages: [cpp]
levels: [beginner]
params:
time: 10 minutes
---

Просмотреть файл

@ -4,8 +4,7 @@ keywords: database, mysql
title: Use containerized databases
summary: |
Learn how to effectively run and manage databases as containers.
levels: [beginner]
subjects: [databases]
tags: [databases]
aliases:
- /guides/use-case/databases/
params:

Просмотреть файл

@ -7,8 +7,7 @@ description: |
summary: |
Build applications up to 39x faster using cloud-based resources, shared team
cache, and native multi-architecture support.
levels: [beginner]
products: [dbc]
tags: [product-demo]
aliases:
- /learning-paths/docker-build-cloud/
params:

Просмотреть файл

@ -5,8 +5,7 @@ summary: |
Simplify the process of defining, configuring, and running multi-container
Docker applications.
description: Learn how to use Docker Compose to define and run multi-container Docker applications.
levels: [beginner]
products: [compose]
tags: [product-demo]
aliases:
- /learning-paths/docker-compose/
params:

Просмотреть файл

@ -8,8 +8,7 @@ description: |
Learn how to use Docker Scout to enhance container security by automating
vulnerability detection and remediation, ensuring compliance, and protecting
your development workflow.
levels: [Beginner]
products: [scout]
tags: [product-demo]
aliases:
- /learning-paths/docker-scout/
params:

Просмотреть файл

@ -9,7 +9,6 @@ aliases:
- /language/dotnet/
- /guides/language/dotnet/
languages: [c-sharp]
levels: [beginner]
params:
time: 20 minutes
toc_min: 1

Просмотреть файл

@ -5,8 +5,7 @@ keywords: python, generative ai, genai, llm, neo4j, ollama, langchain
summary: |
Learn how to build a PDF bot for parsing PDF documents and generating
responses using Docker and generative AI.
levels: [beginner]
subjects: [ai]
tags: [ai]
aliases:
- /guides/use-case/genai-pdf-bot/
params:

Просмотреть файл

@ -6,8 +6,7 @@ keywords: python, generative ai, genai, llm, whisper, pinecone, openai, whisper
summary: |
Learn how to build and deploy a generative AI video analysis and
transcription bot using Docker.
subjects: [ai]
levels: [beginner]
tags: [ai]
aliases:
- /guides/use-case/genai-video-bot/
params:

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/golang/
- /guides/language/golang/
languages: [go]
levels: [beginner]
params:
time: 30 minutes
---

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/java/
- /guides/language/java/
languages: [java]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -5,9 +5,8 @@ title: Data science with JupyterLab
toc_max: 2
summary: |
Use Docker to run Jupyter notebooks.
tags: [data-science]
languages: [python]
levels: [beginner]
subjects: [data-science]
aliases:
- /guides/use-case/jupyter/
params:

Просмотреть файл

@ -5,9 +5,8 @@ title: Developing event-driven applications with Kafka and Docker
linktitle: Event-driven apps with Kafka
summary: |
This guide explains how to run Apache Kafka in Docker containers.
subjects: [distributed-systems]
tags: [distributed-systems]
languages: [js]
levels: [intermediate]
aliases:
- /guides/use-case/kafka/
params:

Просмотреть файл

@ -7,8 +7,7 @@ aliases:
- /guides/deployment-orchestration/kube-deploy/
summary: |
Learn how to deploy and orchestrate Docker containers using Kubernetes.
subjects: [deploy]
levels: [beginner]
tags: [deploy]
params:
time: 10 minutes
---

Просмотреть файл

@ -6,8 +6,7 @@ description: Learn how to build and run a language translation application using
summary: |
This guide demonstrates how to use Docker to deploy language translation
models for NLP tasks.
levels: [beginner]
subjects: [ai]
tags: [ai]
languages: [python]
aliases:
- /guides/use-case/nlp/language-translation/

Просмотреть файл

@ -6,9 +6,8 @@ linktitle: AWS development with LocalStack
summary: |
This guide explains how to use Docker to run LocalStack, a local AWS cloud
stack emulator.
subjects: [cloud-services]
tags: [cloud-services]
languages: [js]
levels: [intermediate]
params:
time: 20 minutes
---

Просмотреть файл

@ -6,9 +6,8 @@ description: Learn how to build and run a named entity recognition application u
summary: |
This guide explains how to containerize named entity recognition (NER) models
using Docker.
subjects: [ai]
tags: [ai]
languages: [python]
levels: [beginner]
aliases:
- /guides/use-case/nlp/named-entity-recognition/
params:

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/nodejs/
- /guides/language/nodejs/
languages: [js]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -7,8 +7,7 @@ aliases:
- /guides/deployment-orchestration/orchestration/
summary: |
Explore the essentials of container orchestration with Docker.
subjects: [deploy]
levels: [beginner]
tags: [deploy]
params:
time: 10 minutes
---

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/php/
- /guides/language/php/
languages: [php]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -4,8 +4,7 @@ linktitle: Pre-seeding database
description: &desc Pre-seeding database with schema and data at startup for development environment
keywords: Pre-seeding, database, postgres, container-supported development
summary: *desc
levels: [intermediate]
subjects: [databases]
tags: [app-dev, databases]
params:
time: 20 minutes
---

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/python/
- /guides/language/python/
languages: [python]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /languages/r/
- /guides/languages/r/
languages: [r]
levels: [beginner]
params:
time: 10 minutes
---

Просмотреть файл

@ -6,8 +6,7 @@ linkTitle: RAG Ollama application
summary: |
This guide demonstrates how to use Docker to deploy Retrieval-Augmented
Generation (RAG) models with Ollama.
subjects: [ai]
levels: [beginner]
tags: [ai]
aliases:
- /guides/use-case/rag-ollama/
params:

Просмотреть файл

@ -12,7 +12,6 @@ aliases:
- /language/ruby/
- /guides/language/ruby/
languages: [ruby]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -11,7 +11,6 @@ aliases:
- /language/rust/
- /guides/language/rust/
languages: [rust]
levels: [beginner]
params:
time: 20 minutes
---

Просмотреть файл

@ -6,9 +6,8 @@ description: Learn how to build and run a sentiment analysis application using P
summary: |
This guide demonstrates how to containerize sentiment analysis models using
Docker.
subjects: [ai]
tags: [ai]
languages: [python]
levels: [beginner]
aliases:
- /guides/use-case/nlp/sentiment-analysis/
params:

Просмотреть файл

@ -8,8 +8,7 @@ aliases:
- /guides/deployment-orchestration/swarm-deploy/
summary: |
Discover how to deploy and manage Docker containers using Docker Swarm.
subjects: [deploy]
levels: [beginner]
tags: [deploy]
params:
time: 10 minutes
---

Просмотреть файл

@ -4,9 +4,8 @@ keywords: tensorflow.js, machine learning, ml, mediapipe, blazeface, face detect
title: Face detection with TensorFlow.js
summary: |
This guide explains how to run TensorFlow.js in Docker containers.
subjects: [ai]
tags: [ai]
languages: [js]
levels: [beginner]
aliases:
- /guides/use-case/tensorflowjs/
params:

Просмотреть файл

@ -6,8 +6,7 @@ summary: |
Automate, scale, and optimize testing workflows with Testcontainers Cloud
description: |
Testcontainers Cloud by Docker streamlines integration testing by offloading container management to the cloud. It enables faster, consistent tests for containerized services like databases, improving performance and scalability in CI/CD pipelines without straining local or CI resources. Ideal for developers needing efficient, reliable testing environments.
levels: [Beginner]
products: [testcontainers]
tags: [product-demo]
params:
featured: true
image: images/learning-paths/testcontainers-cloud-learning-path.png

Просмотреть файл

@ -6,9 +6,8 @@ description: Learn how to build and run a text recognition application using Pyt
summary: |
This guide details how to containerize text classification models using
Docker.
subjects: [ai]
tags: [ai]
languages: [python]
levels: [beginner]
aliases:
- /guides/use-case/nlp/text-classification/
params:

Просмотреть файл

@ -5,9 +5,8 @@ keywords: nlp, natural language processing, text summarization, python, bert ext
description: Learn how to build and run a text summarization application using Python, Bert Extractive Summarizer, and Docker.
summary: |
This guide shows how to containerize text summarization models with Docker.
subjects: [ai]
tags: [ai]
languages: [python]
levels: [beginner]
aliases:
- /guides/use-case/nlp/text-summarization/
params:

Просмотреть файл

@ -4,8 +4,7 @@ description: &desc Use Traefik to easily route traffic between multiple containe
keywords: traefik, container-supported development
linktitle: HTTP routing with Traefik
summary: *desc
levels: [intermediate]
subjects: [networking]
tags: [networking]
params:
time: 20 minutes
---

Просмотреть файл

@ -4,8 +4,8 @@ description: &desc Mocking API services in development and testing with WireMock
keywords: WireMock, container-supported development
linktitle: Mocking API services with WireMock
summary: *desc
levels: [intermediate]
subjects: [distributed-systems]
tags: [app-dev, distributed-systems]
languages: [js]
params:
time: 20 minutes
---

Просмотреть файл

@ -3,3 +3,4 @@ cascade:
build:
render: never
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: C#
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: C++
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/cplusplus/cplusplus-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: Go
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: Java
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: JavaScript
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: PHP
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/php/php-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: Python
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: R
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/r/r-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: Ruby
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/ruby/ruby-original.svg
---

Просмотреть файл

@ -1,3 +1,5 @@
---
title: Rust
params:
icon: https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/rust/rust-original.svg
---

Просмотреть файл

@ -1,5 +0,0 @@
---
cascade:
build:
render: never
---

Просмотреть файл

@ -1,5 +0,0 @@
---
cascade:
build:
render: never
---

Просмотреть файл

@ -1,3 +0,0 @@
---
title: Docker Compose
---

Просмотреть файл

@ -1,3 +0,0 @@
---
title: Docker Build Cloud
---

Просмотреть файл

@ -1,3 +0,0 @@
---
title: Docker Scout
---

Просмотреть файл

@ -1,5 +0,0 @@
---
cascade:
build:
render: never
---

Просмотреть файл

@ -0,0 +1,3 @@
---
title: Administration
---

Просмотреть файл

Просмотреть файл

@ -0,0 +1,3 @@
---
title: App development
---

Просмотреть файл

@ -1,6 +1,5 @@
---
title: Best practices
icon: star
description: Optimal patterns for Docker
---

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,6 +1,5 @@
---
title: FAQ
icon: quiz
description: Frequently asked questions
---

Просмотреть файл

@ -0,0 +1,3 @@
---
title: Product demo
---

Просмотреть файл

@ -1,6 +1,5 @@
---
title: Secrets
icon: password
description: Use sensitive information in containers securely
---

Просмотреть файл

@ -1,6 +1,5 @@
---
title: Troubleshooting
icon: troubleshoot
description: Fix common issues
---

Просмотреть файл

@ -9,11 +9,7 @@ ignoreLogs:
taxonomies:
tag: tags
# filters for guides:
language: languages
subject: subjects
level: levels
product: products
permalinks:
page:

Просмотреть файл

@ -41,6 +41,7 @@
"Docker-Compose",
"Docker-Desktop",
"Docker-Hub",
"Docker-Scout-Dashboard",
"Download",
"Editor-or-IDE",
"Entra-ID",
@ -86,6 +87,7 @@
"Node",
"Non-compliant",
"Okta",
"Okta-SAML",
"Old-Dockerfile",
"PHP",
"PowerShell",
@ -192,7 +194,6 @@
"chroma",
"col-start-2",
"containerd-image-store",
"cursor-not-allowed",
"cursor-pointer",
"dark:bg-amber-dark",
"dark:bg-amber-dark-200",
@ -231,8 +232,8 @@
"dark:hover:bg-gray-dark-200",
"dark:hover:bg-gray-dark-500",
"dark:hover:text-blue-dark",
"dark:hover:text-white",
"dark:prose-invert",
"dark:ring-blue-dark-400",
"dark:ring-gray-dark-400",
"dark:syntax-dark",
"dark:text-amber-dark",
@ -292,7 +293,6 @@
"grid-cols-1",
"group",
"group-hover:block'",
"group-hover:underline",
"h-16",
"h-2",
"h-32",
@ -311,7 +311,6 @@
"hover:bg-gray-light-100",
"hover:bg-gray-light-200",
"hover:bg-gray-light-300",
"hover:bg-white",
"hover:bg-white/20",
"hover:border-gray-light-200",
"hover:border-white/20",
@ -321,7 +320,6 @@
"hover:dark:text-blue-dark",
"hover:drop-shadow-lg",
"hover:opacity-90",
"hover:text-black",
"hover:text-blue-light",
"hover:text-white",
"hover:underline",
@ -329,6 +327,7 @@
"icon-sm",
"icon-svg",
"inline",
"inline-flex",
"inset-0",
"invertible",
"italic",
@ -352,7 +351,6 @@
"lg:no-underline",
"lg:pb-2",
"lg:scale-100",
"lg:text-base",
"link",
"lntable",
"lntd",
@ -368,6 +366,7 @@
"max-w-full",
"max-w-none",
"max-w-xl",
"mb-1",
"mb-2",
"mb-4",
"mb-8",
@ -399,7 +398,6 @@
"mt-4",
"mx-auto",
"my-0",
"my-2",
"my-4",
"my-6",
"no-underline",
@ -427,6 +425,7 @@
"pb-1",
"pb-2",
"pb-4",
"pb-8",
"pl-1",
"pl-2",
"pl-3",
@ -440,7 +439,6 @@
"pt-4",
"px-1",
"px-2",
"px-3",
"px-4",
"px-6",
"py-1",
@ -452,7 +450,9 @@
"right-0",
"right-3",
"right-8",
"ring-2",
"ring-[1.5px]",
"ring-blue-light-400",
"ring-gray-light-200",
"rotate-45",
"rounded",
@ -519,9 +519,7 @@
"top-full",
"transition",
"truncate",
"underline",
"underline-offset-2",
"underline-offset-8",
"w-2",
"w-8",
"w-[1200px]",

Просмотреть файл

@ -1,165 +1,221 @@
{{ define "left" }}
{{ partial "sidebar/mainnav.html" . }}
<div class="p-4 flex flex-col gap-4 text-sm">
<div>
<span class="icon-svg">{{ partialCached "icon" "filter_alt" "filter_alt" }}</span>
<span>Filters</span>
</div>
<div class="space-y-4"
x-data="{
selected: { products: [], subjects: [], languages: [], levels: [], },
toggleFilter(taxonomy, term) {
this.selected[taxonomy].includes(term)
? this.selected[taxonomy] = this.selected[taxonomy].filter(el => el != term)
: this.selected[taxonomy].push(term);
{{- partial "sidebar/mainnav.html" . }}
<div class="p-4 flex flex-col gap-2 text-sm">
<p>Filter guides by tag or programming language.<p>
<div class="space-y-4" x-data="{
filters: { tags: [], languages: [] },
toggleFilter(grp, tag) {
this.filters[grp].includes(tag)
? this.filters[grp] = this.filters[grp].filter(el => el != tag)
: this.filters[grp].push(tag);
// Update URL
const url = new URL(window.location.href);
if (this.selected[taxonomy].length > 0) {
url.searchParams.set(taxonomy, this.selected[taxonomy].join('~'));
if (this.filters[grp].length > 0) {
url.searchParams.set(grp, this.filters[grp].join('~'));
} else {
url.searchParams.delete(taxonomy);
url.searchParams.delete(grp);
}
window.history.replaceState({}, '', url);
this.$dispatch('guide-filter', { filters: this.selected });
this.$dispatch('guide-filter', { filters: this.filters });
},
init() {
const url = new URL(window.location.href);
for (const [key, value] of url.searchParams.entries()) {
if (value) {
this.selected[key] = value.split('~');
}
const tags = url.searchParams.get('tags')
const langs = url.searchParams.get('languages')
if (tags != null) {
this.filters['tags'] = tags.split('~');
}
if (langs != null) {
this.filters['languages'] = langs.split('~');
}
}
}">
{{ template "taxofilters" site.Taxonomies.products }}
{{ template "taxofilters" site.Taxonomies.subjects }}
{{ template "taxofilters" site.Taxonomies.levels }}
{{ template "taxofilters" site.Taxonomies.languages }}
}" @guide-filter.window="filters = $event.detail.filters;">
<div class="pl-2"><strong>Tags</strong></div>
<fieldset class="flex flex-col gap-2">
{{- range $name, $taxonomy := where site.Taxonomies.tags ".Page.Type" "guides" }}
{{- $id := anchorize (fmt.Printf "tag-%s" $name) }}
<div class="pl-2 flex gap-2">
<input value="{{ $name }}" type="checkbox" id="{{ $id }}" @change="toggleFilter('tags', '{{ $name }}')"
:checked="filters['tags'].includes('{{ $name }}')">
<label class="select-none" for="{{ $id }}">{{ .Page.LinkTitle }}</label>
</div>
{{ end }}
</fieldset>
<div class="pl-2"><strong>Languages</strong></div>
<fieldset class="pl-2 flex flex-wrap gap-2">
{{- range $name, $taxonomy := where site.Taxonomies.languages ".Page.Type" "guides" }}
{{- $id := anchorize (fmt.Printf "lang-%s" $name) }}
<button
class="px-2 py-1 rounded-full flex gap-1 bg-white dark:bg-gray-dark-300 border border-divider-light dark:border-divider-dark"
:class="{ 'ring-2 ring-blue-light-400 dark:ring-blue-dark-400': filters['languages'].includes('{{ $name }}') }"
@click="toggleFilter('languages', '{{ $name }}')">
<img height="18" width="18" title="{{ .Page.LinkTitle }}" src="{{ .Page.Params.icon }}">
<span>{{ .Page.LinkTitle }}</span>
</button>
{{ end }}
</fieldset>
</div>
</div>
{{ end }}
{{ define "taxofilters" }}
{{- $taxonomy := .Page.Data.Plural }}
<fieldset class="flex flex-col gap-2">
<div>{{ humanize $taxonomy }}</div>
{{- range . }}
{{- $term := .Page.Data.Term }}
<div class="pl-2 flex gap-2">
{{- $id := anchorize (fmt.Printf "%s-%s" $taxonomy $term) }}
<input value="{{$term}}" type="checkbox" id="{{ $id }}"
@change="toggleFilter('{{$taxonomy}}','{{$term}}')"
:checked="selected['{{$taxonomy}}'].includes('{{$term}}')">
<label class="select-none" for="{{ $id }}">{{ .Page.LinkTitle }}</label>
</div>
{{ end }}
</fieldset>
{{ end }}
{{ define "main" }}
<div class="flex gap-8 w-full">
<article class="prose min-w-0 max-w-none dark:prose-invert">
{{- partial "breadcrumbs.html" . }}
<h1 data-pagefind-weight="10" class="scroll-mt-36">{{ .Title }}</h1>
{{ .Content }}
<h2>Featured</h2>
<div class="not-prose py-4 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8">
{{- $featured := where .Pages "Params.featured" true }}
{{- with $featured }}
{{- range . }}
<div class="flex flex-col h-full">
<a class="hover:underline" href="{{ .Permalink }}">
{{- $img := resources.Get (.Params.image | default "/images/thumbnail.webp") }}
{{- $img = $img.Process "resize 600x" }}
<img class="h-48 w-full object-cover rounded shadow" src="{{ $img.Permalink }}">
<p class="text-xl leading-snug my-4">{{ .Title }}</p>
</a>
<p class="flex-grow text-sm">{{ .Summary }}</p>
<div class="mt-4">
{{ template "guide-metadata" . }}
</div>
</div>
{{- end }}
{{- end }}
</div>
</ul>
<hr class="text-divider-light dark:text-divider-dark">
{{- $taxonomies := slice "products" "subjects" "levels" "languages" }}
<div class="not-prose min-h-screen"
x-data="{
filters: { {{ delimit (apply $taxonomies "fmt.Printf" "%s: []," "." ) "" }} },
<div class="not-prose min-h-screen" x-data="{
filters: { tags: [], languages: [] },
hidden: [],
total: {{ len .Pages }},
noFilters() {
return Object.values(this.filters).every(arr=> Array.isArray(arr) && arr.length === 0);
},
showItem(taxonomies) {
if (this.noFilters()) return true;
let match = false;
isVisible(el) {
return !this.hidden.includes(el.id);
},
for (const taxonomy in this.filters) {
const selectedTerms = this.filters[taxonomy];
if (selectedTerms.length > 0) {
const itemTerms = taxonomies[taxonomy] || [];
// Check if all selected terms are included in the item's terms
const hasAnyTerms = selectedTerms.some(term => itemTerms.includes(term));
if (hasAnyTerms) {
match = true;
break;
updateVisible() {
const hiddenSet = new Set();
// show if no filters have been selected
if (this.noFilters()) return;
const guideContainer = this.$refs['guide-container'];
const guides = guideContainer.children
// Loop over the filters object
for (const [key, value] of Object.entries(this.filters)) {
if (!value || value.length === 0) continue;
for (const g of guides) {
// Get the dataset for the current guide (e.g., languages or tags)
const terms = JSON.parse(g.dataset[key]);
// Check if any of the filter values exist in the terms
const containsSome = terms.some(term => this.filters[key].includes(term));
// If none of the terms match the filter, mark the guide as hidden
if (!containsSome) {
hiddenSet.add(g.id)
}
}
}
return match;
this.hidden = Array.from(hiddenSet)
},
init() {
const url = new URL(window.location.href);
for (const [key, value] of url.searchParams.entries()) {
if (value) {
this.filters[key] = value.split('~');
}
const tags = url.searchParams.get('tags')
const langs = url.searchParams.get('languages')
if (tags != null) {
this.filters['tags'] = tags.split('~');
}
if (langs != null) {
this.filters['languages'] = langs.split('~');
}
this.updateVisible();
}
}"
x-cloak
@guide-filter.window="filters = $event.detail.filters; document.getElementById('all-guides').scrollIntoView({ behavior: 'smooth' })">
<h2 id="all-guides" class="scroll-mt-36">All guides</h2>
{{- range .Pages }}
{{- $opts := dict "page" . "taxonomies" $taxonomies }}
{{- $filters := partial "utils/filter-terms.html" $opts }}
<a href="{{ .Permalink }}" x-show="showItem({{ jsonify $filters }});" x-transition
class="group flex flex-col justify-between p-4 border-b
border-divider-light hover:bg-white
hover:dark:bg-gray-dark-200 dark:border-divider-dark
drop-shadow transition">
<div class="flex flex-col xl:flex-row justify-between">
<div class="text-lg group-hover:underline mb-2 xl:mb-0 truncate">{{ .Title }}</div>
{{ template "guide-metadata" . }}
}" x-cloak
@guide-filter.window="filters = $event.detail.filters; updateVisible(); $nextTick(() => { window.scrollTo({ top: 0 }) })">
<div x-show="noFilters()">
<h2>Featured guides</h2>
<div class="not-prose py-4 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8">
{{- $featured := where .Pages "Params.featured" true }}
{{- with $featured }}
{{- range . }}
<div class="flex flex-col h-full">
<a class="hover:underline" href="{{ .Permalink }}">
{{- $img := resources.Get (.Params.image | default "/images/thumbnail.webp") }}
{{- $img = $img.Process "resize 600x" }}
<img class="h-48 w-full object-cover rounded shadow" src="{{ $img.Permalink }}">
<p class="text-xl leading-snug my-4">{{ .Title }}</p>
</a>
<p class="flex-grow text-sm">{{ .Summary }}</p>
<div class="mt-4">
{{ template "guide-metadata" . }}
</div>
</div>
{{- end }}
{{- end }}
</div>
<hr class="text-divider-light dark:text-divider-dark">
</div>
<h2 x-show="noFilters()" id="all-guides" class="scroll-mt-36">All guides</h2>
<div x-show="!noFilters()" class="pb-8 flex flex-col gap-2">
<div class="flex gap-2 items-center">
<span class="mb-1 icon-svg icon-sm">{{ partialCached "icon" "filter_alt" "filter_alt" }}</span>
<p>Filtered results: showing <span x-text="total - hidden.length"></span> out of <span x-text="total"></span> guides.</p>
</div>
<div class="pl-4 flex gap-2 items-center">
{{- range $name, $taxonomy := site.Taxonomies.tags }}
<div x-show="filters.tags.includes('{{$name}}')">{{ template "termchip" $taxonomy.Page.LinkTitle }}</div>
{{- end }}
{{- range $name, $taxonomy := site.Taxonomies.languages }}
<div x-show="filters.languages.includes('{{$name}}')"
class="text-sm inline-flex gap-1 items-center rounded-full border
border-divider-light dark:border-divider-dark bg-gray-light-100
px-2 text-gray-light-800 dark:bg-gray-dark-200
dark:text-gray-dark-800 select-none">
<img class="py-1" height="18" width="18" title="{{ .Page.LinkTitle }}" src="{{ .Page.Params.icon }}">
<span>{{ .Page.LinkTitle }}</span>
</div>
</a>
{{- end }}
{{- end }}
</div>
</div>
<div x-ref="guide-container">
{{- range .Pages }}
<div
id="guide-{{ math.Counter }}"
data-languages='{{ jsonify (.Params.languages | default slice) }}'
data-tags='{{ jsonify (.Params.tags | default slice) }}'
x-show="isVisible($el);"
class="flex flex-col justify-between p-4 border-b border-divider-light
hover:bg-gray-light-100 hover:dark:bg-gray-dark-200
dark:border-divider-dark">
<div class="flex flex-col xl:flex-row justify-between">
<a href="{{ .Permalink }}" class="text-lg hover:underline mb-2 xl:mb-0 truncate">{{ .Title }}</a>
{{ template "guide-metadata" . }}
</div>
</div>
{{- end }}
</div>
</div>
</article>
</div>
{{ end }}
{{- define "guide-metadata" }}
<div class="flex gap-8 items-center text-sm justify-between text-gray-light dark:text-gray-dark">
<div class="text-sm flex gap-8 items-center justify-between text-gray-light dark:text-gray-dark">
<div class="flex flex-wrap md:flex-nowrap gap-2">
{{- $taxoterms := .GetTerms "languages" }}
{{- $taxoterms = $taxoterms | append (.GetTerms "levels") }}
{{- $taxoterms = $taxoterms | append (.GetTerms "subjects") }}
{{- range $taxoterms }}
<span class="rounded whitespace-nowrap bg-gray-light-200 dark:bg-gray-dark-300 px-2">{{- .Page.LinkTitle }}</span>
{{- $langs := .GetTerms "languages" }}
{{ partial "languages" $langs }}
{{- $tags := .GetTerms "tags" }}
{{- range $tags }}
{{ template "termchip" .Page.LinkTitle }}
{{- end }}
</div>
{{- with .Params.time }}
<div class="flex whitespace-nowrap flex-shrink gap-2">
<span class="icon-svg">{{ partialCached "icon" "schedule" "schedule" }}</span>
<span>{{ . }}</span>
</div>
<div class="flex whitespace-nowrap flex-shrink gap-2">
<span class="icon-svg">{{ partialCached "icon" "schedule" "schedule" }}</span>
<span>{{ . }}</span>
</div>
{{- end }}
</div>
{{- end }}
{{- define "termchip" }}
<span class="whitespace-nowrap inline-flex text-sm min-w-0 items-center rounded-full
border border-divider-light dark:border-divider-dark
bg-gray-light-100 px-2 text-gray-light-800 dark:bg-gray-dark-200
dark:text-gray-dark-800 select-none">
<span class="icon-svg icon-sm pb-0.5">
{{ partialCached "icon" "tag" "tag" }}
</span>
{{ . }}
</span>
{{- end }}

Просмотреть файл

@ -2,6 +2,13 @@
<article class="prose min-w-0 flex-[2_2_0%] max-w-4xl dark:prose-invert">
{{ partial "breadcrumbs.html" . }}
<h1 data-pagefind-weight="10" class="scroll-mt-36">{{ .Title }}</h1>
{{- if ne .Type "guides" }}
{{ with .GetTerms "tags" }}
<div class="not-prose">
{{- partial "tags.html" . }}
</div>
{{- end }}
{{- end }}
<div class="block lg:hidden">
{{ partialCached "pagemeta.html" . . }}
<hr />

Просмотреть файл

@ -1,8 +1,8 @@
{{- $svg := resources.Get (fmt.Printf "icons/%s-fill.svg" .) }}
{{- if not $svg }}
{{- errorf "Failed to get icon: %s" . }}
{{- errorf "Failed to get icon: %v\n\n" . }}
{{ end }}
{{- if not $svg.Content }}
{{- errorf "Failed to get icon: %s" . }}
{{- errorf "Failed to get icon: %v\n\n" . }}
{{- end }}
{{- safe.HTML $svg.Content -}}

Просмотреть файл

@ -0,0 +1,21 @@
{{- /*
List of languages (taxonomy) chips with images
Context: page.Pages
*/
-}}
{{- range . -}}
{{- if eq .File nil }}
{{- errorf "[languages] Undefined language: '%s' in %s" (urlize (strings.ToLower .Title)) page.File.Filename }}
{{- end }}
{{- if not .Page.Params.icon }}
{{- errorf "[languages] language is missing an icon: '%s' in %s" (urlize (strings.ToLower .Title)) page.File.Filename }}
{{- end }}
<span class="text-sm inline-flex gap-1 items-center rounded-full border
border-divider-light dark:border-divider-dark bg-gray-light-100
px-2 text-gray-light-800 dark:bg-gray-dark-200
dark:text-gray-dark-800 select-none">
<img class="py-1" height="18" width="18" title="{{ .Page.LinkTitle }}" src="{{ .Page.Params.icon }}">
<span>{{ .Page.LinkTitle }}</span>
</span>
{{- end -}}

Просмотреть файл

@ -2,24 +2,13 @@
{{- with .Fragments }}
{{- $toc = and (ne page.Params.notoc true) .Headings }}
{{- end }}
{{- $tags := .GetTerms "tags" }}
{{- if or $toc $tags }}
{{- with $toc }}
<div data-pagefind-ignore class="not-prose">
{{- with $tags }}
<div class="flex flex-col gap-2 my-2">
<span class="text-lg">Tags</span>
<div class="flex flex-wrap items-center gap-2">
{{- partial "tags.html" . -}}
</div>
</div>
{{- end }}
{{- with $toc }}
<div class="text-lg pb-0 lg:pb-2">{{ T "tableOfContents" }}</div>
<nav class="toc">
{{ $root := (index page.Fragments.Headings 0).Headings }}
{{- template "walkHeadingFragments" $root }}
</nav>
{{- end }}
<div class="text-lg pb-0 lg:pb-2">{{ T "tableOfContents" }}</div>
<nav class="toc">
{{ $root := (index page.Fragments.Headings 0).Headings }}
{{- template "walkHeadingFragments" $root }}
</nav>
</div>
{{- end }}

Просмотреть файл

@ -9,18 +9,15 @@
<a href="{{ $root.Permalink }}">{{ $root.Title }}</a>
</div>
<div>{{ $root.Summary }}</div>
<div class="space-y-2">
{{- with ($root.GetTerms "levels") }}
<div class="flex gap-2 text-gray-light dark:text-gray-dark">
<span class="icon-svg">{{ partialCached "icon" "school" "school" }}</span>
<span>
{{- range $i, $e := . -}}
{{- if $i -}},{{ end -}}
{{- .Page.Title -}}
{{- end -}}
</span>
</div>
{{- end -}}
<div class="space-y-4">
<div class="flex flex-wrap gap-2">
{{- with ($root.GetTerms "languages") }}
{{ partial "languages.html" . }}
{{- end }}
{{- with ($root.GetTerms "tags") }}
{{ partial "tags.html" . }}
{{- end }}
</div>
{{- with ($root.Params.time) }}
<div class="flex gap-2 text-gray-light dark:text-gray-dark">
<span class="icon-svg"

Просмотреть файл

@ -1,10 +1,13 @@
<!-- Main navigation for the sidebar -->
<div class="py-2 px-4" x-data="{ expanded: false }">
<div class="flex w-full items-center justify-between">
<!-- Current section: use menu, fall back to current page -->
{{- $curr := page }}
<!-- Current section: use menu, fall back to current section or page -->
{{- $curr := .FirstSection }}
{{- if eq $curr site.Home }}
{{- $curr = . }}
{{- end }}
{{- range site.Menus.main }}
{{- if .Page.IsAncestor page }}
{{- if or (.Page.IsAncestor page) (eq .Page page) }}
{{- $curr = .Page }}
{{- end }}
{{- end }}

Просмотреть файл

@ -1,11 +1,10 @@
<ul>
<ul class="md:text-sm">
{{- range site.Taxonomies.tags }}
<li class="pl-4 hover:text-blue-light hover:dark:text-blue-dark
{{ if eq .Page page }} bg-gray-light-200 dark:bg-gray-dark-200{{ end }}">
<a class="py-2 w-full truncate block"
href="{{ .Page.Permalink }}"
title="{{ .Page.LinkTitle }}">
<span class="icon-svg">{{- partialCached "icon.html" .Page.Params.icon .Page.Params.icon -}}</span>
{{ .Page.LinkTitle }}
</a>
</li>

Просмотреть файл

@ -1,13 +1,20 @@
{{- /*
List of tag "chips" as links
Context: page.Pages
*/ -}}
*/
-}}
{{- range . -}}
{{- if eq .File nil }}
{{- errorf "[tags] Undefined tag: '%s' in %s" (urlize (strings.ToLower .Title)) page.File.Filename }}
{{- end }}
<a class="flex items-center gap-2 lg:text-base px-2 rounded text-gray-light-800 bg-gray-light-200 dark:bg-gray-dark-300 dark:text-gray-dark-800" href="{{ .Permalink }}">
<div class="icon-svg icon-sm pb-0.5">{{ partialCached "icon" .Params.icon .Params.icon }}</div>
<div>{{ .LinkTitle }}</div>
{{- if eq .File nil }}
{{- errorf "[tags] Undefined tag: '%s' in %s" (urlize (strings.ToLower .Title)) page.File.Filename }}
{{- end }}
<a class="text-sm inline-flex items-center rounded-full border
border-divider-light dark:border-divider-dark bg-gray-light-100 px-2
text-gray-light-800 dark:bg-gray-dark-200 dark:text-gray-dark-800
select-none"
href="{{ .Permalink }}">
<span class="icon-svg icon-sm pb-0.5">
{{ partialCached "icon" "tag" "tag" }}
</span>
<span>{{ .LinkTitle }}</span>
</a>
{{- end -}}

Просмотреть файл

@ -1,11 +0,0 @@
{{- $currentPage := .page }}
{{- $taxonomies := .taxonomies }}
{{- $scratch := collections.NewScratch }}
{{- range $taxo := $taxonomies }}
{{- $terms := slice }}
{{- range ($currentPage.GetTerms $taxo) }}
{{- $terms = $terms | append .Page.Data.Term }}
{{- end }}
{{ $scratch.SetInMap "filters" $taxo $terms }}
{{- end }}
{{- return ($scratch.Get "filters") }}

Просмотреть файл

@ -6,19 +6,18 @@
{{ define "main" }}
<article class="prose max-w-none dark:prose-invert">
{{ partial "breadcrumbs.html" . }}
<h1 class="scroll-mt-36">{{ .Title }}</h1>
<h1 class="scroll-mt-36 flex items-center">
<span class="icon-svg icon-lg pb-2">{{ partialCached "icon" "tag" "tag" }}</span>
<span>{{ .Title }}</span>
</h1>
{{ .Content }}
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3">
<ul>
{{ range site.Taxonomies.tags }}
{{ partial "components/card.html"
(dict
"link" .Page.Permalink
"title" .Page.Title
"description" .Page.Description
"icon" .Page.Params.icon
)
}}
<li>
<a href="{{ .Page.Permalink }}">{{ .Page.Title }}</a>
({{ (len .Pages) }} {{ cond (gt (len .Pages) 1) "pages" "page" }})
</li>
{{ end }}
</div>
</ul>
</article>
{{ end }}

Просмотреть файл

@ -1,44 +1,25 @@
{{ define "left" }}
{{ partial "sidebar/mainnav.html" . }}
{{ partial "sidebar/tags.html" . }}
{{ end }}
{{ define "main" }}
<article class="prose max-w-none dark:prose-invert">
{{ partial "breadcrumbs.html" . }}
<h1 class="scroll-mt-36 flex gap-4 items-center">
<span class="bg-gray-light-200 dark:bg-gray-dark-300 rounded-full px-3 icon-svg icon-lg pb-2">{{ partialCached "icon" .Params.icon .Params.icon }}</span>
<h1 class="scroll-mt-36 flex items-center">
<span class="icon-svg icon-lg pb-2">{{ partialCached "icon" "tag" "tag" }}</span>
</span>{{ .Title }}</span>
</h1>
{{ .Content }}
<div class="not-prose flex justify-between items-center">
<span class="text-gray-light dark:text-gray-dark">Pages with this tag:</span>
{{ partial "pagination.html" . }}
</div>
<table style="display: table">
<thead>
<tr>
<th width="50%">Title</th>
<th width="50%">Section</th>
</tr>
</thead>
<tbody>
{{ range .Paginator.Pages }}
<tr>
<td>
<a href="{{ .Permalink }}" class="link">
{{ .Title }}
</a>
</td>
<td>
<span class="text-gray-light dark:text-gray-dark">
{{- range .Ancestors.Reverse }}
{{ .Title }} /
{{- end }}
</span>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{- range .Pages.GroupBy "Type" }}
<h2>{{ (site.GetPage .Key).LinkTitle }}</h2>
<ul>
{{ range .Pages }}
<li>
<a href="{{ .Permalink }}">{{ .Title }}</a>
</li>
{{- end }}
</ul>
{{- end }}
</article>
{{ end }}