Archived
2
0

adjust tag cloud

This commit is contained in:
overcuriousity 2025-07-14 15:29:11 +02:00
parent 47eb5ad72a
commit b8183ec961

View File

@ -11,8 +11,18 @@ const data = load(yamlContent) as any;
const domains = data.domains; const domains = data.domains;
const phases = data.phases; const phases = data.phases;
// Get unique tags from all tools // Get unique tags from all tools with frequency count
const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(); const tagFrequency = data.tools.reduce((acc: Record<string, number>, tool: any) => {
tool.tags.forEach((tag: string) => {
acc[tag] = (acc[tag] || 0) + 1;
});
return acc;
}, {});
// Sort tags by frequency (descending)
const sortedTags = Object.entries(tagFrequency)
.sort(([,a], [,b]) => (b as number) - (a as number))
.map(([tag]) => tag);
--- ---
<div class="filters-container"> <div class="filters-container">
@ -56,24 +66,28 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
<!-- Additional Filters --> <!-- Additional Filters -->
<div style="margin-bottom: 1.5rem;"> <div style="margin-bottom: 1.5rem;">
<div class="checkbox-wrapper" style="margin-bottom: 1rem;"> <div class="checkbox-wrapper" style="margin-bottom: 1rem;">
<input type="checkbox" id="include-proprietary" /> <input type="checkbox" id="include-proprietary" checked />
<label for="include-proprietary">Include Proprietary Software</label> <label for="include-proprietary">Include Proprietary Software</label>
</div> </div>
<!-- Tag Filters --> <!-- Tag Cloud -->
<details> <div style="margin-bottom: 1rem;">
<summary style="cursor: pointer; font-weight: 500; margin-bottom: 0.5rem;"> <label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
Filter by Tags Filter by Tags
</summary> </label>
<div class="grid grid-cols-3 gap-2" style="margin-top: 0.5rem;"> <div class="tag-cloud">
{allTags.map(tag => ( {sortedTags.map(tag => (
<div class="checkbox-wrapper"> <button
<input type="checkbox" id={`tag-${tag}`} data-tag={tag} class="tag-filter" /> class="tag-cloud-item"
<label for={`tag-${tag}`} style="font-size: 0.875rem;">{tag}</label> data-tag={tag}
</div> data-frequency={tagFrequency[tag]}
>
{tag}
<span class="tag-frequency">({tagFrequency[tag]})</span>
</button>
))} ))}
</div> </div>
</details> </div>
</div> </div>
<!-- View Toggle --> <!-- View Toggle -->
@ -84,7 +98,56 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
</div> </div>
</div> </div>
<script define:vars={{ toolsData: data.tools }}> <style>
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
}
.tag-cloud-item {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.375rem 0.75rem;
border: 1px solid var(--color-border);
border-radius: 1rem;
background-color: var(--color-bg);
color: var(--color-text);
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s ease;
user-select: none;
}
.tag-cloud-item:hover {
border-color: var(--color-primary);
background-color: var(--color-bg-secondary);
}
.tag-cloud-item.active {
background-color: var(--color-accent);
border-color: var(--color-accent);
color: white;
}
.tag-cloud-item.active:hover {
background-color: var(--color-accent-hover);
border-color: var(--color-accent-hover);
}
.tag-frequency {
font-size: 0.75rem;
opacity: 0.8;
}
.tag-cloud-item.active .tag-frequency {
opacity: 1;
}
</style>
<script define:vars={{ toolsData: data.tools, tagFrequency }}>
// Store tools data globally for filtering // Store tools data globally for filtering
window.toolsData = toolsData; window.toolsData = toolsData;
@ -94,18 +157,18 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
const domainSelect = document.getElementById('domain-select'); const domainSelect = document.getElementById('domain-select');
const phaseSelect = document.getElementById('phase-select'); const phaseSelect = document.getElementById('phase-select');
const proprietaryCheckbox = document.getElementById('include-proprietary'); const proprietaryCheckbox = document.getElementById('include-proprietary');
const tagFilters = document.querySelectorAll('.tag-filter'); const tagCloudItems = document.querySelectorAll('.tag-cloud-item');
const viewToggles = document.querySelectorAll('.view-toggle'); const viewToggles = document.querySelectorAll('.view-toggle');
// Track selected tags
let selectedTags = new Set();
// Filter function // Filter function
function filterTools() { function filterTools() {
const searchTerm = searchInput.value.toLowerCase(); const searchTerm = searchInput.value.toLowerCase();
const selectedDomain = domainSelect.value; const selectedDomain = domainSelect.value;
const selectedPhase = phaseSelect.value; const selectedPhase = phaseSelect.value;
const includeProprietary = proprietaryCheckbox.checked; const includeProprietary = proprietaryCheckbox.checked;
const selectedTags = Array.from(tagFilters)
.filter(cb => cb.checked)
.map(cb => cb.getAttribute('data-tag'));
const filtered = window.toolsData.filter(tool => { const filtered = window.toolsData.filter(tool => {
// Search filter // Search filter
@ -133,7 +196,7 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
} }
// Tag filter // Tag filter
if (selectedTags.length > 0 && !selectedTags.some(tag => tool.tags.includes(tag))) { if (selectedTags.size > 0 && !Array.from(selectedTags).some(tag => tool.tags.includes(tag))) {
return false; return false;
} }
@ -144,6 +207,21 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: filtered })); window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: filtered }));
} }
// Handle tag cloud clicks
function handleTagClick(tagItem) {
const tag = tagItem.getAttribute('data-tag');
if (selectedTags.has(tag)) {
selectedTags.delete(tag);
tagItem.classList.remove('active');
} else {
selectedTags.add(tag);
tagItem.classList.add('active');
}
filterTools();
}
// View toggle handler // View toggle handler
function handleViewToggle(view) { function handleViewToggle(view) {
viewToggles.forEach(btn => { viewToggles.forEach(btn => {
@ -161,17 +239,31 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
} }
} }
// Clear all tag filters function
function clearTagFilters() {
selectedTags.clear();
tagCloudItems.forEach(item => item.classList.remove('active'));
filterTools();
}
// Attach event listeners // Attach event listeners
searchInput.addEventListener('input', filterTools); searchInput.addEventListener('input', filterTools);
domainSelect.addEventListener('change', filterTools); domainSelect.addEventListener('change', filterTools);
phaseSelect.addEventListener('change', filterTools); phaseSelect.addEventListener('change', filterTools);
proprietaryCheckbox.addEventListener('change', filterTools); proprietaryCheckbox.addEventListener('change', filterTools);
tagFilters.forEach(cb => cb.addEventListener('change', filterTools));
tagCloudItems.forEach(item => {
item.addEventListener('click', () => handleTagClick(item));
});
viewToggles.forEach(btn => { viewToggles.forEach(btn => {
btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view'))); btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view')));
}); });
// Initial filter // Expose clear function globally for potential use
window.clearTagFilters = clearTagFilters;
// Initial filter - this ensures the initial state matches the checkbox state
filterTools(); filterTools();
}); });
</script> </script>