diff --git a/core/static/bundled/core/tooltips-index.ts b/core/static/bundled/core/tooltips-index.ts
new file mode 100644
index 00000000..60908431
--- /dev/null
+++ b/core/static/bundled/core/tooltips-index.ts
@@ -0,0 +1,62 @@
+import { type Placement, computePosition } from "@floating-ui/dom";
+
+/**
+ * Library usage:
+ * Add a `tooltip` attribute to any html element with it's tooltip text
+ * You can control the position of the tooltp with the `position` attribute
+ * Allowed placements are `top`, `right`, `bottom`, `left`
+ * You can add `-start` and `-end` to all allowed placement values
+ **/
+
+function getPlacement(element: HTMLElement): Placement {
+ const position = element.getAttribute("position");
+ if (position) {
+ return position as Placement;
+ }
+ return "bottom";
+}
+
+function getTooltip(element: HTMLElement) {
+ for (const tooltip of document.body.getElementsByClassName("tooltip")) {
+ if (tooltip.textContent === element.getAttribute("tooltip")) {
+ return tooltip as HTMLElement;
+ }
+ }
+
+ const tooltip = document.createElement("div");
+ document.body.append(tooltip);
+ tooltip.classList.add("tooltip");
+ tooltip.innerText = element.getAttribute("tooltip");
+
+ return tooltip;
+}
+
+addEventListener("mouseover", (event: MouseEvent) => {
+ const target = event.target as HTMLElement;
+ if (!target.hasAttribute("tooltip")) {
+ return;
+ }
+
+ const tooltip = getTooltip(target);
+ tooltip.setAttribute("tooltip-status", "open");
+
+ computePosition(target, tooltip, {
+ placement: getPlacement(target),
+ }).then(({ x, y }) => {
+ Object.assign(tooltip.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ });
+ });
+
+ document.body.append(tooltip);
+});
+
+addEventListener("mouseout", (event: MouseEvent) => {
+ const target = event.target as HTMLElement;
+ if (!target.hasAttribute("tooltip")) {
+ return;
+ }
+
+ getTooltip(target).setAttribute("tooltip-status", "close");
+});
diff --git a/core/static/core/style.scss b/core/static/core/style.scss
index 58b188fc..ec962a2a 100644
--- a/core/static/core/style.scss
+++ b/core/static/core/style.scss
@@ -45,17 +45,10 @@ body {
}
}
-[tooltip] {
- position: relative;
-}
-
-[tooltip]::before {
+.tooltip {
@include shadow;
z-index: 1;
pointer-events: none;
- content: attr(tooltip);
- left: 50%;
- transform: translateX(-50%);
background-color: #333;
color: #fff;
border: 0.5px solid hsl(0, 0%, 50%);
@@ -64,44 +57,20 @@ body {
position: absolute;
white-space: nowrap;
opacity: 0;
- transition: opacity 500ms ease-out;
- top: 120%; // Put the tooltip under the element
+ transition: opacity 500ms ease-in;
+
+ position: absolute;
+ width: max-content;
+
+ left: 0;
+ top: 0;
}
-[tooltip]:hover::before {
+.tooltip[tooltip-status=open] {
opacity: 1;
transition: opacity 500ms ease-in;
}
-[no-hover][tooltip]::before {
- opacity: 1;
- transition: opacity 500ms ease-in;
-}
-
-[position="top"][tooltip]::before {
- top: initial;
- bottom: 120%;
-}
-
-[position="bottom"][tooltip]::before {
- top: 120%;
- bottom: initial;
-}
-
-[position="left"][tooltip]::before {
- top: initial;
- bottom: 0%;
- left: initial;
- right: 65%;
-}
-
-[position="right"][tooltip]::before {
- top: initial;
- bottom: 0%;
- left: 150%;
- right: initial;
-}
-
.ib {
display: inline-block;
padding: 1px;
diff --git a/core/templates/core/base.jinja b/core/templates/core/base.jinja
index 41b13398..832bc1b8 100644
--- a/core/templates/core/base.jinja
+++ b/core/templates/core/base.jinja
@@ -24,6 +24,7 @@
+
diff --git a/package-lock.json b/package-lock.json
index 541a492d..87d4bc5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"@alpinejs/sort": "^3.14.7",
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
+ "@floating-ui/dom": "^1.6.13",
"@fortawesome/fontawesome-free": "^6.6.0",
"@fullcalendar/core": "^6.1.15",
"@fullcalendar/daygrid": "^6.1.15",
@@ -2162,6 +2163,31 @@
"node": ">=18"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
+ "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.13",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
+ "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.6.0",
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
+ "license": "MIT"
+ },
"node_modules/@fortawesome/fontawesome-free": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz",
diff --git a/package.json b/package.json
index 94fa21fc..22b1d2c1 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"dependencies": {
"@alpinejs/sort": "^3.14.7",
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
+ "@floating-ui/dom": "^1.6.13",
"@fortawesome/fontawesome-free": "^6.6.0",
"@fullcalendar/core": "^6.1.15",
"@fullcalendar/daygrid": "^6.1.15",