A curated list of libraries & SDKs for the Bluesky API and AT Protocol

added tag filtering

+56 -3
+5 -1
_includes/project_card.html
··· 15 15 {% assign user = '@' | append: info.user_login %} 16 16 {% endif %} 17 17 18 - <li class="{% if info.user_login == 'bluesky-social' %}official{% endif %}"> 18 + <li class="{% if info.user_login == 'bluesky-social' %}official{% endif %}" 19 + {% if include.project.tags %} 20 + data-tags="{{ include.project.tags | join: ',' }}" 21 + {% endif %}> 22 + 19 23 <p class="title"> 20 24 <a class="project-name" href="{{ include.project.url | default: include.project.urls[0] }}" target="_blank">{{ include.project.name | default: info.name }}</a> 21 25 <span class="author"><span class="dot">•</span>
+1
_layouts/default.html
··· 16 16 <meta name="twitter:image" content="/assets/images/embed.png"> 17 17 18 18 <link href="/assets/style.css" rel="stylesheet"> 19 + <script defer src="/assets/scripts.js"></script> 19 20 </head> 20 21 21 22 <body>
+33
assets/scripts.js
··· 1 + document.addEventListener('DOMContentLoaded', () => { 2 + let tagCloud = document.getElementById('tag_cloud'); 3 + let tagButtons = Array.from(tagCloud.querySelectorAll('span[data-tag]')); 4 + let languageSections = Array.from(document.querySelectorAll('.language')); 5 + 6 + let activeTag = null; 7 + 8 + for (let button of tagButtons) { 9 + button.addEventListener('click', (event) => { 10 + let tagValue = button.dataset.tag; 11 + activeTag = (activeTag === tagValue) ? null : tagValue; 12 + 13 + tagButtons.forEach(button => { 14 + button.classList.toggle('selected', button.dataset.tag === activeTag); 15 + }); 16 + 17 + languageSections.forEach(section => { 18 + let projectItems = Array.from(section.querySelectorAll('.projects > li')); 19 + let anyMatched = false; 20 + 21 + for (let project of projectItems) { 22 + let projectTags = project.dataset.tags ? project.dataset.tags.split(',') : []; 23 + let match = !activeTag || projectTags.includes(activeTag); 24 + 25 + project.style.display = match ? '' : 'none'; 26 + anyMatched = anyMatched || match; 27 + } 28 + 29 + section.style.display = anyMatched ? '' : 'none'; 30 + }); 31 + }); 32 + } 33 + });
+15
assets/style.scss
··· 67 67 } 68 68 } 69 69 70 + #tag_cloud { 71 + span { 72 + cursor: pointer; 73 + transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease; 74 + 75 + &.selected { 76 + background-color: hsl(210, 90%, 45%); 77 + border-color: hsl(210, 90%, 38%); 78 + color: white; 79 + box-shadow: 0 0 0 2px hsla(210, 90%, 45%, 0.25); 80 + } 81 + } 82 + } 83 + 70 84 .language { 71 85 margin-bottom: 60px; 72 86 ··· 166 180 } 167 181 168 182 .tags span { background-color: #073659; border-color: #1c5179; color: #77aad1; } 183 + #tag_cloud span.selected { background-color: hsl(210, 80%, 58%); border-color: hsl(210, 80%, 52%); color: #01213a; box-shadow: 0 0 0 2px hsla(210, 80%, 58%, 0.25); } 169 184 170 185 .projects li { 171 186 border-color: #666;
+2 -2
index.html
··· 25 25 26 26 <div class="nav-separator"></div> 27 27 28 - <p class="tags"> 28 + <p class="tags" id="tag_cloud"> 29 29 {% for tag in tags %} 30 - <span>{{ tag }}</span> 30 + <span data-tag="{{ tag }}">{{ tag }}</span> 31 31 {% endfor %} 32 32 </p> 33 33