UI overhaul
This commit is contained in:
parent
0d22210040
commit
0adabad94d
@ -444,39 +444,46 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Smart Prompting Input Handling
|
// Smart Prompting Input Handling - Fixed Race Conditions
|
||||||
aiInput.addEventListener('input', () => {
|
aiInput.addEventListener('input', () => {
|
||||||
console.log('[DEBUG] Input event triggered, length:', aiInput.value.trim().length);
|
console.log('[DEBUG] Input event triggered, length:', aiInput.value.trim().length);
|
||||||
const inputLength = aiInput.value.trim().length;
|
const inputLength = aiInput.value.trim().length;
|
||||||
|
|
||||||
// Clear existing timeout
|
// Clear ALL existing timeouts and abort controllers
|
||||||
clearTimeout(enhancementTimeout);
|
clearTimeout(enhancementTimeout);
|
||||||
|
|
||||||
// Cancel any pending enhancement call
|
|
||||||
if (enhancementAbortController) {
|
if (enhancementAbortController) {
|
||||||
enhancementAbortController.abort();
|
enhancementAbortController.abort();
|
||||||
|
enhancementAbortController = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide suggestions if input is too short
|
// Hide suggestions immediately if input is too short
|
||||||
if (inputLength < 40) {
|
if (inputLength < 40) {
|
||||||
showPromptingStatus('hidden');
|
showPromptingStatus('hidden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show analyzing state after 1 second
|
// Single consolidated timeout for all smart prompting logic
|
||||||
setTimeout(() => {
|
|
||||||
if (aiInput.value.trim().length >= 50) {
|
|
||||||
showPromptingStatus('analyzing');
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// Trigger AI enhancement after 1.5 seconds
|
|
||||||
enhancementTimeout = setTimeout(() => {
|
enhancementTimeout = setTimeout(() => {
|
||||||
console.log('[DEBUG] Enhancement timeout fired, calling triggerSmartPrompting');
|
const currentLength = aiInput.value.trim().length;
|
||||||
if (aiInput.value.trim().length >= 40) {
|
|
||||||
triggerSmartPrompting();
|
// Double-check length hasn't changed during timeout
|
||||||
|
if (currentLength < 40) {
|
||||||
|
showPromptingStatus('hidden');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, 1500);
|
|
||||||
|
// Show analyzing state first
|
||||||
|
if (currentLength >= 50) {
|
||||||
|
showPromptingStatus('analyzing');
|
||||||
|
|
||||||
|
// Trigger enhancement after showing analyzing state
|
||||||
|
setTimeout(() => {
|
||||||
|
if (aiInput.value.trim().length >= 50) {
|
||||||
|
triggerSmartPrompting();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 1000); // Single timeout instead of multiple
|
||||||
});
|
});
|
||||||
|
|
||||||
aiInput.addEventListener('input', updateCharacterCount);
|
aiInput.addEventListener('input', updateCharacterCount);
|
||||||
|
@ -68,40 +68,59 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
|
|||||||
<script define:vars={{ allScenarios: scenarios, maxDisplay: maxDisplayed }}>
|
<script define:vars={{ allScenarios: scenarios, maxDisplay: maxDisplayed }}>
|
||||||
let showingAllScenarios = false;
|
let showingAllScenarios = false;
|
||||||
|
|
||||||
// Apply scenario search using existing search functionality
|
|
||||||
window.applyScenarioSearch = function(scenarioId) {
|
window.applyScenarioSearch = function(scenarioId) {
|
||||||
console.log(`Applying scenario search: ${scenarioId}`);
|
console.log(`Applying scenario search: ${scenarioId}`);
|
||||||
|
|
||||||
// Find the main search input (existing)
|
const clickedChip = document.querySelector(`[data-scenario-id="${scenarioId}"]`);
|
||||||
const mainSearchInput = document.getElementById('search-input');
|
const mainSearchInput = document.getElementById('search-input');
|
||||||
if (mainSearchInput) {
|
|
||||||
// Use scenario ID as search term (it should match tool tags)
|
if (!mainSearchInput) return;
|
||||||
mainSearchInput.value = scenarioId;
|
|
||||||
|
// Check if this scenario is already active (allow deselection)
|
||||||
|
if (clickedChip && clickedChip.classList.contains('active')) {
|
||||||
|
// Deselect: clear search and remove active state
|
||||||
|
mainSearchInput.value = '';
|
||||||
|
document.querySelectorAll('.suggestion-chip').forEach(chip => {
|
||||||
|
chip.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
// Trigger existing search functionality
|
// Clear the targeted search input too
|
||||||
|
const targetedInput = document.getElementById('targeted-search-input');
|
||||||
|
if (targetedInput) {
|
||||||
|
targetedInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger search to show all results
|
||||||
const inputEvent = new Event('input', { bubbles: true });
|
const inputEvent = new Event('input', { bubbles: true });
|
||||||
mainSearchInput.dispatchEvent(inputEvent);
|
mainSearchInput.dispatchEvent(inputEvent);
|
||||||
|
|
||||||
// Switch to grid view
|
return;
|
||||||
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
}
|
||||||
if (gridToggle && !gridToggle.classList.contains('active')) {
|
|
||||||
gridToggle.click();
|
// Apply new search
|
||||||
}
|
mainSearchInput.value = scenarioId;
|
||||||
|
|
||||||
// Scroll to results
|
// Trigger existing search functionality
|
||||||
setTimeout(() => {
|
const inputEvent = new Event('input', { bubbles: true });
|
||||||
const toolsGrid = document.getElementById('tools-grid');
|
mainSearchInput.dispatchEvent(inputEvent);
|
||||||
if (toolsGrid) {
|
|
||||||
toolsGrid.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
// Switch to grid view
|
||||||
}
|
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
||||||
}, 200);
|
if (gridToggle && !gridToggle.classList.contains('active')) {
|
||||||
|
gridToggle.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visual feedback
|
// Visual feedback
|
||||||
document.querySelectorAll('.suggestion-chip').forEach(chip => {
|
document.querySelectorAll('.suggestion-chip').forEach(chip => {
|
||||||
chip.classList.remove('active');
|
chip.classList.remove('active');
|
||||||
});
|
});
|
||||||
document.querySelector(`[data-scenario-id="${scenarioId}"]`)?.classList.add('active');
|
if (clickedChip) {
|
||||||
|
clickedChip.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll to results with better positioning
|
||||||
|
window.scrollToElementById('tools-grid');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Toggle showing all scenarios
|
// Toggle showing all scenarios
|
||||||
@ -149,18 +168,14 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
|
|||||||
|
|
||||||
targetedInput.addEventListener('keydown', (e) => {
|
targetedInput.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Switch to grid view and scroll to results
|
// Switch to grid view and scroll to results
|
||||||
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
||||||
if (gridToggle) {
|
if (gridToggle) {
|
||||||
gridToggle.click();
|
gridToggle.click();
|
||||||
setTimeout(() => {
|
// Use consolidated scroll utility
|
||||||
const toolsGrid = document.getElementById('tools-grid');
|
window.scrollToElementById('tools-grid');
|
||||||
if (toolsGrid) {
|
}
|
||||||
toolsGrid.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,15 +117,24 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
window.toolsData = toolsData;
|
window.toolsData = toolsData;
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const searchInput = document.getElementById('search-input');
|
// Cache DOM elements once
|
||||||
const domainSelect = document.getElementById('domain-select');
|
const elements = {
|
||||||
const phaseButtons = document.querySelectorAll('.phase-button');
|
searchInput: document.getElementById('search-input'),
|
||||||
const proprietaryCheckbox = document.getElementById('include-proprietary');
|
domainSelect: document.getElementById('domain-select'),
|
||||||
const tagCloudItems = document.querySelectorAll('.tag-cloud-item');
|
phaseButtons: document.querySelectorAll('.phase-button'),
|
||||||
const tagCloud = document.getElementById('tag-cloud');
|
proprietaryCheckbox: document.getElementById('include-proprietary'),
|
||||||
const tagCloudToggle = document.getElementById('tag-cloud-toggle');
|
tagCloudItems: document.querySelectorAll('.tag-cloud-item'),
|
||||||
const viewToggles = document.querySelectorAll('.view-toggle');
|
tagCloud: document.getElementById('tag-cloud'),
|
||||||
const aiViewToggle = document.getElementById('ai-view-toggle');
|
tagCloudToggle: document.getElementById('tag-cloud-toggle'),
|
||||||
|
viewToggles: document.querySelectorAll('.view-toggle'),
|
||||||
|
aiViewToggle: document.getElementById('ai-view-toggle')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify critical elements exist
|
||||||
|
if (!elements.searchInput || !elements.domainSelect || !elements.proprietaryCheckbox) {
|
||||||
|
console.error('Critical filter elements not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let selectedTags = new Set();
|
let selectedTags = new Set();
|
||||||
let selectedPhase = '';
|
let selectedPhase = '';
|
||||||
@ -133,7 +142,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
|
|
||||||
function initTagCloud() {
|
function initTagCloud() {
|
||||||
const visibleCount = 22;
|
const visibleCount = 22;
|
||||||
tagCloudItems.forEach((item, index) => {
|
elements.tagCloudItems.forEach((item, index) => {
|
||||||
if (index >= visibleCount) {
|
if (index >= visibleCount) {
|
||||||
item.style.display = 'none';
|
item.style.display = 'none';
|
||||||
}
|
}
|
||||||
@ -145,22 +154,22 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
const visibleCount = 22;
|
const visibleCount = 22;
|
||||||
|
|
||||||
if (isTagCloudExpanded) {
|
if (isTagCloudExpanded) {
|
||||||
tagCloud.classList.add('expanded');
|
elements.tagCloud.classList.add('expanded');
|
||||||
tagCloudToggle.textContent = 'Weniger zeigen';
|
elements.tagCloudToggle.textContent = 'Weniger zeigen';
|
||||||
tagCloudToggle.setAttribute('data-expanded', 'true');
|
elements.tagCloudToggle.setAttribute('data-expanded', 'true');
|
||||||
|
|
||||||
tagCloudItems.forEach(item => {
|
elements.tagCloudItems.forEach(item => {
|
||||||
if (!item.classList.contains('hidden')) {
|
if (!item.classList.contains('hidden')) {
|
||||||
item.style.display = 'inline-flex';
|
item.style.display = 'inline-flex';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tagCloud.classList.remove('expanded');
|
elements.tagCloud.classList.remove('expanded');
|
||||||
tagCloudToggle.textContent = 'Mehr zeigen';
|
elements.tagCloudToggle.textContent = 'Mehr zeigen';
|
||||||
tagCloudToggle.setAttribute('data-expanded', 'false');
|
elements.tagCloudToggle.setAttribute('data-expanded', 'false');
|
||||||
|
|
||||||
let visibleIndex = 0;
|
let visibleIndex = 0;
|
||||||
tagCloudItems.forEach(item => {
|
elements.tagCloudItems.forEach(item => {
|
||||||
if (!item.classList.contains('hidden')) {
|
if (!item.classList.contains('hidden')) {
|
||||||
if (visibleIndex < visibleCount) {
|
if (visibleIndex < visibleCount) {
|
||||||
item.style.display = 'inline-flex';
|
item.style.display = 'inline-flex';
|
||||||
@ -174,11 +183,11 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function filterTagCloud() {
|
function filterTagCloud() {
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = elements.searchInput.value.toLowerCase();
|
||||||
let visibleCount = 0;
|
let visibleCount = 0;
|
||||||
const maxVisibleWhenCollapsed = 22;
|
const maxVisibleWhenCollapsed = 22;
|
||||||
|
|
||||||
tagCloudItems.forEach(item => {
|
elements.tagCloudItems.forEach(item => {
|
||||||
const tagName = item.getAttribute('data-tag').toLowerCase();
|
const tagName = item.getAttribute('data-tag').toLowerCase();
|
||||||
const shouldShow = tagName.includes(searchTerm);
|
const shouldShow = tagName.includes(searchTerm);
|
||||||
|
|
||||||
@ -196,10 +205,10 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasHiddenTags = Array.from(tagCloudItems).some(item =>
|
const hasHiddenTags = Array.from(elements.tagCloudItems).some(item =>
|
||||||
!item.classList.contains('hidden') && item.style.display === 'none'
|
!item.classList.contains('hidden') && item.style.display === 'none'
|
||||||
);
|
);
|
||||||
tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
|
elements.tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isToolHosted(tool) {
|
function isToolHosted(tool) {
|
||||||
@ -224,7 +233,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
el.classList.remove('highlight-row', 'highlight-column');
|
el.classList.remove('highlight-row', 'highlight-column');
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedDomain = domainSelect.value;
|
const selectedDomain = elements.domainSelect.value;
|
||||||
|
|
||||||
if (selectedDomain) {
|
if (selectedDomain) {
|
||||||
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
|
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
|
||||||
@ -252,9 +261,9 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function filterTools() {
|
function filterTools() {
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = elements.searchInput.value.toLowerCase();
|
||||||
const selectedDomain = domainSelect.value;
|
const selectedDomain = elements.domainSelect.value;
|
||||||
const includeProprietary = proprietaryCheckbox.checked;
|
const includeProprietary = elements.proprietaryCheckbox.checked;
|
||||||
|
|
||||||
const filtered = window.toolsData.filter(tool => {
|
const filtered = window.toolsData.filter(tool => {
|
||||||
const domains = tool.domains || [];
|
const domains = tool.domains || [];
|
||||||
@ -314,7 +323,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
selectedPhase = '';
|
selectedPhase = '';
|
||||||
button.classList.remove('active');
|
button.classList.remove('active');
|
||||||
} else {
|
} else {
|
||||||
phaseButtons.forEach(btn => btn.classList.remove('active'));
|
elements.phaseButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
selectedPhase = phase;
|
selectedPhase = phase;
|
||||||
button.classList.add('active');
|
button.classList.add('active');
|
||||||
}
|
}
|
||||||
@ -323,7 +332,7 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleViewToggle(view) {
|
function handleViewToggle(view) {
|
||||||
viewToggles.forEach(btn => {
|
elements.viewToggles.forEach(btn => {
|
||||||
btn.classList.toggle('active', btn.getAttribute('data-view') === view);
|
btn.classList.toggle('active', btn.getAttribute('data-view') === view);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -339,37 +348,38 @@ const sortedTags = Object.entries(tagFrequency)
|
|||||||
|
|
||||||
function clearTagFilters() {
|
function clearTagFilters() {
|
||||||
selectedTags.clear();
|
selectedTags.clear();
|
||||||
tagCloudItems.forEach(item => item.classList.remove('active'));
|
elements.tagCloudItems.forEach(item => item.classList.remove('active'));
|
||||||
filterTools();
|
filterTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAllFilters() {
|
function clearAllFilters() {
|
||||||
searchInput.value = '';
|
elements.searchInput.value = '';
|
||||||
domainSelect.value = '';
|
elements.domainSelect.value = '';
|
||||||
selectedPhase = '';
|
selectedPhase = '';
|
||||||
phaseButtons.forEach(btn => btn.classList.remove('active'));
|
elements.phaseButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
clearTagFilters();
|
clearTagFilters();
|
||||||
filterTagCloud();
|
filterTagCloud();
|
||||||
}
|
}
|
||||||
|
|
||||||
searchInput.addEventListener('input', () => {
|
// Event listeners using cached elements
|
||||||
|
elements.searchInput.addEventListener('input', () => {
|
||||||
filterTagCloud();
|
filterTagCloud();
|
||||||
filterTools();
|
filterTools();
|
||||||
});
|
});
|
||||||
|
|
||||||
domainSelect.addEventListener('change', filterTools);
|
elements.domainSelect.addEventListener('change', filterTools);
|
||||||
proprietaryCheckbox.addEventListener('change', filterTools);
|
elements.proprietaryCheckbox.addEventListener('change', filterTools);
|
||||||
tagCloudToggle.addEventListener('click', toggleTagCloud);
|
elements.tagCloudToggle.addEventListener('click', toggleTagCloud);
|
||||||
|
|
||||||
tagCloudItems.forEach(item => {
|
elements.tagCloudItems.forEach(item => {
|
||||||
item.addEventListener('click', () => handleTagClick(item));
|
item.addEventListener('click', () => handleTagClick(item));
|
||||||
});
|
});
|
||||||
|
|
||||||
phaseButtons.forEach(btn => {
|
elements.phaseButtons.forEach(btn => {
|
||||||
btn.addEventListener('click', () => handlePhaseClick(btn));
|
btn.addEventListener('click', () => handlePhaseClick(btn));
|
||||||
});
|
});
|
||||||
|
|
||||||
viewToggles.forEach(btn => {
|
elements.viewToggles.forEach(btn => {
|
||||||
btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view')));
|
btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -672,6 +672,13 @@ domains.forEach((domain: any) => {
|
|||||||
const primaryModal = document.getElementById('tool-details-primary');
|
const primaryModal = document.getElementById('tool-details-primary');
|
||||||
const secondaryModal = document.getElementById('tool-details-secondary');
|
const secondaryModal = document.getElementById('tool-details-secondary');
|
||||||
|
|
||||||
|
// Debounce rapid calls
|
||||||
|
if (window.modalHideInProgress) return;
|
||||||
|
window.modalHideInProgress = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.modalHideInProgress = false;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
if (modalType === 'both' || modalType === 'all') {
|
if (modalType === 'both' || modalType === 'all') {
|
||||||
if (primaryModal) {
|
if (primaryModal) {
|
||||||
@ -702,13 +709,19 @@ domains.forEach((domain: any) => {
|
|||||||
if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
|
if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Consolidated state checking with safety checks
|
||||||
const primaryActive = primaryModal && primaryModal.classList.contains('active');
|
const primaryActive = primaryModal && primaryModal.classList.contains('active');
|
||||||
const secondaryActive = secondaryModal && secondaryModal.classList.contains('active');
|
const secondaryActive = secondaryModal && secondaryModal.classList.contains('active');
|
||||||
|
|
||||||
|
// Update overlay and body classes atomically
|
||||||
if (!primaryActive && !secondaryActive) {
|
if (!primaryActive && !secondaryActive) {
|
||||||
if (overlay) overlay.classList.remove('active');
|
if (overlay) overlay.classList.remove('active');
|
||||||
document.body.classList.remove('modals-side-by-side');
|
document.body.classList.remove('modals-side-by-side');
|
||||||
} else if (primaryActive !== secondaryActive) {
|
} else if (primaryActive && secondaryActive) {
|
||||||
|
// Both active - ensure side-by-side class
|
||||||
|
document.body.classList.add('modals-side-by-side');
|
||||||
|
} else {
|
||||||
|
// Only one active - remove side-by-side class
|
||||||
document.body.classList.remove('modals-side-by-side');
|
document.body.classList.remove('modals-side-by-side');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
21
src/env.d.ts
vendored
21
src/env.d.ts
vendored
@ -22,10 +22,25 @@ declare global {
|
|||||||
findToolByIdentifier: (tools: any[], identifier: string) => any | undefined;
|
findToolByIdentifier: (tools: any[], identifier: string) => any | undefined;
|
||||||
isToolHosted: (tool: any) => boolean;
|
isToolHosted: (tool: any) => boolean;
|
||||||
|
|
||||||
checkClientAuth: () => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;
|
checkClientAuth: (context?: string) => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;
|
||||||
requireClientAuth: (callback?: () => void, returnUrl?: string) => Promise<boolean>;
|
requireClientAuth: (callback?: () => void, returnUrl?: string, context?: string) => Promise<boolean>;
|
||||||
showIfAuthenticated: (selector: string) => Promise<void>;
|
showIfAuthenticated: (selector: string, context?: string) => Promise<void>;
|
||||||
setupAuthButtons: (selector?: string) => void;
|
setupAuthButtons: (selector?: string) => void;
|
||||||
|
|
||||||
|
// Consolidated scroll utilities
|
||||||
|
scrollToElement: (element: Element | null, options?: ScrollIntoViewOptions) => void;
|
||||||
|
scrollToElementById: (elementId: string, options?: ScrollIntoViewOptions) => void;
|
||||||
|
scrollToElementBySelector: (selector: string, options?: ScrollIntoViewOptions) => void;
|
||||||
|
|
||||||
|
// Additional global functions that might be called
|
||||||
|
applyScenarioSearch?: (scenarioId: string) => void;
|
||||||
|
selectPhase?: (phase: string) => void;
|
||||||
|
selectApproach?: (approach: string) => void;
|
||||||
|
navigateToGrid?: (toolName: string) => void;
|
||||||
|
navigateToMatrix?: (toolName: string) => void;
|
||||||
|
toggleAllScenarios?: () => void;
|
||||||
|
showShareDialog?: (shareButton: Element) => void;
|
||||||
|
modalHideInProgress?: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,35 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
|
|||||||
getStoredTheme
|
getStoredTheme
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Consolidated scrolling utility
|
||||||
|
(window as any).scrollToElement = function(element, options = {}) {
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
// Calculate target position manually to avoid double-scroll
|
||||||
|
setTimeout(() => {
|
||||||
|
const headerHeight = document.querySelector('nav')?.offsetHeight || 80;
|
||||||
|
const elementRect = element.getBoundingClientRect();
|
||||||
|
const absoluteElementTop = elementRect.top + window.pageYOffset;
|
||||||
|
const targetPosition = absoluteElementTop - headerHeight - 20; // Adjust this 20 as needed
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top: targetPosition,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convenience functions for common scroll targets
|
||||||
|
(window as any).scrollToElementById = function(elementId, options = {}) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
(window as any).scrollToElement(element, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
(window as any).scrollToElementBySelector = function(selector, options = {}) {
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
(window as any).scrollToElement(element, options);
|
||||||
|
};
|
||||||
|
|
||||||
function createToolSlug(toolName) {
|
function createToolSlug(toolName) {
|
||||||
if (!toolName || typeof toolName !== 'string') {
|
if (!toolName || typeof toolName !== 'string') {
|
||||||
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
|
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
|
||||||
|
@ -192,14 +192,14 @@ const phases = data.phases;
|
|||||||
const methodologySection = document.getElementById('methodology-section');
|
const methodologySection = document.getElementById('methodology-section');
|
||||||
if (methodologySection) {
|
if (methodologySection) {
|
||||||
methodologySection.classList.add('active');
|
methodologySection.classList.add('active');
|
||||||
methodologySection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
window.scrollToElementById('methodology-section');
|
||||||
}
|
}
|
||||||
} else if (approach === 'targeted') {
|
} else if (approach === 'targeted') {
|
||||||
// Show targeted scenarios section
|
// Show targeted scenarios section
|
||||||
const targetedSection = document.getElementById('targeted-section');
|
const targetedSection = document.getElementById('targeted-section');
|
||||||
if (targetedSection) {
|
if (targetedSection) {
|
||||||
targetedSection.classList.add('active');
|
targetedSection.classList.add('active');
|
||||||
targetedSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
window.scrollToElementById('targeted-section');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -213,13 +213,13 @@ const phases = data.phases;
|
|||||||
card.classList.remove('active');
|
card.classList.remove('active');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add active class to selected phase card (use actual phase ID)
|
// Add active class to selected phase card
|
||||||
const selectedCard = document.querySelector(`.phase-card.phase-${phase}`);
|
const selectedCard = document.querySelector(`.phase-card.phase-${phase}`);
|
||||||
if (selectedCard) {
|
if (selectedCard) {
|
||||||
selectedCard.classList.add('active');
|
selectedCard.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use existing phase filter functionality with correct phase ID
|
// Use existing phase filter functionality
|
||||||
const existingPhaseButton = document.querySelector(`[data-phase="${phase}"]`);
|
const existingPhaseButton = document.querySelector(`[data-phase="${phase}"]`);
|
||||||
if (existingPhaseButton && !existingPhaseButton.classList.contains('active')) {
|
if (existingPhaseButton && !existingPhaseButton.classList.contains('active')) {
|
||||||
existingPhaseButton.click();
|
existingPhaseButton.click();
|
||||||
@ -231,13 +231,8 @@ const phases = data.phases;
|
|||||||
gridToggle.click();
|
gridToggle.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to results after a short delay
|
// Scroll to results using consolidated utility
|
||||||
setTimeout(() => {
|
window.scrollToElementById('tools-grid');
|
||||||
const toolsGrid = document.getElementById('tools-grid');
|
|
||||||
if (toolsGrid) {
|
|
||||||
toolsGrid.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
@ -367,7 +362,7 @@ const phases = data.phases;
|
|||||||
|
|
||||||
if (targetCard) {
|
if (targetCard) {
|
||||||
console.log('Found target card, scrolling...');
|
console.log('Found target card, scrolling...');
|
||||||
targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
window.scrollToElement(targetCard, { block: 'center' });
|
||||||
targetCard.style.animation = 'highlight-flash 2s ease-out';
|
targetCard.style.animation = 'highlight-flash 2s ease-out';
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -377,6 +372,8 @@ const phases = data.phases;
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
console.warn('Tool card not found in grid:', toolName);
|
console.warn('Tool card not found in grid:', toolName);
|
||||||
|
// Fallback to tools grid
|
||||||
|
window.scrollToElementById('tools-grid');
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 200);
|
}, 200);
|
||||||
@ -410,9 +407,11 @@ const phases = data.phases;
|
|||||||
|
|
||||||
if (firstMatch) {
|
if (firstMatch) {
|
||||||
console.log(`Found ${matchCount} occurrences of tool, highlighting all and scrolling to first`);
|
console.log(`Found ${matchCount} occurrences of tool, highlighting all and scrolling to first`);
|
||||||
firstMatch.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
window.scrollToElement(firstMatch, { block: 'center' });
|
||||||
} else {
|
} else {
|
||||||
console.warn('Tool chip not found in matrix:', toolName);
|
console.warn('Tool chip not found in matrix:', toolName);
|
||||||
|
// Fallback to matrix container
|
||||||
|
window.scrollToElementById('matrix-container');
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user