clone of repo on github
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

172 lines
4.5 KiB

<!--
NodeTooltip Component
Displays detailed information about a node when hovering or clicking on it
in the event network visualization.
-->
<script lang="ts">
import type { NetworkNode } from "./types";
import { onMount } from "svelte";
// Component props
let { node, selected = false, x, y, onclose } = $props<{
node: NetworkNode; // The node to display information for
selected?: boolean; // Whether the node is selected (clicked)
x: number; // X position for the tooltip
y: number; // Y position for the tooltip
onclose: () => void; // Function to call when closing the tooltip
}>();
// DOM reference and positioning
let tooltipElement: HTMLDivElement;
let tooltipX = $state(x + 10); // Add offset to avoid cursor overlap
let tooltipY = $state(y - 10);
// Maximum content length to display
const MAX_CONTENT_LENGTH = 200;
/**
* Gets the author name from the event tags
*/
function getAuthorTag(node: NetworkNode): string {
if (node.event) {
const authorTags = node.event.getMatchingTags("author");
if (authorTags.length > 0) {
return authorTags[0][1];
}
}
return "Unknown";
}
/**
* Gets the summary from the event tags
*/
function getSummaryTag(node: NetworkNode): string | null {
if (node.event) {
const summaryTags = node.event.getMatchingTags("summary");
if (summaryTags.length > 0) {
return summaryTags[0][1];
}
}
return null;
}
/**
* Gets the d-tag from the event
*/
function getDTag(node: NetworkNode): string {
if (node.event) {
const dTags = node.event.getMatchingTags("d");
if (dTags.length > 0) {
return dTags[0][1];
}
}
return "View Publication";
}
/**
* Truncates content to a maximum length
*/
function truncateContent(content: string, maxLength: number = MAX_CONTENT_LENGTH): string {
if (!content) return "";
if (content.length <= maxLength) return content;
return content.substring(0, maxLength) + "...";
}
/**
* Closes the tooltip
*/
function closeTooltip() {
onclose();
}
/**
* Ensures tooltip is fully visible on screen
*/
onMount(() => {
if (tooltipElement) {
const rect = tooltipElement.getBoundingClientRect();
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const padding = 10; // Padding from window edges
// Adjust position if tooltip goes off screen
if (rect.right > windowWidth) {
tooltipX = windowWidth - rect.width - padding;
}
if (rect.bottom > windowHeight) {
tooltipY = windowHeight - rect.height - padding;
}
if (rect.left < 0) {
tooltipX = padding;
}
if (rect.top < 0) {
tooltipY = padding;
}
}
});
</script>
<div
bind:this={tooltipElement}
class="tooltip-leather"
style="left: {tooltipX}px; top: {tooltipY}px;"
>
<!-- Close button -->
<button
class="tooltip-close-btn"
onclick={closeTooltip}
aria-label="Close"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<!-- Tooltip content -->
<div class="tooltip-content">
<!-- Title with link -->
<div class="tooltip-title">
<a
href="/publication?id={node.id}"
class="tooltip-title-link"
>
{node.title || "Untitled"}
</a>
</div>
<!-- Node type and kind -->
<div class="tooltip-metadata">
{node.type} (kind: {node.kind})
</div>
<!-- Author -->
<div class="tooltip-metadata">
Author: {getAuthorTag(node)}
</div>
<!-- Summary (for index nodes) -->
{#if node.isContainer && getSummaryTag(node)}
<div class="tooltip-summary">
<span class="font-semibold">Summary:</span> {truncateContent(getSummaryTag(node) || "")}
</div>
{/if}
<!-- Content preview -->
{#if node.content}
<div class="tooltip-content-preview">
{truncateContent(node.content)}
</div>
{/if}
<!-- Help text for selected nodes -->
{#if selected}
<div class="tooltip-help-text">
Click node again to dismiss
</div>
{/if}
</div>
</div>