sharing mechanic with eye-watering animation
This commit is contained in:
@@ -21,7 +21,6 @@ export interface Props {
|
||||
|
||||
const { tool } = Astro.props;
|
||||
|
||||
|
||||
// Check types
|
||||
const isMethod = tool.type === 'method';
|
||||
const isConcept = tool.type === 'concept';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
import { getToolsData } from '../utils/dataService.js';
|
||||
import ShareButton from './ShareButton.astro';
|
||||
|
||||
|
||||
|
||||
// Load tools data
|
||||
@@ -146,12 +148,17 @@ domains.forEach((domain: any) => {
|
||||
<div class="tool-details" id="tool-details-primary">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
||||
<h2 id="tool-name-primary" style="margin: 0;">Tool Name</h2>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('primary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<div id="share-button-primary" style="display: none;">
|
||||
<!-- Share button will be populated by JavaScript -->
|
||||
</div>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('primary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p id="tool-description-primary" class="text-muted"></p>
|
||||
@@ -169,12 +176,17 @@ domains.forEach((domain: any) => {
|
||||
<div class="tool-details" id="tool-details-secondary">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
||||
<h2 id="tool-name-secondary" style="margin: 0;">Tool Name</h2>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('secondary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<div id="share-button-secondary" style="display: none;">
|
||||
<!-- Share button will be populated by JavaScript -->
|
||||
</div>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('secondary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p id="tool-description-secondary" class="text-muted"></p>
|
||||
@@ -266,6 +278,201 @@ domains.forEach((domain: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== SHARING FUNCTIONALITY =====
|
||||
|
||||
// Create tool slug from name (same logic as ShareButton.astro)
|
||||
function createToolSlug(toolName) {
|
||||
return toolName.toLowerCase()
|
||||
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
.replace(/-+/g, '-') // Remove duplicate hyphens
|
||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||
}
|
||||
|
||||
// Find tool by name or slug
|
||||
function findTool(identifier) {
|
||||
return toolsData.find(tool =>
|
||||
tool.name === identifier ||
|
||||
createToolSlug(tool.name) === identifier.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
// Generate share URLs
|
||||
function generateShareURL(toolName, view, modal = null) {
|
||||
const toolSlug = createToolSlug(toolName);
|
||||
const baseUrl = window.location.origin + window.location.pathname;
|
||||
const params = new URLSearchParams();
|
||||
params.set('tool', toolSlug);
|
||||
params.set('view', view);
|
||||
if (modal) {
|
||||
params.set('modal', modal);
|
||||
}
|
||||
return `${baseUrl}?${params.toString()}`;
|
||||
}
|
||||
|
||||
// Copy to clipboard with feedback
|
||||
async function copyToClipboard(text, button) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
// Show feedback
|
||||
const originalHTML = button.innerHTML;
|
||||
button.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20,6 9,17 4,12"/></svg> Kopiert!';
|
||||
button.style.color = 'var(--color-accent)';
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
button.style.color = '';
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
// Fallback for older browsers
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
|
||||
// Show feedback
|
||||
const originalHTML = button.innerHTML;
|
||||
button.innerHTML = 'Kopiert!';
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Show share dialog
|
||||
window.showShareDialog = function(shareButton) {
|
||||
const toolName = shareButton.getAttribute('data-tool-name');
|
||||
const context = shareButton.getAttribute('data-context');
|
||||
|
||||
// Create modal backdrop
|
||||
let backdrop = document.getElementById('share-modal-backdrop');
|
||||
if (!backdrop) {
|
||||
backdrop = document.createElement('div');
|
||||
backdrop.id = 'share-modal-backdrop';
|
||||
backdrop.style.cssText = `
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5); z-index: 9999;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 0; transition: opacity 0.2s ease;
|
||||
`;
|
||||
document.body.appendChild(backdrop);
|
||||
}
|
||||
|
||||
// Create share dialog
|
||||
const dialog = document.createElement('div');
|
||||
dialog.style.cssText = `
|
||||
background: var(--color-bg); border: 1px solid var(--color-border);
|
||||
border-radius: 0.75rem; padding: 1.5rem; max-width: 400px; width: 90%;
|
||||
box-shadow: var(--shadow-lg); transform: scale(0.9); transition: transform 0.2s ease;
|
||||
`;
|
||||
|
||||
dialog.innerHTML = `
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem;">
|
||||
<h3 style="margin: 0; color: var(--color-primary);">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
||||
</svg>
|
||||
${toolName} teilen
|
||||
</h3>
|
||||
<button id="close-share-dialog" style="background: none; border: none; cursor: pointer; padding: 0.25rem;color: var(--color-text-secondary)">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
|
||||
<button class="share-option-btn" data-url="${generateShareURL(toolName, 'grid')}"
|
||||
style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; border: 1px solid var(--color-border); border-radius: 0.5rem; background: var(--color-bg); cursor: pointer; transition: var(--transition-fast); text-align: left; width: 100%;">
|
||||
<div style="width: 32px; height: 32px; background: var(--color-primary); border-radius: 0.25rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 500; margin-bottom: 0.25rem;color: var(--color-text-secondary)">Kachelansicht</div>
|
||||
<div style="font-size: 0.8125rem; color: var(--color-text-secondary);">Scrollt zur Karte in der Übersicht</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button class="share-option-btn" data-url="${generateShareURL(toolName, 'matrix')}"
|
||||
style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; border: 1px solid var(--color-border); border-radius: 0.5rem; background: var(--color-bg); cursor: pointer; transition: var(--transition-fast); text-align: left; width: 100%;">
|
||||
<div style="width: 32px; height: 32px; background: var(--color-accent); border-radius: 0.25rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||
<path d="M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 500; margin-bottom: 0.25rem;color: var(--color-text-secondary)">Matrix-Ansicht</div>
|
||||
<div style="font-size: 0.8125rem; color: var(--color-text-secondary);">Zeigt Tool-Position in der Matrix</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button class="share-option-btn" data-url="${generateShareURL(toolName, 'modal', 'primary')}"
|
||||
style="display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; border: 1px solid var(--color-border); border-radius: 0.5rem; background: var(--color-bg); cursor: pointer; transition: var(--transition-fast); text-align: left; width: 100%;">
|
||||
<div style="width: 32px; height: 32px; background: var(--color-warning); border-radius: 0.25rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 500; margin-bottom: 0.25rem;color: var(--color-text-secondary)">Tool-Details</div>
|
||||
<div style="font-size: 0.8125rem; color: var(--color-text-secondary);">Öffnet Detail-Fenster direkt</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
backdrop.appendChild(dialog);
|
||||
|
||||
// Show with animation
|
||||
requestAnimationFrame(() => {
|
||||
backdrop.style.opacity = '1';
|
||||
dialog.style.transform = 'scale(1)';
|
||||
});
|
||||
|
||||
// Event handlers
|
||||
const closeDialog = () => {
|
||||
backdrop.style.opacity = '0';
|
||||
dialog.style.transform = 'scale(0.9)';
|
||||
setTimeout(() => {
|
||||
if (backdrop.parentNode) {
|
||||
document.body.removeChild(backdrop);
|
||||
}
|
||||
}, 200);
|
||||
};
|
||||
|
||||
backdrop.addEventListener('click', (e) => {
|
||||
if (e.target === backdrop) closeDialog();
|
||||
});
|
||||
|
||||
document.getElementById('close-share-dialog').addEventListener('click', closeDialog);
|
||||
|
||||
// Share option handlers
|
||||
dialog.querySelectorAll('.share-option-btn').forEach(btn => {
|
||||
btn.addEventListener('mouseover', () => {
|
||||
btn.style.backgroundColor = 'var(--color-bg-secondary)';
|
||||
btn.style.borderColor = 'var(--color-primary)';
|
||||
});
|
||||
|
||||
btn.addEventListener('mouseout', () => {
|
||||
btn.style.backgroundColor = 'var(--color-bg)';
|
||||
btn.style.borderColor = 'var(--color-border)';
|
||||
});
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
const url = btn.getAttribute('data-url');
|
||||
copyToClipboard(url, btn);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Make functions globally available
|
||||
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection;
|
||||
|
||||
@@ -372,10 +579,23 @@ domains.forEach((domain: any) => {
|
||||
return `<span class="tag" style="background-color: var(--color-bg-tertiary); color: var(--color-text-secondary); margin: 0.125rem;">${conceptName}</span>`;
|
||||
}).join('');
|
||||
|
||||
// Check if mobile device
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
const collapseOnMobile = isMobile && relatedConcepts.length > 2;
|
||||
|
||||
tagsHTML += `
|
||||
<div style="margin-top: 1rem;">
|
||||
<strong style="display: block; margin-bottom: 0.5rem; color: var(--color-text);">Verwandte Konzepte:</strong>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||
<strong style="color: var(--color-text);">Verwandte Konzepte:</strong>
|
||||
${collapseOnMobile ? `
|
||||
<button id="concepts-toggle-${modalType}"
|
||||
onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'; this.textContent = this.textContent === '▼' ? '▲' : '▼';"
|
||||
style="background: none; border: 1px solid var(--color-border); border-radius: 0.25rem; padding: 0.25rem 0.5rem; cursor: pointer; font-size: 0.75rem;">
|
||||
▼
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
<div ${collapseOnMobile ? 'style="display: none;"' : ''} style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
${conceptLinks}
|
||||
</div>
|
||||
</div>
|
||||
@@ -436,6 +656,30 @@ domains.forEach((domain: any) => {
|
||||
|
||||
elements.links.innerHTML = linksHTML;
|
||||
|
||||
// ===== POPULATE SHARE BUTTON =====
|
||||
const shareButtonContainer = document.getElementById(`share-button-${modalType}`);
|
||||
if (shareButtonContainer) {
|
||||
const toolSlug = createToolSlug(tool.name);
|
||||
shareButtonContainer.innerHTML = `
|
||||
<button class="share-btn share-btn--medium"
|
||||
data-tool-name="${tool.name}"
|
||||
data-tool-slug="${toolSlug}"
|
||||
data-context="modal-${modalType}"
|
||||
onclick="event.stopPropagation(); window.showShareDialog(this)"
|
||||
title="${tool.name} teilen"
|
||||
aria-label="${tool.name} teilen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="18" cy="5" r="3"/>
|
||||
<circle cx="6" cy="12" r="3"/>
|
||||
<circle cx="18" cy="19" r="3"/>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
|
||||
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
||||
</svg>
|
||||
</button>
|
||||
`;
|
||||
shareButtonContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show modals and update layout
|
||||
const overlay = document.getElementById('modal-overlay');
|
||||
const primaryModal = document.getElementById('tool-details-primary');
|
||||
@@ -460,14 +704,28 @@ domains.forEach((domain: any) => {
|
||||
const secondaryModal = document.getElementById('tool-details-secondary');
|
||||
|
||||
if (modalType === 'both' || modalType === 'all') {
|
||||
if (primaryModal) primaryModal.classList.remove('active');
|
||||
if (secondaryModal) secondaryModal.classList.remove('active');
|
||||
if (primaryModal) {
|
||||
primaryModal.classList.remove('active');
|
||||
// Hide share button
|
||||
const shareButtonPrimary = document.getElementById('share-button-primary');
|
||||
if (shareButtonPrimary) shareButtonPrimary.style.display = 'none';
|
||||
}
|
||||
if (secondaryModal) {
|
||||
secondaryModal.classList.remove('active');
|
||||
// Hide share button
|
||||
const shareButtonSecondary = document.getElementById('share-button-secondary');
|
||||
if (shareButtonSecondary) shareButtonSecondary.style.display = 'none';
|
||||
}
|
||||
if (overlay) overlay.classList.remove('active');
|
||||
document.body.classList.remove('modals-side-by-side');
|
||||
} else if (modalType === 'primary' && primaryModal) {
|
||||
primaryModal.classList.remove('active');
|
||||
const shareButtonPrimary = document.getElementById('share-button-primary');
|
||||
if (shareButtonPrimary) shareButtonPrimary.style.display = 'none';
|
||||
} else if (modalType === 'secondary' && secondaryModal) {
|
||||
secondaryModal.classList.remove('active');
|
||||
const shareButtonSecondary = document.getElementById('share-button-secondary');
|
||||
if (shareButtonSecondary) shareButtonSecondary.style.display = 'none';
|
||||
}
|
||||
|
||||
// Check if any modal is still active
|
||||
|
||||
Reference in New Issue
Block a user