From c019d96c89a75cc5303936c4d601bd56caadc497 Mon Sep 17 00:00:00 2001
From: Dennis Collaris <d.a.c.collaris@uu.nl>
Date: Fri, 30 Aug 2024 08:53:43 +0000
Subject: [PATCH] feat: schema panel tooltips, redesigned tooltip, minimap
 configuration option

---
 libs/config/styling/variables.css             |   5 +
 .../lib/assets/carbonIcons/carbonIcons.tsx    |  56 -------
 .../CardToolTipVis/VisualizationTooltip.tsx   | 140 ------------------
 .../CardToolTipVis/cardtooltipvis.module.scss |   6 -
 .../CardToolTipVis/cardtooltipvis.stories.tsx |  85 -----------
 .../lib/components/CardToolTipVis/index.tsx   |   1 -
 .../components/DesignGuides/styleGuide.mdx    |  11 ++
 .../VisualizationTooltip.module.scss          |   6 -
 .../VisualizationTooltip.stories.tsx          |  85 +++++++----
 .../VisualizationTooltip.tsx                  | 134 ++---------------
 libs/shared/lib/components/pills/Pill.tsx     |   6 +-
 .../lib/components/pills/PillContext.tsx      |   2 +-
 .../shared/lib/components/tooltip/Tooltip.tsx |   2 +-
 .../lib/data-access/store/schemaSlice.ts      |   2 +
 libs/shared/lib/schema/model/reactflow.tsx    |   3 +
 libs/shared/lib/schema/panel/Schema.tsx       | 102 +++++++++----
 .../lib/schema/panel/SchemaSettings.tsx       |   8 +
 .../lib/schema/panel/schema.stories.tsx       |  21 +++
 .../pills/nodes/SchemaPopUp/SchemaPopUp.tsx   |  89 +++++++++++
 .../pills/nodes/entity/SchemaEntityPill.tsx   |  84 ++++++-----
 .../nodes/relation/SchemaRelationPill.tsx     | 102 +++++++------
 .../lib/vis/components/VisualizationPanel.tsx |   4 +-
 .../vis/components/VisualizationTabBar.tsx    |   2 +-
 .../nodelinkvis/components/NLPixi.tsx         |  67 ++++-----
 .../nodelinkvis/components/NLPopup.tsx        |  71 +++++----
 25 files changed, 460 insertions(+), 634 deletions(-)
 delete mode 100644 libs/shared/lib/assets/carbonIcons/carbonIcons.tsx
 delete mode 100644 libs/shared/lib/components/CardToolTipVis/VisualizationTooltip.tsx
 delete mode 100644 libs/shared/lib/components/CardToolTipVis/cardtooltipvis.module.scss
 delete mode 100644 libs/shared/lib/components/CardToolTipVis/cardtooltipvis.stories.tsx
 delete mode 100644 libs/shared/lib/components/CardToolTipVis/index.tsx
 delete mode 100644 libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.module.scss
 create mode 100644 libs/shared/lib/schema/pills/nodes/SchemaPopUp/SchemaPopUp.tsx

diff --git a/libs/config/styling/variables.css b/libs/config/styling/variables.css
index 47bab4049..09ef3956b 100644
--- a/libs/config/styling/variables.css
+++ b/libs/config/styling/variables.css
@@ -97,6 +97,11 @@
     --clr-cat-12: var(--clr-neutral-50);
     --clr-cat-13: var(--clr-neutral-50);
     --clr-cat-14: var(--clr-neutral-50);
