adjust tag cloud
This commit is contained in:
parent
47eb5ad72a
commit
b8183ec961
@ -11,8 +11,18 @@ const data = load(yamlContent) as any;
|
||||
const domains = data.domains;
|
||||
const phases = data.phases;
|
||||
|
||||
// Get unique tags from all tools
|
||||
const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort();
|
||||
// Get unique tags from all tools with frequency count
|
||||
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">
|
||||
@ -56,24 +66,28 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
|
||||
<!-- Additional Filters -->
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Tag Filters -->
|
||||
<details>
|
||||
<summary style="cursor: pointer; font-weight: 500; margin-bottom: 0.5rem;">
|
||||
<!-- Tag Cloud -->
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
|
||||
Filter by Tags
|
||||
</summary>
|
||||
<div class="grid grid-cols-3 gap-2" style="margin-top: 0.5rem;">
|
||||
{allTags.map(tag => (
|
||||
<div class="checkbox-wrapper">
|
||||
<input type="checkbox" id={`tag-${tag}`} data-tag={tag} class="tag-filter" />
|
||||
<label for={`tag-${tag}`} style="font-size: 0.875rem;">{tag}</label>
|
||||
</div>
|
||||
</label>
|
||||
<div class="tag-cloud">
|
||||
{sortedTags.map(tag => (
|
||||
<button
|
||||
class="tag-cloud-item"
|
||||
data-tag={tag}
|
||||
data-frequency={tagFrequency[tag]}
|
||||
>
|
||||
{tag}
|
||||
<span class="tag-frequency">({tagFrequency[tag]})</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Toggle -->
|
||||
@ -84,7 +98,56 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
|
||||
</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
|
||||
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 phaseSelect = document.getElementById('phase-select');
|
||||
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');
|
||||
|
||||
// Track selected tags
|
||||
let selectedTags = new Set();
|
||||
|
||||
// Filter function
|
||||
function filterTools() {
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
const selectedDomain = domainSelect.value;
|
||||
const selectedPhase = phaseSelect.value;
|
||||
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 => {
|
||||
// Search filter
|
||||
@ -133,7 +196,7 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -144,6 +207,21 @@ const allTags = [...new Set(data.tools.flatMap((tool: any) => tool.tags))].sort(
|
||||
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
|
||||
function handleViewToggle(view) {
|
||||
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
|
||||
searchInput.addEventListener('input', filterTools);
|
||||
domainSelect.addEventListener('change', filterTools);
|
||||
phaseSelect.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 => {
|
||||
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();
|
||||
});
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user