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 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>
|
Reference in New Issue
Block a user