+
+    /* Colors pills */
+    --clr-node: var(--clr-acc);
+    --clr-relation: var(--clr-pri);
+    --clr-filter: var(--clr-acc--800);
   }
 
   body.light-mode {
diff --git a/libs/shared/lib/assets/carbonIcons/carbonIcons.tsx b/libs/shared/lib/assets/carbonIcons/carbonIcons.tsx
deleted file mode 100644
index de379ae1d..000000000
--- a/libs/shared/lib/assets/carbonIcons/carbonIcons.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import React from 'react';
-import type { SVGProps } from 'react';
-
-export function CarbonStringInteger(props: SVGProps<SVGSVGElement>) {
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
-      <path
-        fill="currentColor"
-        d="M26 12h-4v2h4v2h-3v2h3v2h-4v2h4a2.003 2.003 0 0 0 2-2v-6a2 2 0 0 0-2-2m-7 10h-6v-4a2 2 0 0 1 2-2h2v-2h-4v-2h4a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-2v2h4zM8 20v-8H6v1H4v2h2v5H4v2h6v-2z"
-      ></path>
-    </svg>
-  );
-}
-
-export function CarbonStringText(props: SVGProps<SVGSVGElement>) {
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
-      <path
-        fill="currentColor"
-        d="M29 22h-5a2.003 2.003 0 0 1-2-2v-6a2 2 0 0 1 2-2h5v2h-5v6h5zM18 12h-4V8h-2v14h6a2.003 2.003 0 0 0 2-2v-6a2 2 0 0 0-2-2m-4 8v-6h4v6zm-6-8H3v2h5v2H4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h6v-8a2 2 0 0 0-2-2m0 8H4v-2h4z"
-      ></path>
-    </svg>
-  );
-}
-
-export function CarbonCalendar(props: SVGProps<SVGSVGElement>) {
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
-      <path
-        fill="currentColor"
-        d="M26 4h-4V2h-2v2h-8V2h-2v2H6c-1.1 0-2 .9-2 2v20c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 22H6V12h20zm0-16H6V6h4v2h2V6h8v2h2V6h4z"
-      ></path>
-    </svg>
-  );
-}
-
-export function CarbonBoolean(props: SVGProps<SVGSVGElement>) {
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
-      <path fill="currentColor" d="M23 23a7 7 0 1 1 7-7a7.01 7.01 0 0 1-7 7m0-12a5 5 0 1 0 5 5a5.006 5.006 0 0 0-5-5"></path>
-      <circle cx={9} cy={16} r={7} fill="currentColor"></circle>
-    </svg>
-  );
-}
-
-export function CarbonUndefined(props: SVGProps<SVGSVGElement>) {
-  return (
-    <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 0 32 32" {...props}>
-      <path fill="currentColor" d="M11 14h10v4H11z"></path>
-      <path
-        fill="currentColor"
-        d="M29.391 14.527L17.473 2.609A2.08 2.08 0 0 0 16 2c-.533 0-1.067.203-1.473.609L2.609 14.527C2.203 14.933 2 15.466 2 16s.203 1.067.609 1.473L14.526 29.39c.407.407.941.61 1.474.61s1.067-.203 1.473-.609L29.39 17.474c.407-.407.61-.94.61-1.474s-.203-1.067-.609-1.473M16 28.036L3.965 16L16 3.964L28.036 16z"
-      ></path>
-    </svg>
-  );
-}
diff --git a/libs/shared/lib/components/CardToolTipVis/VisualizationTooltip.tsx b/libs/shared/lib/components/CardToolTipVis/VisualizationTooltip.tsx
deleted file mode 100644
index a6e5fc675..000000000
--- a/libs/shared/lib/components/CardToolTipVis/VisualizationTooltip.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import React from 'react';
-import { Icon } from '@graphpolaris/shared/lib/components/icon';
-import styles from './cardtooltipvis.module.scss';
-import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
-import {
-  CarbonStringInteger,
-  CarbonStringText,
-  CarbonCalendar,
-  CarbonBoolean,
-  CarbonUndefined,
-} from '@graphpolaris/shared/lib/assets/carbonIcons/carbonIcons';
-
-export type CardToolTipVisProps = {
-  type: string;
-  name: string;
-  data: Record<string, any>;
-  typeOfSchema?: string;
-  colorHeader: string;
-  connectedTo?: string;
-  connectedFrom?: string;
-  maxVisibleItems?: number;
-  numberOfElements?: number;
-};
-
-const formatNumber = (number: number) => {
-  return number.toLocaleString('de-DE'); // Format number with dots as thousand separators
-};
-
-export const VisualizationTooltip: React.FC<CardToolTipVisProps> = ({
-  type,
-  name,
-  data,
-  colorHeader,
-  maxVisibleItems = 5,
-  connectedFrom,
-  connectedTo,
-  typeOfSchema,
-  numberOfElements,
-}) => {
-  const itemsToShow = Object.entries(data).slice(0, maxVisibleItems);
-
-  return (
-    <div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-1">
-      <div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative">
-        <div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div>
-        <div className="px-2.5 py-1 truncate flex">
-          <Tooltip>
-            <TooltipTrigger className={'flex max-w-full'}>
-              <span className="text-base font-semibold truncate">{name}</span>
-            </TooltipTrigger>
-            <TooltipContent side={'top'}>
-              <span>{name}</span>
-            </TooltipContent>
-          </Tooltip>
-        </div>
-        {/*
-        <div className="flex-shrink-0 ml-2">
-          <Button variantType="secondary" variant="ghost" size="xs" rounded={true} iconComponent={<Close />} onClick={() => {}} />
-        </div>
-        */}
-      </div>
-
-      {type === 'schema' && numberOfElements && (
-        <div className="px-4 py-1 border-b border-sec-200">
-          <div className="flex flex-row gap-1 items-center justify-between">
-            <Icon component="icon-[ic--baseline-numbers]" size={24} />{' '}
-            <span className="ml-auto text-right">{formatNumber(numberOfElements)}</span>
-          </div>
-        </div>
-      )}
-
-      {type === 'schema' && typeOfSchema === 'relationship' && (
-        <div className="px-4 py-1 border-b border-sec-200">
-          <div className="flex flex-row gap-3 items-center justify-between">
-            <span className="font-semibold">From</span>
-            <span className="ml-auto text-right">{connectedFrom}</span>
-          </div>
-
-          <div className="flex flex-row gap-1 items-center justify-between">
-            <span className="font-semibold">To</span>
-            <span className="ml-auto text-right">{connectedTo}</span>
-          </div>
-        </div>
-      )}
-
-      <TooltipProvider delayDuration={300}>
-        <div className={`px-3 py-1.5 ${data.length > maxVisibleItems ? 'max-h-20 overflow-y-auto' : ''}`}>
-          {data && Object.keys(data).length === 0 ? (
-            <div className="flex justify-center items-center h-full">
-              <span>No attributes</span>
-            </div>
-          ) : (
-            Object.entries(data).map(([k, v]) => (
-              <Tooltip key={k}>
-                <div className="flex flex-row gap-1 items-center min-h-6">
-                  <span className={`font-semibold truncate ${type === 'schema' ? 'w-[90%]' : 'min-w-[40%]'}`}>{k}</span>
-                  <TooltipTrigger asChild>
-                    <span className="ml-auto text-right truncate grow-1 flex items-center">
-                      {type === 'schema' ? (
-                        <Icon
-                          className="ml-auto text-right flex-shrink-0"
-                          component={
-                            v === 'int' || v === 'float' ? (
-                              <CarbonStringInteger />
-                            ) : v === 'string' ? (
-                              <CarbonStringText />
-                            ) : v === 'boolean' ? (
-                              <CarbonBoolean />
-                            ) : v === 'date' ? (
-                              <CarbonCalendar />
-                            ) : v === 'undefined' ? (
-                              <CarbonUndefined />
-                            ) : (
-                              <CarbonUndefined />
-                            )
-                          }
-                          color="hsl(var(--clr-sec--400))"
-                          size={24}
-                        />
-                      ) : v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? (
-                        <span className="ml-auto text-right truncate">{typeof v === 'number' ? formatNumber(v) : v.toString()}</span>
-                      ) : (
-                        <div className={`ml-auto mt-auto h-4 w-12 ${styles['diagonal-lines']}`}></div>
-                      )}
-                    </span>
-                  </TooltipTrigger>
-                  <TooltipContent side="right">
-                    <div className="max-w-[18rem] break-all line-clamp-6">
-                      {v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? v : 'noData'}
-                    </div>
-                  </TooltipContent>
-                </div>
-              </Tooltip>
-            ))
-          )}
-        </div>
-      </TooltipProvider>
-    </div>
-  );
-};
diff --git a/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.module.scss b/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.module.scss
deleted file mode 100644
index 87c1972b5..000000000
--- a/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.diagonal-lines {
-  border: 1px solid lightgray;
-  background:
-    repeating-linear-gradient(-45deg, transparent, transparent 6px, #eaeaea 6px, #eaeaea 8px),
-    /* Gray diagonal lines */ linear-gradient(to bottom, transparent, transparent); /* Vertical gradient */
-}
diff --git a/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.stories.tsx b/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.stories.tsx
deleted file mode 100644
index ed1149af7..000000000
--- a/libs/shared/lib/components/CardToolTipVis/cardtooltipvis.stories.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React, { useState } from 'react';
-import type { Meta, StoryObj } from '@storybook/react';
-import { VisualizationTooltip, CardToolTipVisProps } from '@graphpolaris/shared/lib/components/CardToolTipVis';
-
-const metaCardToolTipVis: Meta<typeof VisualizationTooltip> = {
-  component: VisualizationTooltip,
-  title: 'Components/CardToolTipVis',
-};
-
-export default metaCardToolTipVis;
-type Story = StoryObj<typeof VisualizationTooltip>;
-
-export const SchemaNode: Story = {
-  render: (args) => {
-    return (
-      <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
-      </div>
-    );
-  },
-  args: {
-    type: 'schema',
-    typeOfSchema: 'node',
-    name: 'Person',
-    data: {
-      born: 'int',
-      name: 'string',
-      description: 'string',
-    },
-    colorHeader: '#fb7b04',
-    numberOfElements: 1000,
-  },
-};
-
-export const SchemaRelationship: Story = {
-  render: (args) => {
-    return (
-      <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
-      </div>
-    );
-  },
-  args: {
-    type: 'schema',
-    typeOfSchema: 'relationship',
-    name: 'Directed',
-    data: {
-      born: 'int',
-      name: 'string',
-      description: 'string',
-      imdb: 'string',
-      imdbVotes: 'int',
-    },
-    colorHeader: '#0676C1',
-    connectedTo: 'Person',
-    numberOfElements: 231230,
-    connectedFrom: 'Movie',
-  },
-};
-
-export const PopUpVis: Story = {
-  render: (args) => {
-    return (
-      <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
-      </div>
-    );
-  },
-  args: {
-    name: 'Person',
-    type: 'popupvis',
-    data: {
-      bio: 'From wikipedia was born in usa from a firefighter father',
-      name: 'Charlotte Henry',
-      born: {},
-      imdbRank: 21213,
-      imdbVotes: 1213,
-      poster: 'https://image.tmdb.org/t/p/w440_and_h660_face/kTKiREs37qd8GUlNI4Koiupwy6W.jpg',
-      tmdbId: '94105',
-      country: undefined,
-      labels: ['Actor', 'Person', 'Human'],
-    },
-    colorHeader: '#B69AEf',
-  },
-};
diff --git a/libs/shared/lib/components/CardToolTipVis/index.tsx b/libs/shared/lib/components/CardToolTipVis/index.tsx
deleted file mode 100644
index 868ce59de..000000000
--- a/libs/shared/lib/components/CardToolTipVis/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export * from './VisualizationTooltip';
diff --git a/libs/shared/lib/components/DesignGuides/styleGuide.mdx b/libs/shared/lib/components/DesignGuides/styleGuide.mdx
index 6847394b8..fd0755de1 100644
--- a/libs/shared/lib/components/DesignGuides/styleGuide.mdx
+++ b/libs/shared/lib/components/DesignGuides/styleGuide.mdx
@@ -57,6 +57,7 @@ import { visualizationColors } from '../../../../config/src/colors.ts';
     }}
   />
 
+  {' '}
   <ColorItem
     title="Extra"
     colors={{
@@ -64,6 +65,16 @@ import { visualizationColors } from '../../../../config/src/colors.ts';
       dark: 'hsl(var(--clr-dark))',
     }}
   />
+
+  {' '}
+  <ColorItem
+    title="Entities"
+    colors={{
+      node: 'hsl(var(--clr-node))',
+      relation: 'hsl(var(--clr-relation))',
+      filter: 'hsl(var(--clr-filter))',
+    }}
+  />
 </ColorPalette>
 
 #### Usage of colors
