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",