Archived
2
0

adjust style

This commit is contained in:
overcuriousity 2025-07-14 21:31:20 +02:00
parent 92dcd2ab74
commit 0667180da5
3 changed files with 457 additions and 110 deletions

View File

@ -36,13 +36,14 @@ const sortedTags = Object.entries(tagFrequency)
/> />
</div> </div>
<!-- Domain and Phase Dropdowns --> <!-- Domain Dropdown and Phase Buttons -->
<div class="grid grid-cols-2 gap-4" style="margin-bottom: 1.5rem;"> <div class="domain-phase-container" style="margin-bottom: 1.5rem;">
<div> <!-- Domain Selection -->
<div class="domain-section">
<label for="domain-select" style="display: block; margin-bottom: 0.5rem; font-weight: 500;"> <label for="domain-select" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">
Forensic Domain Forensic Domain
</label> </label>
<select id="domain-select"> <select id="domain-select" style="max-width: 300px;">
<option value="">All Domains</option> <option value="">All Domains</option>
{domains.map((domain: any) => ( {domains.map((domain: any) => (
<option value={domain.id}>{domain.name}</option> <option value={domain.id}>{domain.name}</option>
@ -50,16 +51,22 @@ const sortedTags = Object.entries(tagFrequency)
</select> </select>
</div> </div>
<div> <!-- Phase Selection Buttons -->
<label for="phase-select" style="display: block; margin-bottom: 0.5rem; font-weight: 500;"> <div class="phase-section">
<label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
Investigation Phase Investigation Phase
</label> </label>
<select id="phase-select"> <div class="phase-buttons">
<option value="">All Phases</option>
{phases.map((phase: any) => ( {phases.map((phase: any) => (
<option value={phase.id}>{phase.name}</option> <button
class="phase-button"
data-phase={phase.id}
type="button"
>
{phase.name}
</button>
))} ))}
</select> </div>
</div> </div>
</div> </div>
@ -72,15 +79,25 @@ const sortedTags = Object.entries(tagFrequency)
<!-- Tag Cloud --> <!-- Tag Cloud -->
<div style="margin-bottom: 1rem;"> <div style="margin-bottom: 1rem;">
<label style="display: block; margin-bottom: 0.75rem; font-weight: 500;"> <div class="tag-header">
Filter by Tags <label style="font-weight: 500;">
</label> Filter by Tags
<div class="tag-cloud"> </label>
{sortedTags.map(tag => ( <button
id="tag-cloud-toggle"
class="btn-tag-toggle"
data-expanded="false"
>
Show More
</button>
</div>
<div class="tag-cloud" id="tag-cloud">
{sortedTags.map((tag, index) => (
<button <button
class="tag-cloud-item" class="tag-cloud-item"
data-tag={tag} data-tag={tag}
data-frequency={tagFrequency[tag]} data-frequency={tagFrequency[tag]}
data-index={index}
> >
{tag} {tag}
<span class="tag-frequency">({tagFrequency[tag]})</span> <span class="tag-frequency">({tagFrequency[tag]})</span>
@ -98,56 +115,8 @@ const sortedTags = Object.entries(tagFrequency)
</div> </div>
</div> </div>
<style>
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
}
.tag-cloud-item { <script define:vars={{ toolsData: data.tools, tagFrequency, sortedTags }}>
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;
@ -155,19 +124,99 @@ const sortedTags = Object.entries(tagFrequency)
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('search-input'); const searchInput = document.getElementById('search-input');
const domainSelect = document.getElementById('domain-select'); const domainSelect = document.getElementById('domain-select');
const phaseSelect = document.getElementById('phase-select'); const phaseButtons = document.querySelectorAll('.phase-button');
const proprietaryCheckbox = document.getElementById('include-proprietary'); const proprietaryCheckbox = document.getElementById('include-proprietary');
const tagCloudItems = document.querySelectorAll('.tag-cloud-item'); const tagCloudItems = document.querySelectorAll('.tag-cloud-item');
const tagCloud = document.getElementById('tag-cloud');
const tagCloudToggle = document.getElementById('tag-cloud-toggle');
const viewToggles = document.querySelectorAll('.view-toggle'); const viewToggles = document.querySelectorAll('.view-toggle');
// Track selected tags // Track selected tags and phase
let selectedTags = new Set(); let selectedTags = new Set();
let selectedPhase = '';
let isTagCloudExpanded = false;
// Initialize tag cloud state
function initTagCloud() {
const visibleCount = 12; // Show first 12 tags initially
tagCloudItems.forEach((item, index) => {
if (index >= visibleCount) {
item.style.display = 'none';
}
});
}
// Toggle tag cloud expansion
function toggleTagCloud() {
isTagCloudExpanded = !isTagCloudExpanded;
const visibleCount = 12;
if (isTagCloudExpanded) {
tagCloud.classList.add('expanded');
tagCloudToggle.textContent = 'Show Less';
tagCloudToggle.setAttribute('data-expanded', 'true');
// Show all filtered tags
tagCloudItems.forEach(item => {
if (!item.classList.contains('hidden')) {
item.style.display = 'inline-flex';
}
});
} else {
tagCloud.classList.remove('expanded');
tagCloudToggle.textContent = 'Show More';
tagCloudToggle.setAttribute('data-expanded', 'false');
// Show only first visible tags
let visibleIndex = 0;
tagCloudItems.forEach(item => {
if (!item.classList.contains('hidden')) {
if (visibleIndex < visibleCount) {
item.style.display = 'inline-flex';
visibleIndex++;
} else {
item.style.display = 'none';
}
}
});
}
}
// Filter tag cloud based on search input
function filterTagCloud() {
const searchTerm = searchInput.value.toLowerCase();
let visibleCount = 0;
const maxVisibleWhenCollapsed = 12;
tagCloudItems.forEach(item => {
const tagName = item.getAttribute('data-tag').toLowerCase();
const shouldShow = tagName.includes(searchTerm);
if (shouldShow) {
item.classList.remove('hidden');
if (isTagCloudExpanded || visibleCount < maxVisibleWhenCollapsed) {
item.style.display = 'inline-flex';
visibleCount++;
} else {
item.style.display = 'none';
}
} else {
item.classList.add('hidden');
item.style.display = 'none';
}
});
// Update toggle button visibility
const hasHiddenTags = Array.from(tagCloudItems).some(item =>
!item.classList.contains('hidden') && item.style.display === 'none'
);
tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
}
// 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 includeProprietary = proprietaryCheckbox.checked; const includeProprietary = proprietaryCheckbox.checked;
const filtered = window.toolsData.filter(tool => { const filtered = window.toolsData.filter(tool => {
@ -230,6 +279,24 @@ const sortedTags = Object.entries(tagFrequency)
filterTools(); filterTools();
} }
// Handle phase button clicks
function handlePhaseClick(button) {
const phase = button.getAttribute('data-phase');
if (selectedPhase === phase) {
// Deselect if already selected
selectedPhase = '';
button.classList.remove('active');
} else {
// Select new phase
phaseButtons.forEach(btn => btn.classList.remove('active'));
selectedPhase = phase;
button.classList.add('active');
}
filterTools();
}
// View toggle handler // View toggle handler
function handleViewToggle(view) { function handleViewToggle(view) {
viewToggles.forEach(btn => { viewToggles.forEach(btn => {
@ -255,24 +322,45 @@ const sortedTags = Object.entries(tagFrequency)
filterTools(); filterTools();
} }
// Attach event listeners // Clear all filters function
searchInput.addEventListener('input', filterTools); function clearAllFilters() {
searchInput.value = '';
domainSelect.value = '';
selectedPhase = '';
phaseButtons.forEach(btn => btn.classList.remove('active'));
clearTagFilters();
filterTagCloud();
}
// Event listeners
searchInput.addEventListener('input', () => {
filterTagCloud();
filterTools();
});
domainSelect.addEventListener('change', filterTools); domainSelect.addEventListener('change', filterTools);
phaseSelect.addEventListener('change', filterTools);
proprietaryCheckbox.addEventListener('change', filterTools); proprietaryCheckbox.addEventListener('change', filterTools);
tagCloudToggle.addEventListener('click', toggleTagCloud);
tagCloudItems.forEach(item => { tagCloudItems.forEach(item => {
item.addEventListener('click', () => handleTagClick(item)); item.addEventListener('click', () => handleTagClick(item));
}); });
phaseButtons.forEach(btn => {
btn.addEventListener('click', () => handlePhaseClick(btn));
});
viewToggles.forEach(btn => { viewToggles.forEach(btn => {
btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view'))); btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view')));
}); });
// Expose clear function globally for potential use // Expose functions globally for potential use
window.clearTagFilters = clearTagFilters; window.clearTagFilters = clearTagFilters;
window.clearAllFilters = clearAllFilters;
// Initial filter - this ensures the initial state matches the checkbox state // Initialize
initTagCloud();
filterTagCloud();
filterTools(); filterTools();
}); });
</script> </script>

View File

@ -135,6 +135,12 @@ domains.forEach((domain: any) => {
</div> </div>
<script define:vars={{ toolsData: tools, collaborationTools, domainTools }}> <script define:vars={{ toolsData: tools, collaborationTools, domainTools }}>
// Helper function to get selected phase from active button
function getSelectedPhase() {
const activePhaseButton = document.querySelector('.phase-button.active');
return activePhaseButton ? activePhaseButton.getAttribute('data-phase') : '';
}
// Tool details functions // Tool details functions
window.showToolDetails = function(toolName) { window.showToolDetails = function(toolName) {
const tool = toolsData.find(t => t.name === toolName); const tool = toolsData.find(t => t.name === toolName);
@ -220,7 +226,8 @@ domains.forEach((domain: any) => {
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view'); const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix') { if (currentView === 'matrix') {
const selectedPhase = document.getElementById('phase-select')?.value; // Get selected phase from active button instead of dropdown
const selectedPhase = getSelectedPhase();
// Handle collaboration tools section // Handle collaboration tools section
const collaborationSection = document.getElementById('collaboration-tools-section'); const collaborationSection = document.getElementById('collaboration-tools-section');

View File

@ -415,6 +415,25 @@ input[type="checkbox"] {
color: white; color: white;
} }
.badge-mini {
display: inline-flex;
align-items: center;
padding: 0.0625rem 0.375rem;
border-radius: 9999px;
font-size: 0.625rem;
font-weight: 500;
}
.badge-mini.badge-primary {
background-color: var(--color-primary);
color: white;
}
.badge-mini.badge-success {
background-color: var(--color-accent);
color: white;
}
/* Tags */ /* Tags */
.tag { .tag {
display: inline-block; display: inline-block;
@ -455,34 +474,6 @@ footer {
.gap-2 { gap: 0.5rem; } .gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; } .gap-4 { gap: 1rem; }
/* Responsive */
@media (max-width: 768px) {
.grid-cols-2 { grid-template-columns: 1fr; }
.grid-cols-3 { grid-template-columns: 1fr; }
.grid-cols-4 { grid-template-columns: 1fr; }
.nav-links {
gap: 1rem;
}
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
.footer-content {
flex-direction: column;
text-align: center;
}
/* Add this inside the existing @media (max-width: 768px) block */
.collaboration-tools-compact {
flex-direction: column;
}
.collaboration-tool-compact {
min-width: auto;
max-width: none;
}
}
/* Animations */ /* Animations */
@keyframes fadeIn { @keyframes fadeIn {
from { opacity: 0; } from { opacity: 0; }
@ -534,21 +525,282 @@ footer {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.badge-mini { /* Filter Components - Domain and Phase Layout */
.domain-phase-container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 2rem;
align-items: start;
}
.domain-section {
min-width: 0;
}
.phase-section {
min-width: 0;
}
/* Tag Header Layout */
.tag-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.75rem;
}
/* Phase Buttons */
.phase-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.phase-button {
padding: 0.5rem 1rem;
border: 1px solid var(--color-border);
border-radius: 0.375rem;
background-color: var(--color-bg);
color: var(--color-text);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
user-select: none;
white-space: nowrap;
min-width: fit-content;
}
.phase-button:hover {
border-color: var(--color-primary);
background-color: var(--color-bg-secondary);
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.phase-button.active {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: white;
}
.phase-button.active:hover {
background-color: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
.phase-button:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Tag Cloud */
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
max-height: 120px;
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
position: relative;
}
.tag-cloud.expanded {
max-height: 1000px;
}
.tag-cloud.expanding {
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.tag-cloud.collapsing {
transition: max-height 0.3s ease-out;
}
.tag-cloud::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background: linear-gradient(
to bottom,
transparent 0%,
transparent 60%,
var(--color-bg) 100%
);
pointer-events: none;
opacity: 1;
transition: opacity 0.3s ease;
}
.tag-cloud.expanded::after {
opacity: 0;
}
.tag-cloud-item {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
padding: 0.0625rem 0.375rem; gap: 0.25rem;
border-radius: 9999px; padding: 0.375rem 0.75rem;
font-size: 0.625rem; border: 1px solid var(--color-border);
font-weight: 500; border-radius: 1rem;
background-color: var(--color-bg);
color: var(--color-text);
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s ease, transform 0.15s ease;
user-select: none;
opacity: 1;
transform: scale(1);
} }
.badge-mini.badge-primary { .tag-cloud-item.hidden {
background-color: var(--color-primary); display: none;
color: white;
} }
.badge-mini.badge-success { .tag-cloud-item:hover {
border-color: var(--color-primary);
background-color: var(--color-bg-secondary);
transform: scale(1.05);
}
.tag-cloud-item:active {
transform: scale(0.98);
}
.tag-cloud-item.active {
background-color: var(--color-accent); background-color: var(--color-accent);
border-color: var(--color-accent);
color: white; color: white;
} }
.tag-cloud-item.active:hover {
background-color: var(--color-accent-hover);
border-color: var(--color-accent-hover);
}
.tag-cloud-item.loading {
opacity: 0.6;
pointer-events: none;
}
.tag-cloud-item:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.tag-frequency {
font-size: 0.75rem;
opacity: 0.8;
}
.tag-cloud-item.active .tag-frequency {
opacity: 1;
}
/* Tag Toggle Button */
.btn-tag-toggle {
padding: 0.25rem 0.75rem;
border: 1px solid var(--color-border);
border-radius: 0.25rem;
background-color: var(--color-bg-secondary);
color: var(--color-text-secondary);
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-tag-toggle:hover {
background-color: var(--color-bg-tertiary);
color: var(--color-text);
transform: translateY(-1px);
}
.btn-tag-toggle:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Responsive Design */
@media (max-width: 768px) {
.grid-cols-2 { grid-template-columns: 1fr; }
.grid-cols-3 { grid-template-columns: 1fr; }
.grid-cols-4 { grid-template-columns: 1fr; }
.nav-links {
gap: 1rem;
}
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
.footer-content {
flex-direction: column;
text-align: center;
}
.collaboration-tools-compact {
flex-direction: column;
}
.collaboration-tool-compact {
min-width: auto;
max-width: none;
}
.domain-phase-container {
grid-template-columns: 1fr;
gap: 1rem;
}
.phase-buttons {
gap: 0.375rem;
}
.phase-button {
padding: 0.375rem 0.75rem;
font-size: 0.8125rem;
}
.tag-cloud {
max-height: 100px;
}
.tag-header {
gap: 0.75rem;
}
}
@media (max-width: 640px) {
.phase-buttons {
justify-content: center;
}
.phase-button {
flex: 1;
min-width: 0;
text-align: center;
}
.tag-cloud {
max-height: 80px;
}
.tag-cloud.expanded {
max-height: 800px;
}
}
@media (max-width: 480px) {
.phase-buttons {
flex-direction: column;
gap: 0.375rem;
}
.phase-button {
width: 100%;
}
}