diff --git a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.module.scss b/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.module.scss
deleted file mode 100644
index 87c1972b5..000000000
--- a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.diagonal-lines {
-  border: 1px solid lightgray;
-  background:
-    repeating-linear-gradient(-45deg, transparent, transparent 6px, #eaeaea 6px, #eaeaea 8px),
-    /* Gray diagonal lines */ linear-gradient(to bottom, transparent, transparent); /* Vertical gradient */
-}
diff --git a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.stories.tsx b/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.stories.tsx
index 0a4f40459..8f96cbf3c 100644
--- a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.stories.tsx
+++ b/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.stories.tsx
@@ -1,6 +1,8 @@
-import React, { useState } from 'react';
+import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { VisualizationTooltip, CardToolTipVisProps } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
+import { VisualizationTooltip, VisualizationTooltipProps } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
+import { SchemaPopUp, SchemaPopUpProps } from '@graphpolaris/shared/lib/schema/pills/nodes/SchemaPopUp/SchemaPopUp';
+import { NLPopUp, NLPopUpProps } from '@graphpolaris/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup';
 
 const meta: Meta<typeof VisualizationTooltip> = {
   component: VisualizationTooltip,
@@ -8,67 +10,96 @@ const meta: Meta<typeof VisualizationTooltip> = {
 };
 
 export default meta;
-type Story = StoryObj<typeof VisualizationTooltip>;
+
+type CombinedProps = VisualizationTooltipProps & SchemaPopUpProps & NLPopUpProps & { attributes: { name: string; type: string }[] };
+
+type Story = StoryObj<CombinedProps>;
 
 export const SchemaNode: Story = {
   render: (args) => {
+    const { name, attributes, colorHeader, numberOfElements } = args;
+    const data = attributes.reduce(
+      (acc, attr) => {
+        if (attr.name && attr.type) {
+          acc[attr.name] = attr.type;
+        }
+        return acc;
+      },
+      {} as Record<string, any>,
+    );
+
     return (
       <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
+        <VisualizationTooltip name={name} colorHeader={colorHeader}>
+          <SchemaPopUp data={data} numberOfElements={numberOfElements} />
+        </VisualizationTooltip>
       </div>
     );
   },
   args: {
-    type: 'schema',
-    typeOfSchema: 'node',
     name: 'Person',
-    data: {
-      born: 'int',
-      name: 'string',
-      description: 'string',
-    },
-    colorHeader: '#fb7b04',
+    attributes: [
+      { name: 'int', type: 'int' },
+      { name: 'float', type: 'float' },
+      { name: 'date', type: 'date' },
+      { name: 'string', type: 'string' },
+      { name: 'boolean', type: 'boolean' },
+      { name: 'undefined', type: 'undefined' },
+    ],
+    colorHeader: 'hsl(var(--clr-node))',
     numberOfElements: 1000,
   },
 };
 
 export const SchemaRelationship: Story = {
   render: (args) => {
+    const { name, attributes, colorHeader, numberOfElements, connections } = args;
+    const data = attributes.reduce(
+      (acc, attr) => {
+        if (attr.name && attr.type) {
+          acc[attr.name] = attr.type;
+        }
+        return acc;
+      },
+      {} as Record<string, any>,
+    );
+
     return (
       <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
+        <VisualizationTooltip name={name} colorHeader={colorHeader}>
+          <SchemaPopUp data={data} numberOfElements={numberOfElements} connections={connections} />
+        </VisualizationTooltip>
       </div>
     );
   },
   args: {
-    type: 'schema',
-    typeOfSchema: 'relationship',
     name: 'Directed',
-    data: {
-      born: 'int',
-      name: 'string',
-      description: 'string',
-      imdb: 'string',
-      imdbVotes: 'int',
-    },
+    attributes: [
+      { name: 'born', type: 'int' },
+      { name: 'name', type: 'string' },
+      { name: 'description', type: 'string' },
+      { name: 'imdb', type: 'string' },
+      { name: 'imdbVotes', type: 'int' },
+    ],
     colorHeader: '#0676C1',
-    connectedTo: 'Person',
     numberOfElements: 231230,
-    connectedFrom: 'Movie',
+    connections: { to: 'Person', from: 'Movie' },
   },
 };
 
-export const PopUpVis: Story = {
+export const NodeLinkPopUp: Story = {
   render: (args) => {
+    const { name, data, colorHeader } = args;
     return (
       <div className="w-1/4 my-10 m-auto flex items-center justify-center">
-        <VisualizationTooltip {...args} />
+        <VisualizationTooltip name={name} colorHeader={colorHeader}>
+          <NLPopUp data={data} />
+        </VisualizationTooltip>
       </div>
     );
   },
   args: {
     name: 'Person',
-    type: 'popupvis',
     data: {
       bio: 'From wikipedia was born in usa from a firefighter father',
       name: 'Charlotte Henry',
diff --git a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.tsx b/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.tsx
index 699c3354a..67ecc6be1 100644
--- a/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.tsx
+++ b/libs/shared/lib/components/VisualizationTooltip/VisualizationTooltip.tsx
@@ -1,140 +1,24 @@
-import React from 'react';
-import { Icon } from '@graphpolaris/shared/lib/components/icon';
-import styles from './VisualizationTooltip.module.scss';
+import React, { ReactNode } from 'react';
 import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
-import {
-  CarbonStringInteger,
-  CarbonStringText,
-  CarbonCalendar,
-  CarbonBoolean,
-  CarbonUndefined,
-} from '@graphpolaris/shared/lib/assets/carbonIcons/carbonIcons';
 
-export type CardToolTipVisProps = {
-  type: string;
+export type VisualizationTooltipProps = {
   name: string;
-  data: Record<string, any>;
-  typeOfSchema?: string;
   colorHeader: string;
-  connectedTo?: string;
-  connectedFrom?: string;
-  maxVisibleItems?: number;
-  numberOfElements?: number;
+  children: ReactNode;
 };
 
-const formatNumber = (number: number) => {
-  return number.toLocaleString('de-DE'); // Format number with dots as thousand separators
-};
-
-export const VisualizationTooltip: React.FC<CardToolTipVisProps> = ({
-  type,
-  name,
-  data,
-  colorHeader,
-  maxVisibleItems = 5,
-  connectedFrom,
-  connectedTo,
-  typeOfSchema,
-  numberOfElements,
-}) => {
-  const itemsToShow = Object.entries(data).slice(0, maxVisibleItems);
-
+export const VisualizationTooltip: React.FC<VisualizationTooltipProps> = ({ name, colorHeader, children }) => {
   return (
-    <div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-1">
+    <div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-2">
       <div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative">
         <div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div>
         <div className="px-2.5 py-1 truncate flex">
-          <Tooltip>
-            <TooltipTrigger className={'flex max-w-full'}>
-              <span className="text-base font-semibold truncate">{name}</span>
-            </TooltipTrigger>
-            <TooltipContent side={'top'}>
-              <span>{name}</span>
-            </TooltipContent>
-          </Tooltip>
-        </div>
-        {/*
-        <div className="flex-shrink-0 ml-2">
-          <Button variantType="secondary" variant="ghost" size="xs" rounded={true} iconComponent={<Close />} onClick={() => {}} />
-        </div>
-        */}
-      </div>
-
-      {type === 'schema' && numberOfElements && (
-        <div className="px-4 py-1 border-b border-sec-200">
-          <div className="flex flex-row gap-1 items-center justify-between">
-            <Icon component="icon-[ic--baseline-numbers]" size={24} />{' '}
-            <span className="ml-auto text-right">{formatNumber(numberOfElements)}</span>
+          <div className={'flex max-w-full'}>
+            <span className="text-base font-semibold truncate">{name}</span>
           </div>
         </div>
-      )}
-
-      {type === 'schema' && typeOfSchema === 'relationship' && (
-        <div className="px-4 py-1 border-b border-sec-200">
-          <div className="flex flex-row gap-3 items-center justify-between">
-            <span className="font-semibold">From</span>
-            <span className="ml-auto text-right">{connectedFrom}</span>
-          </div>
-
-          <div className="flex flex-row gap-1 items-center justify-between">
-            <span className="font-semibold">To</span>
-            <span className="ml-auto text-right">{connectedTo}</span>
-          </div>
-        </div>
-      )}
-
-      <TooltipProvider delayDuration={300}>
-        <div className={`px-3 py-1.5 ${data.length > maxVisibleItems ? 'max-h-20 overflow-y-auto' : ''}`}>
-          {data && Object.keys(data).length === 0 ? (
-            <div className="flex justify-center items-center h-full">
-              <span>No attributes</span>
-            </div>
-          ) : (
-            Object.entries(data).map(([k, v]) => (
-              <Tooltip key={k}>
-                <div className="flex flex-row gap-1 items-center min-h-6">
-                  <span className={`font-semibold truncate ${type === 'schema' ? 'w-[90%]' : 'min-w-[40%]'}`}>{k}</span>
-                  <TooltipTrigger asChild>
-                    <span className="ml-auto text-right truncate grow-1 flex items-center">
-                      {type === 'schema' ? (
-                        <Icon
-                          className="ml-auto text-right flex-shrink-0"
-                          component={
-                            v === 'int' || v === 'float' ? (
-                              <CarbonStringInteger />
-                            ) : v === 'string' ? (
-                              <CarbonStringText />
-                            ) : v === 'boolean' ? (
-                              <CarbonBoolean />
-                            ) : v === 'date' ? (
-                              <CarbonCalendar />
-                            ) : v === 'undefined' ? (
-                              <CarbonUndefined />
-                            ) : (
-                              <CarbonUndefined />
-                            )
-                          }
-                          color="hsl(var(--clr-sec--400))"
-                          size={24}
-                        />
-                      ) : v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? (
-                        <span className="ml-auto text-right truncate">{typeof v === 'number' ? formatNumber(v) : v.toString()}</span>
-                      ) : (
-                        <div className={`ml-auto mt-auto h-4 w-12 ${styles['diagonal-lines']}`}></div>
-                      )}
-                    </span>
-                  </TooltipTrigger>
-                  <TooltipContent side="right">
-                    <div className="max-w-[18rem] break-all line-clamp-6">
-                      {v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? v : 'noData'}
-                    </div>
-                  </TooltipContent>
-                </div>
-              </Tooltip>
-            ))
-          )}
-        </div>
-      </TooltipProvider>
+      </div>
+      {children}
     </div>
   );
 };
diff --git a/libs/shared/lib/components/pills/Pill.tsx b/libs/shared/lib/components/pills/Pill.tsx
index a479ef713..56492a5ed 100644
--- a/libs/shared/lib/components/pills/Pill.tsx
+++ b/libs/shared/lib/components/pills/Pill.tsx
@@ -156,7 +156,7 @@ export const EntityPill = React.memo((props: Omit<PillI, 'topColor'> & { withHan
     </>
   );
   return (
-    <PillContext.Provider value={{ color: 'hsl(29 96 60)' }}>
+    <PillContext.Provider value={{ color: 'hsl(var(--clr-node))' }}>
       <Pill {...props} corner="rounded" handles={handles} />
     </PillContext.Provider>
   );
@@ -184,7 +184,7 @@ export const RelationPill = React.memo((props: Omit<PillI, 'topColor'> & { withH
   );
 
   return (
-    <PillContext.Provider value={{ color: '#0676C1' }}>
+    <PillContext.Provider value={{ color: 'hsl(var(--clr-relation))' }}>
       <Pill {...props} corner="diamond" handles={handles} />
     </PillContext.Provider>
   );
@@ -198,7 +198,7 @@ export const LogicPill = React.memo((props: Omit<PillI, 'topColor'>) => {
   );
 
   return (
-    <PillContext.Provider value={{ color: '#543719' }}>
+    <PillContext.Provider value={{ color: 'hsl(var(--clr-filter))' }}>
       <Pill {...props} corner="square" handles={handles} />
     </PillContext.Provider>
   );
diff --git a/libs/shared/lib/components/pills/PillContext.tsx b/libs/shared/lib/components/pills/PillContext.tsx
index 3ec61a12a..34cf06e2e 100644
--- a/libs/shared/lib/components/pills/PillContext.tsx
+++ b/libs/shared/lib/components/pills/PillContext.tsx
@@ -1,5 +1,5 @@
 import { createContext } from 'react';
 
 export const PillContext = createContext({
-  color: 'hsl(29 96 60)',
+  color: 'hsl(var(--clr-node))',
 });
diff --git a/libs/shared/lib/components/tooltip/Tooltip.tsx b/libs/shared/lib/components/tooltip/Tooltip.tsx
index 4f6b91f7d..ac37a2506 100644
--- a/libs/shared/lib/components/tooltip/Tooltip.tsx
+++ b/libs/shared/lib/components/tooltip/Tooltip.tsx
@@ -205,7 +205,7 @@ export const TooltipContent = React.forwardRef<
     <FloatingPortal>
       <div
         ref={ref}
-        className={`z-50 max-w-64 rounded bg-light px-2 py-2 shadow text-xs border border-secondary-200
+        className={`max-w-64 rounded bg-light px-2 py-2 shadow text-xs border border-secondary-200
           text-dark animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0
           data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
           data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2${className ? ` ${className}` : ''}`}
diff --git a/libs/shared/lib/data-access/store/schemaSlice.ts b/libs/shared/lib/data-access/store/schemaSlice.ts
index 39bd69e4e..e0398dac1 100644
--- a/libs/shared/lib/data-access/store/schemaSlice.ts
+++ b/libs/shared/lib/data-access/store/schemaSlice.ts
@@ -12,6 +12,7 @@ export type SchemaSettings = {
   connectionType: SchemaConnectionTypes;
   layoutName: AllLayoutAlgorithms;
   animatedEdges: boolean;
+  showMinimap: boolean;
 };
 
 type schemaSliceI = {
@@ -37,6 +38,7 @@ export const initialState: schemaSliceI = {
     connectionType: 'connection',
     layoutName: Layouts.DAGRE,
     animatedEdges: false,
+    showMinimap: true,
   },
 };
 export const schemaSlice = createSlice({
diff --git a/libs/shared/lib/schema/model/reactflow.tsx b/libs/shared/lib/schema/model/reactflow.tsx
index d03ac9135..60567d10a 100644
--- a/libs/shared/lib/schema/model/reactflow.tsx
+++ b/libs/shared/lib/schema/model/reactflow.tsx
@@ -37,6 +37,7 @@ export type SchemaReactflowEntity = SchemaReactflowData & {
   x: number;
   y: number;
   reactFlowRef: any;
+  tooltipClose: boolean;
 };
 
 export type SchemaReactflowRelation = SchemaReactflowData & {
@@ -47,6 +48,8 @@ export type SchemaReactflowRelation = SchemaReactflowData & {
   toRatio: number;
   x: number;
   y: number;
+  reactFlowRef: any;
+  tooltipClose: boolean;
 };
 
 export type SchemaReactflowNodeWithFunctions = SchemaReactflowEntity & {
diff --git a/libs/shared/lib/schema/panel/Schema.tsx b/libs/shared/lib/schema/panel/Schema.tsx
index 086af4dbb..aae59bad9 100644
--- a/libs/shared/lib/schema/panel/Schema.tsx
+++ b/libs/shared/lib/schema/panel/Schema.tsx
@@ -49,6 +49,13 @@ export const Schema = (props: Props) => {
   const [nodes, setNodes, onNodesChange] = useNodesState([] as Node[]);
   const [edges, setEdges, onEdgesChange] = useEdgesState([] as Edge[]);
 
+  // viewport
+  const initialViewportRef = useRef<{ x: number; y: number; zoom: number } | null>(null);
+  const [hasLayoutBeenRun, setHasLayoutBeenRun] = useState(false);
+
+  // Time threshold for distinguishing between a click and a drag
+  const isPillClicked = useRef<boolean>(false);
+
   const reactFlowInstanceRef = useRef<ReactFlowInstance | null>(null);
   const reactFlowRef = useRef<HTMLDivElement>(null);
 
@@ -87,18 +94,45 @@ export const Schema = (props: Props) => {
     updateLayout();
     const expandedSchema = schemaExpandRelation(schemaGraphology);
     const bounds = reactFlowRef.current?.getBoundingClientRect();
-
     const xy = bounds ? { x1: 50, x2: bounds.width - 50, y1: 50, y2: bounds.height - 200 } : { x1: 0, x2: 500, y1: 0, y2: 1000 };
     await layout.current?.layout(expandedSchema, xy);
     const schemaFlow = schemaGraphology2Reactflow(expandedSchema, settings.connectionType, settings.animatedEdges);
 
-    const nodesWithRef = schemaFlow.nodes.map((node) => ({
-      ...node,
-      data: { ...node.data, reactFlowRef },
-    }));
+    let nodesWithRef, edgesWithRef;
+    if (!hasLayoutBeenRun) {
+      nodesWithRef = schemaFlow.nodes.map((node) => {
+        return {
+          ...node,
+          data: { ...node.data, reactFlowRef, tooltipClose: false },
+        };
+      });
+
+      edgesWithRef = schemaFlow.edges.map((edge) => {
+        return {
+          ...edge,
+          data: { ...edge.data, reactFlowRef, tooltipClose: false },
+        };
+      });
+
+      setHasLayoutBeenRun(true);
+    } else {
+      nodesWithRef = nodes.map((node) => {
+        return {
+          ...node,
+          data: { ...node.data },
+        };
+      });
+
+      edgesWithRef = edges.map((edge) => {
+        return {
+          ...edge,
+          data: { ...edge.data },
+        };
+      });
+    }
 
     setNodes(nodesWithRef);
-    setEdges(schemaFlow.edges);
+    setEdges(edgesWithRef);
     setTimeout(() => fitView(), 100);
   }
 
@@ -118,14 +152,39 @@ export const Schema = (props: Props) => {
   const nodeColor = (node: any) => {
     switch (node.type) {
       case 'entity':
-        return '#fb7b04';
+        return 'hsl(var(--clr-node))';
       case 'relation':
-        return '#0676C1';
+        return 'hsl(var(--clr-relation))';
       default:
         return '#ff0072';
     }
   };
 
+  const handleOnClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+    const target = event.target as HTMLElement;
+    const clickedOutsideNode = target.classList.contains('react-flow__pane');
+
+    setNodes((nds) =>
+      nds.map((node) => ({
+        ...node,
+        data: {
+          ...node.data,
+          tooltipClose: clickedOutsideNode,
+        },
+      })),
+    );
+
+    setEdges((edg) =>
+      edg.map((edge) => ({
+        ...edge,
+        data: {
+          ...edge.data,
+          tooltipClose: clickedOutsideNode,
+        },
+      })),
+    );
+  };
+
   return (
     <Panel
       title="Schema"
@@ -204,7 +263,10 @@ export const Schema = (props: Props) => {
         </>
       }
     >
-      <div className="schema-panel w-full h-full flex flex-col justify-between" ref={reactFlowRef}>
+      <div
+        className="schema-panel w-full h-full flex flex-col justify-between"
+        ref={reactFlowRef}
+      >
         {nodes.length === 0 ? (
           <p className="m-3 text-xl font-bold">No Elements</p>
         ) : (
@@ -226,31 +288,13 @@ export const Schema = (props: Props) => {
                 reactFlowInstanceRef.current = reactFlowInstance;
                 onInit(reactFlowInstance);
               }}
+              onClick={handleOnClick}
               proOptions={{ hideAttribution: true }}
             >
-              <MiniMap nodeColor={nodeColor} />
+              {settings.showMinimap && <MiniMap nodeColor={nodeColor} />}
             </ReactFlow>
           </ReactFlowProvider>
         )}
-        {/* <div>
-          <div
-            className="w-full py-0 px-2 bg-secondary-50 cursor-pointer border-y flex items-center gap-1"
-            onClick={() => setExpanded(!expanded)}
-          >
-            <Button
-              size="xs"
-              variant="ghost"
-              iconComponent={expanded ? <KeyboardArrowDown /> : <KeyboardArrowRight />}
-              onClick={() => setExpanded(!expanded)}
-            />
-            <span className="text-xs font-semibold text-secondary-600 truncate">Schema settings</span>
-          </div>
-          {expanded && (
-            <div className="h-full w-full overflow-y-auto">
-              <SchemaDialog open={toggleSchemaSettings} onClose={() => setToggleSchemaSettings(false)} />
-            </div>
-          )}
-        </div> */}
       </div>
     </Panel>
   );
diff --git a/libs/shared/lib/schema/panel/SchemaSettings.tsx b/libs/shared/lib/schema/panel/SchemaSettings.tsx
index 0a6534d52..572472aa0 100644
--- a/libs/shared/lib/schema/panel/SchemaSettings.tsx
+++ b/libs/shared/lib/schema/panel/SchemaSettings.tsx
@@ -19,6 +19,14 @@ export const SchemaSettings = () => {
           dispatch(setSchemaSettings({ ...settings, animatedEdges: value as any }));
         }}
       />
+      <Input
+        type="boolean"
+        value={settings.showMinimap}
+        label="Show Minimap"
+        onChange={(value: boolean) => {
+          dispatch(setSchemaSettings({ ...settings, showMinimap: value as any }));
+        }}
+      />
       <Input
         type="dropdown"
         label="Type of Connection"
diff --git a/libs/shared/lib/schema/panel/schema.stories.tsx b/libs/shared/lib/schema/panel/schema.stories.tsx
index 17679da8a..69e3b95a6 100644
--- a/libs/shared/lib/schema/panel/schema.stories.tsx
+++ b/libs/shared/lib/schema/panel/schema.stories.tsx
@@ -91,6 +91,27 @@ export const TestSimple = {
   },
 };
 
+export const TestTooltip = {
+  play: async () => {
+    const dispatch = Mockstore.dispatch;
+    const schema = SchemaUtils.schemaBackend2Graphology({
+      nodes: [
+        {
+          name: 'Thijs',
+          attributes: [
+            { name: 'city', type: 'string' },
+            { name: 'vip', type: 'bool' },
+            { name: 'state', type: 'string' },
+          ],
+        },
+      ],
+      edges: [],
+    });
+
+    dispatch(setSchema(schema.export()));
+  },
+};
+
 export const TestMovieSchema = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
diff --git a/libs/shared/lib/schema/pills/nodes/SchemaPopUp/SchemaPopUp.tsx b/libs/shared/lib/schema/pills/nodes/SchemaPopUp/SchemaPopUp.tsx
new file mode 100644
index 000000000..331e0761e
--- /dev/null
+++ b/libs/shared/lib/schema/pills/nodes/SchemaPopUp/SchemaPopUp.tsx
@@ -0,0 +1,89 @@
+import React from 'react';
+import { Icon } from '@graphpolaris/shared/lib/components/icon';
+import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
+import { useSchemaStats } from '@graphpolaris/shared/lib/data-access';
+
+const formatNumber = (number: number) => {
+  return number.toLocaleString('de-DE');
+};
+
+export type SchemaPopUpProps = {
+  data: Record<string, any>;
+  connections?: { to: string; from: string };
+  numberOfElements?: number;
+};
+
+export const SchemaPopUp: React.FC<SchemaPopUpProps> = ({ data, numberOfElements, connections }) => {
+  return (
+    <>
+      <div className="">
+        {numberOfElements != null && numberOfElements != 0 && (
+          <div className="border-b border-sec-200">
+            <div className="flex flex-row gap-1 items-center justify-between px-3 py-1">
+              <Icon component="icon-[ic--baseline-numbers]" size={24} />
+              <span className="ml-auto text-right">{formatNumber(numberOfElements)}</span>
+            </div>
+          </div>
+        )}
+        {connections && (
+          <div className="border-b border-sec-200 px-3 py-1">
+            <div className="flex flex-row gap-3 items-center justify-between">
+              <span className="font-semibold">From</span>
+              <span className="ml-auto text-right">{connections.from}</span>
+            </div>
+            <div className="flex flex-row gap-1 items-center justify-between">
+              <span className="font-semibold">To</span>
+              <span className="ml-auto text-right">{connections.to}</span>
+            </div>
+          </div>
+        )}
+        <TooltipProvider delayDuration={300}>
+          <div className="px-3 py-1">
+            {Object.keys(data).length === 0 ? (
+              <div className="flex justify-center items-center h-full ">
+                <span>No attributes</span>
+              </div>
+            ) : (
+              Object.entries(data).map(([k, v]) => (
+                <Tooltip key={k}>
+                  <div className="flex flex-row gap-1 items-center min-h-6">
+                    <span className={`font-semibold truncate w-[90%]`}>{k}</span>
+                    <TooltipTrigger asChild>
+                      <span className="ml-auto text-right truncate grow-1 flex items-center">
+                        <Icon
+                          className="ml-auto text-right flex-shrink-0"
+                          component={
+                            v === 'int' || v === 'float' ? (
+                              <Icon component="icon-[carbon--string-integer]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            ) : v === 'string' ? (
+                              <Icon component="icon-[carbon--string-text]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            ) : v === 'boolean' || v === 'bool' ? (
+                              <Icon component="icon-[carbon--boolean]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            ) : v === 'date' || v === 'time' || v === 'duration' || v === 'datetime' ? (
+                              <Icon component="icon-[carbon--calendar]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            ) : v === 'undefined' ? (
+                              <Icon component="icon-[carbon--undefined]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            ) : (
+                              <Icon component="icon-[carbon--undefined]" size={24} color={'hsl(var(--clr-sec--500))'} />
+                            )
+                          }
+                          color="hsl(var(--clr-sec--400))"
+                          size={24}
+                        />
+                      </span>
+                    </TooltipTrigger>
+                    <TooltipContent side="right">
+                      <div className="max-w-[18rem] break-all line-clamp-6 mx-1">
+                        {v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? v : 'noData'}
+                      </div>
+                    </TooltipContent>
+                  </div>
+                </Tooltip>
+              ))
+            )}
+          </div>
+        </TooltipProvider>
+      </div>
+    </>
+  );
+};
diff --git a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx
index 3bf2158a5..c4fb7f010 100644
--- a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx
+++ b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPill.tsx
@@ -1,14 +1,20 @@
-import React, { useState, useRef, useEffect } from 'react';
-import { Handle, Position, NodeProps } from 'reactflow';
+import React, { useState, useRef, useEffect, useMemo } from 'react';
+import { Handle, Position, NodeProps, useViewport } from 'reactflow';
 import { SchemaReactflowNodeWithFunctions } from '../../../model/reactflow';
 import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
 import { SchemaNode } from '../../../model';
 import { EntityPill } from '@graphpolaris/shared/lib/components';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip';
-import { VisualizationTooltip, CardToolTipVisProps } from '@graphpolaris/shared/lib/components/CardToolTipVis';
+import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
+import { SchemaPopUp } from '../SchemaPopUp/SchemaPopUp';
+import { useSchemaStats } from '@graphpolaris/shared/lib/data-access';
 
 export const SchemaEntityPill = React.memo(({ id, selected, data }: NodeProps<SchemaReactflowNodeWithFunctions>) => {
-  const [openPopup, setOpenPopup] = useState(false);
+  const [openPopupLocation, setOpenPopupLocation] = useState<{ x: number; y: number } | null>(null);
+
+  const viewport = useViewport();
+  const schemaStats = useSchemaStats();
+
   const ref = useRef<HTMLDivElement>(null);
   /**
    * adds drag functionality in order to be able to drag the entityNode to the schema
@@ -24,19 +30,23 @@ export const SchemaEntityPill = React.memo(({ id, selected, data }: NodeProps<Sc
     event.dataTransfer.effectAllowed = 'move';
   };
 
-  /**
-   * displays the NodeQualityPopup when clicked, and hides them when clicked again
-   */
-  const onClickToggleNodeQualityPopup = (): void => {
-    data.toggleNodeQualityPopup(id);
-  };
+  useEffect(() => {
+    if (data.tooltipClose === true) {
+      setOpenPopupLocation(null);
+    }
+  }, [data.tooltipClose]);
 
-  /**
-   * displays the attribute analytics popup menu when clicked, and hides them when clicked again
-   */
-  const onClickToggleAttributeAnalyticsPopupMenu = (): void => {
-    data.toggleAttributeAnalyticsPopupMenu(id);
-  };
+  const tooltipX = useMemo(() => {
+    if (ref.current == null || openPopupLocation == null) return -1;
+    const rect = ref.current.getBoundingClientRect();
+    return rect.x - openPopupLocation.x + (rect.width / 2);
+  }, [viewport.x, openPopupLocation]);
+
+  const tooltipY = useMemo(() => {
+    if (ref.current == null || openPopupLocation == null) return -1;
+    const rect = ref.current.getBoundingClientRect();
+    return rect.y - openPopupLocation.y + (rect.height / 2);
+  }, [viewport.y, openPopupLocation]);
 
   return (
     <>
@@ -48,32 +58,34 @@ export const SchemaEntityPill = React.memo(({ id, selected, data }: NodeProps<Sc
           if (!event.shiftKey) event.stopPropagation();
         }}
         onClickCapture={(event) => {
-          setOpenPopup(!openPopup);
+          if (openPopupLocation != null || ref.current == null) {
+            return;
+          }
+
+          setOpenPopupLocation(ref.current.getBoundingClientRect());
         }}
         draggable
         ref={ref}
       >
-        {openPopup && (
+        {openPopupLocation !== null && (
           <Tooltip key={data.name} open={true} boundaryElement={data.reactFlowRef} showArrow={true}>
-            <TooltipTrigger />
-            <TooltipContent side="right">
+            <TooltipTrigger x={tooltipX} y={tooltipY} />
+            <TooltipContent>
               <div>
-                <VisualizationTooltip
-                  type="schema"
-                  typeOfSchema="node"
-                  name={data.name}
-                  colorHeader="#fb7b04"
-                  numberOfElements={1000}
-                  data={data.attributes.reduce(
-                    (acc, attr) => {
-                      if (attr.name && attr.type) {
-                        acc[attr.name] = attr.type;
-                      }
-                      return acc;
-                    },
-                    {} as Record<string, string>,
-                  )}
-                />
+                <VisualizationTooltip name={data.name} colorHeader={'hsl(var(--clr-node))'}>
+                  <SchemaPopUp
+                    data={data.attributes.reduce(
+                      (acc, attr) => {
+                        if (attr.name && attr.type) {
+                          acc[attr.name] = attr.type;
+                        }
+                        return acc;
+                      },
+                      {} as Record<string, string>,
+                    )}
+                    numberOfElements={schemaStats.nodeStats[data.name]?.count}
+                  />
+                </VisualizationTooltip>
               </div>
             </TooltipContent>
           </Tooltip>
diff --git a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx
index 10643f810..2bee28161 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationPill.tsx
@@ -1,19 +1,22 @@
-import React, { useState, useRef } from 'react';
-import { Handle, Position, NodeProps } from 'reactflow';
+import React, { useState, useRef, useEffect, useMemo } from 'react';
+import { Handle, Position, NodeProps, useViewport } from 'reactflow';
 import { SchemaReactflowRelationWithFunctions } from '../../../model/reactflow';
 import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder';
-import { Popup } from '@graphpolaris/shared/lib/components/Popup';
-import { SchemaRelationshipPopup } from './SchemaRelationshipPopup';
 import { SchemaEdge } from '../../../model';
 import { RelationPill } from '@graphpolaris/shared/lib/components';
 
 import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip';
-import { VisualizationTooltip, CardToolTipVisProps } from '@graphpolaris/shared/lib/components/CardToolTipVis';
+import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
+import { SchemaPopUp } from '../SchemaPopUp/SchemaPopUp';
+import { useSchemaStats } from '@graphpolaris/shared/lib/data-access';
 
 export const SchemaRelationPill = React.memo(({ id, selected, data, ...props }: NodeProps<SchemaReactflowRelationWithFunctions>) => {
-  const [openPopup, setOpenPopup] = useState(false);
-  const ref = useRef<HTMLDivElement>(null);
+  const [openPopupLocation, setOpenPopupLocation] = useState<{ x: number; y: number } | null>(null);
+
+  const viewport = useViewport();
+  const schemaStats = useSchemaStats();
 
+  const ref = useRef<HTMLDivElement>(null);
   /**
    * Adds drag functionality in order to be able to drag the relationNode to the schema.
    * @param event React Mouse drag event.
@@ -34,19 +37,24 @@ export const SchemaRelationPill = React.memo(({ id, selected, data, ...props }:
     event.dataTransfer.effectAllowed = 'move';
   };
 
-  /**
-   * Displays the NodeQualityPopup when clicked, and hides them when clicked again
-   */
-  const onClickToggleNodeQualityPopup = (): void => {
-    data.toggleNodeQualityPopup(data.collection);
-  };
+  useEffect(() => {
+    if (data.tooltipClose === true) {
+      setOpenPopupLocation(null);
+    }
+  }, [data.tooltipClose]);
+
+  const tooltipX = useMemo(() => {
+    if (ref.current == null || openPopupLocation == null) return -1;
+    const rect = ref.current.getBoundingClientRect();
+    return rect.x - openPopupLocation.x + (rect.width / 2);
+  }, [viewport.x, openPopupLocation]);
+
+  const tooltipY = useMemo(() => {
+    if (ref.current == null || openPopupLocation == null) return -1;
+    const rect = ref.current.getBoundingClientRect();
+    return rect.y - openPopupLocation.y + (rect.height / 2);
+  }, [viewport.y, openPopupLocation]);
 
-  /**
-   * displays the attribute analytics popup menu when clicked, and hides them when clicked again
-   */
-  const onClickToggleAttributeAnalyticsPopupMenu = (): void => {
-    data.toggleAttributeAnalyticsPopupMenu(data.collection);
-  };
   return (
     <>
       <div
@@ -57,37 +65,39 @@ export const SchemaRelationPill = React.memo(({ id, selected, data, ...props }:
           if (!event.shiftKey) event.stopPropagation();
         }}
         onClickCapture={(event) => {
-          setOpenPopup(!openPopup);
+          if (openPopupLocation != null || ref.current == null) {
+            return;
+          }
+
+          setOpenPopupLocation(ref.current.getBoundingClientRect());
         }}
         draggable
+        ref={ref}
       >
-        {openPopup && (
-          <Tooltip key={data.name} open={true} boundaryElement={ref} showArrow={true}>
-            <TooltipTrigger />
-            <TooltipContent side="top">
+        {openPopupLocation !== null && (
+          <Tooltip key={data.name} open={true} boundaryElement={data.reactFlowRef} showArrow={true}>
+            <TooltipTrigger x={tooltipX} y={tooltipY} />
+            <TooltipContent>
               <div>
-                <VisualizationTooltip
-                  type="schema"
-                  typeOfSchema="relationship"
-                  name={data.collection}
-                  colorHeader="#0676C1"
-                  numberOfElements={1000}
-                  connectedFrom={data.from}
-                  connectedTo={data.to}
-                  data={
-                    data.attributes.length > 0
-                      ? data.attributes.reduce(
-                          (acc, attr) => {
-                            if (attr.name && attr.type) {
-                              acc[attr.name] = attr.type;
-                            }
-                            return acc;
-                          },
-                          {} as Record<string, string>,
-                        )
-                      : {}
-                  }
-                />
+                <VisualizationTooltip name={data.collection} colorHeader={'hsl(var(--clr-relation))'}>
+                  <SchemaPopUp
+                    data={
+                      data.attributes.length > 0
+                        ? data.attributes.reduce(
+                            (acc, attr) => {
+                              if (attr.name && attr.type) {
+                                acc[attr.name] = attr.type;
+                              }
+                              return acc;
+                            },
+                            {} as Record<string, string>,
+                          )
+                        : {}
+                    }
+                    connections={{ from: data.from, to: data.to }}
+                    numberOfElements={schemaStats.edgeStats[data.collection]?.count}
+                  />
+                </VisualizationTooltip>
               </div>
             </TooltipContent>
           </Tooltip>
diff --git a/libs/shared/lib/vis/components/VisualizationPanel.tsx b/libs/shared/lib/vis/components/VisualizationPanel.tsx
index b3aa4c103..65772caaa 100644
--- a/libs/shared/lib/vis/components/VisualizationPanel.tsx
+++ b/libs/shared/lib/vis/components/VisualizationPanel.tsx
@@ -81,10 +81,9 @@ export const VisualizationPanel = ({ fullSize }: { fullSize: () => void }) => {
 
   return (
     <div
-      className="vis-panel h-full w-full flex flex-col border bg-light"
+      className="relative pt-7 vis-panel h-full w-full flex flex-col border bg-light"
       onMouseDownCapture={() => dispatch(resultSetFocus({ focusType: 'visualization' }))}
     >
-      <VisualizationTabBar fullSize={fullSize} />
       <div className="grow overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
         {graphQueryResult.queryingBackend ? (
           <Querying />
@@ -116,6 +115,7 @@ export const VisualizationPanel = ({ fullSize }: { fullSize: () => void }) => {
           </div>
         )}
       </div>
+      <VisualizationTabBar fullSize={fullSize} />
     </div>
   );
 };
diff --git a/libs/shared/lib/vis/components/VisualizationTabBar.tsx b/libs/shared/lib/vis/components/VisualizationTabBar.tsx
index 8ff06c7a4..96b68a034 100644
--- a/libs/shared/lib/vis/components/VisualizationTabBar.tsx
+++ b/libs/shared/lib/vis/components/VisualizationTabBar.tsx
@@ -36,7 +36,7 @@ export default function VisualizationTabBar(props: { fullSize: () => void }) {
   };
 
   return (
-    <div className="sticky shrink-0 top-0 flex items-stretch justify-between h-7 bg-secondary-100 border-b border-secondary-200 max-w-full">
+    <div className="absolute shrink-0 top-0 left-0 right-0 flex items-stretch justify-between h-7 bg-secondary-100 border-b border-secondary-200 max-w-full">
       <div className="flex items-center">
         <h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">Visualization</h1>
       </div>
diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
index 47f722d31..52b711cb7 100644
--- a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
@@ -12,10 +12,10 @@ import {
   Text,
   Texture,
   Resource,
-  RenderTexture
+  RenderTexture,
 } from 'pixi.js';
 import { useAppDispatch, useML, useSearchResultData } from '../../../../data-access';
-import { NLPopup } from './NLPopup';
+import { NLPopUp } from './NLPopup';
 import { hslStringToHex, nodeColor } from './utils';
 import { CytoscapeLayout, GraphologyLayout, LayoutFactory, Layouts, GraphologyForceAtlas2Webworker } from '../../../../graph-layout';
 import { MultiGraph } from 'graphology';
@@ -23,6 +23,8 @@ import { Viewport } from 'pixi-viewport';
 import { NodelinkVisProps } from '../nodelinkvis';
 import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip';
 import { MovedEvent } from 'pixi-viewport/dist/types';
+import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip';
+import { nodeColorHex } from './utils';
 import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
 import { useConfig } from '@graphpolaris/shared/lib/data-access/store';
 
@@ -107,23 +109,23 @@ export const NLPixi = (props: Props) => {
     let size = config.NODE_RADIUS * (1 / responsiveScale);
     let lineWidth = selected ? 12 : 6;
     renderTexture.resize(size + lineWidth, size + lineWidth);
-    
+
     const graphics = new Graphics();
     graphics.lineStyle(lineWidth, 0x4e586a);
     graphics.beginFill(0xffffff, 1);
 
-    if (props.configuration.shapes?.shape == "circle") {
-      graphics.drawCircle((size / 2) + (lineWidth / 2), (size / 2) + (lineWidth/2), (size / 2));
+    if (props.configuration.shapes?.shape == 'circle') {
+      graphics.drawCircle(size / 2 + lineWidth / 2, size / 2 + lineWidth / 2, size / 2);
     } else {
       graphics.drawRect(lineWidth, lineWidth, size - lineWidth, size - lineWidth);
     }
     graphics.endFill();
-    
+
     app.renderer.render(graphics, { renderTexture });
 
     return renderTexture;
-  }
-  
+  };
+
   // Pixi viewport zoom scale, but discretized to single decimal.
   const [responsiveScale, setResponsiveScale] = useState(1);
 
@@ -147,7 +149,8 @@ export const NLPixi = (props: Props) => {
     if (graph.current.nodes.length > config.LABEL_MAX_NODES) return;
 
     // Change font size at specific scale intervals
-    const fontSize = (responsiveScale <= 0.1) ? 15 : (responsiveScale <= 0.2) ? 22.5 : (responsiveScale <= 0.4) ? 30 : (responsiveScale <= 0.6) ? 37.5 : 45;
+    const fontSize =
+      responsiveScale <= 0.1 ? 15 : responsiveScale <= 0.2 ? 22.5 : responsiveScale <= 0.4 ? 30 : responsiveScale <= 0.6 ? 37.5 : 45;
 
     const strokeWidth = fontSize / 2;
     linkLabelMap.current.forEach((text) => {
@@ -296,15 +299,15 @@ export const NLPixi = (props: Props) => {
     onZoom(event: FederatedPointerEvent) {
       const scale = viewport.current!.transform.scale.x;
 
-      if (scale > 2) {     
-        const scale  = 1 / viewport.current!.scale.x;  // starts from 0.5 down to 0.
-        setResponsiveScale((scale < 0.05) ? 0.1 : (scale < 0.1) ? 0.2 : (scale < 0.2) ? 0.4 : (scale < 0.3) ? 0.6 : 0.8);
+      if (scale > 2) {
+        const scale = 1 / viewport.current!.scale.x; // starts from 0.5 down to 0.
+        setResponsiveScale(scale < 0.05 ? 0.1 : scale < 0.1 ? 0.2 : scale < 0.2 ? 0.4 : scale < 0.3 ? 0.6 : 0.8);
       } else {
         setResponsiveScale(1);
       }
 
       if (graph.current.nodes.length < config.LABEL_MAX_NODES) {
-        linkLabelLayer.alpha = (scale > 2) ? Math.min(1, (scale - 2) * 3) : 0;
+        linkLabelLayer.alpha = scale > 2 ? Math.min(1, (scale - 2) * 3) : 0;
 
         if (linkLabelLayer.alpha > 0) {
           linkLabelLayer.renderable = true;
@@ -312,7 +315,7 @@ export const NLPixi = (props: Props) => {
           linkLabelLayer.renderable = false;
         }
 
-        nodeLabelLayer.alpha = (scale > 5) ? Math.min(1, (scale - 5) * 3) : 0;
+        nodeLabelLayer.alpha = scale > 5 ? Math.min(1, (scale - 5) * 3) : 0;
         if (nodeLabelLayer.alpha > 0) {
           nodeLabelLayer.renderable = true;
         } else {
@@ -377,7 +380,6 @@ export const NLPixi = (props: Props) => {
     const nodeMeta = props.graph.nodes[node._id];
     const texture = (gfx as any).selected ? selectedTexture : glyphTexture;
     gfx.texture = texture;
-    
 
     // Cluster colors
     if (nodeMeta?.cluster) {
@@ -405,8 +407,8 @@ export const NLPixi = (props: Props) => {
   };
 
   const getNodeLabel = (nodeMeta: NodeType) => {
-    return nodeMeta.label
-  }
+    return nodeMeta.label;
+  };
 
   const createNode = (node: NodeTypeD3, selected?: boolean) => {
     const nodeMeta = props.graph.nodes[node._id];
@@ -442,12 +444,12 @@ export const NLPixi = (props: Props) => {
 
     // Node label
     const attribute = getNodeLabel(nodeMeta);
-    const text = new Text(attribute, { 
+    const text = new Text(attribute, {
       fontSize: 20,
       fill: 0xffffff,
       wordWrap: true,
       wordWrapWidth: 65,
-      align: 'center'
+      align: 'center',
     });
     text.eventMode = 'none';
     text.cullable = true;
@@ -617,19 +619,18 @@ export const NLPixi = (props: Props) => {
     const nodeMeta = props.graph.nodes[node._id];
     const originalText = getNodeLabel(nodeMeta);
 
-    text.text = originalText;  // This is required to ensure the text size check (next line) works
+    text.text = originalText; // This is required to ensure the text size check (next line) works
 
-    if ((text.width/text.scale.x) <= 90 && (text.height/text.scale.y) <= 90) {
+    if (text.width / text.scale.x <= 90 && text.height / text.scale.y <= 90) {
       text.text = originalText;
     } else {
       // Change character limit at specific scale intervals
-      const charLimit = (responsiveScale > 0.2) ? 15 : (responsiveScale > 0.1) ? 30 : 75;
-      text.text = `${ originalText.slice(0, charLimit)}…`;
+      const charLimit = responsiveScale > 0.2 ? 15 : responsiveScale > 0.1 ? 30 : 75;
+      text.text = `${originalText.slice(0, charLimit)}…`;
     }
 
-    text.alpha = ((text.width/text.scale.x) <= 90 && (text.height/text.scale.y) <= 90) ? 1 : 0;
-  }
-
+    text.alpha = text.width / text.scale.x <= 90 && text.height / text.scale.y <= 90 ? 1 : 0;
+  };
 
   // const text = linkLabelMap.current.get(link._id);
   //   if (!text) return;
@@ -662,7 +663,7 @@ export const NLPixi = (props: Props) => {
       nodeLabelLayer.removeChildren();
 
       const layout = layoutAlgorithm.current as GraphologyForceAtlas2Webworker;
-      if(layout?.cleanup != null) layout.cleanup();
+      if (layout?.cleanup != null) layout.cleanup();
     };
   }, []);
 
@@ -908,7 +909,9 @@ export const NLPixi = (props: Props) => {
         <Tooltip key={popup.node._id} open={true} interactive={!dragging} boundaryElement={ref} showArrow={true}>
           <TooltipTrigger x={popup.pos.x} y={popup.pos.y} />
           <TooltipContent>
-            <NLPopup onClose={() => {}} data={{ node: props.graph.nodes[popup.node._id], pos: popup.pos }} key={popup.node._id} />
+            <VisualizationTooltip name={popup.node._id} colorHeader={nodeColorHex(props.graph.nodes[popup.node._id].type)}>
+              <NLPopUp data={props.graph.nodes[popup.node._id].attributes} />
+            </VisualizationTooltip>
           </TooltipContent>
         </Tooltip>
       ))}
@@ -916,11 +919,9 @@ export const NLPixi = (props: Props) => {
         <Tooltip key={quickPopup.node._id} open={true} boundaryElement={ref} showArrow={true}>
           <TooltipTrigger x={quickPopup.pos.x} y={quickPopup.pos.y} />
           <TooltipContent>
-            <NLPopup
-              onClose={() => {}}
-              data={{ node: props.graph.nodes[quickPopup.node._id], pos: quickPopup.pos }}
-              key={quickPopup.node._id}
-            />
+            <VisualizationTooltip name={quickPopup.node._id} colorHeader={nodeColorHex(props.graph.nodes[quickPopup.node._id].type)}>
+              <NLPopUp data={props.graph.nodes[quickPopup.node._id].attributes} />
+            </VisualizationTooltip>
           </TooltipContent>
         </Tooltip>
       )}
diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
index db7f37d0d..07d23ad2c 100644
--- a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
@@ -1,44 +1,43 @@
-import { IPointData } from 'pixi.js';
-import { NodeType } from '../types';
+import React from 'react';
+import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
 
-export type NodelinkPopupProps = {
-  data: { node: NodeType; pos: IPointData };
-  onClose: () => void;
+const formatNumber = (number: number) => {
+  return number.toLocaleString('de-DE');
 };
 
-export const NLPopup = (props: NodelinkPopupProps) => {
-  const node = props.data.node;
+export type NLPopUpProps = {
+  data: Record<string, any>;
+};
 
+export const NLPopUp: React.FC<NLPopUpProps> = ({ data }) => {
   return (
-    <div
-      className="text-[0.9rem] min-w-[10rem]"
-    >
-      <div className="card-body p-0">
-        <span className="px-2.5 pt-2">
-          <span>Node</span>
-          <span className="float-right">{node._id}</span>
-        </span>
-        <div className="h-[1px] w-full bg-secondary-200"></div>
-        <div className="px-2.5 text-[0.8rem]">
-          {node.attributes &&
-            Object.entries(node.attributes).map(([k, v], i) => {
-              return (
-                <div key={k} className="flex flex-row gap-3">
-                  <span className="">{k}: </span>
-                  <span className="ml-auto max-w-[10rem] text-right truncate">
-                    <span title={JSON.stringify(v)}>{JSON.stringify(v)}</span>
-                  </span>
-                </div>
-              );
-            })}
-          {node.cluster && (
-            <p>
-              Cluster: <span className="float-right">{node.cluster}</span>
-            </p>
-          )}
-        </div>
-        <div className="h-[1px] w-full"></div>
+    <TooltipProvider delayDuration={100}>
+      <div className={`px-2`}>
+        {Object.keys(data).length === 0 ? (
+          <div className="flex justify-center items-center h-full">
+            <span>No attributes</span>
+          </div>
+        ) : (
+          Object.entries(data).map(([k, v], index) => (
+            <div className="flex flex-row gap-1 items-center min-h-5" key={k}>
+              <span className={`font-semibold truncate min-w-[40%]`}>{k}</span>
+              <span className="ml-auto text-right truncate grow-1 flex items-center">
+                {v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? (
+                  <span className="ml-auto text-right truncate">{typeof v === 'number' ? formatNumber(v) : v.toString()}</span>
+                ) : (
+                  <div
+                    className={`ml-auto mt-auto h-4 w-12 border-[1px] solid border-gray`}
+                    style={{
+                      background:
+                        'repeating-linear-gradient(-45deg, transparent, transparent 6px, #eaeaea 6px, #eaeaea 8px), linear-gradient(to bottom, transparent, transparent)',
+                    }}
+                  ></div>
+                )}
+              </span>
+            </div>
+          ))
+        )}
       </div>
-    </div>
+    </TooltipProvider>
   );
 };
-- 
GitLab