refactor: replace popperjs with floating-ui

This commit is contained in:
Yassine Doghri 2021-12-31 09:42:52 +00:00
parent 02557539e6
commit ad5cd2c2e9
5 changed files with 115 additions and 56 deletions

View File

@ -1,4 +1,10 @@
import { createPopper, Instance, Placement } from "@popperjs/core"; import {
computePosition,
flip,
offset,
Placement,
shift,
} from "@floating-ui/dom";
const Dropdown = (): void => { const Dropdown = (): void => {
const dropdownButtons: NodeListOf<HTMLButtonElement> = const dropdownButtons: NodeListOf<HTMLButtonElement> =
@ -16,46 +22,46 @@ const Dropdown = (): void => {
// place the menu at then end of the body to prevent any overflow cuts // place the menu at then end of the body to prevent any overflow cuts
document.body.appendChild(menu); document.body.appendChild(menu);
let popperInstance: Instance | null = null; const update = () => {
const create = () => {
const offsetX = menu.dataset.dropdownOffsetX const offsetX = menu.dataset.dropdownOffsetX
? parseInt(menu.dataset.dropdownOffsetX) ? parseInt(menu.dataset.dropdownOffsetX)
: 0; : 0;
const offsetY = menu.dataset.dropdownOffsetY const offsetY = menu.dataset.dropdownOffsetY
? parseInt(menu.dataset.dropdownOffsetY) ? parseInt(menu.dataset.dropdownOffsetY)
: 0; : 0;
popperInstance = createPopper(button, menu, { computePosition(button, menu, {
placement: menu.dataset.dropdownPlacement as Placement, placement: menu.dataset.dropdownPlacement as Placement,
modifiers: [ middleware: [
{ offset({ mainAxis: offsetY, crossAxis: offsetX }),
name: "offset", flip(),
options: { shift(),
offset: [offsetX, offsetY],
},
},
], ],
}).then(({ x, y }) => {
Object.assign(menu.style, {
left: `${x}px`,
top: `${y}px`,
});
}); });
}; };
const destroy = () => { const showMenu = () => {
if (popperInstance) { menu.setAttribute("data-show", "");
popperInstance.destroy(); button.setAttribute("aria-expanded", "true");
popperInstance = null; update();
} };
const hideMenu = () => {
menu.removeAttribute("data-show");
button.setAttribute("aria-expanded", "false");
}; };
const dropdownToggle = () => { const dropdownToggle = () => {
const isExpanded = menu.hasAttribute("data-show"); const isExpanded = menu.hasAttribute("data-show");
if (isExpanded) { if (isExpanded) {
menu.removeAttribute("data-show"); hideMenu();
button.setAttribute("aria-expanded", "false");
destroy();
} else { } else {
menu.setAttribute("data-show", ""); showMenu();
button.setAttribute("aria-expanded", "true");
create();
} }
}; };

View File

@ -1,4 +1,12 @@
import { createPopper, Placement } from "@popperjs/core"; import { Coords } from "@floating-ui/core";
import {
arrow,
computePosition,
flip,
offset,
Placement,
shift,
} from "@floating-ui/dom";
const Tooltip = (): void => { const Tooltip = (): void => {
const tooltipContainers: NodeListOf<HTMLElement> = const tooltipContainers: NodeListOf<HTMLElement> =
@ -12,30 +20,60 @@ const Tooltip = (): void => {
tooltip.setAttribute("id", "tooltip" + i); tooltip.setAttribute("id", "tooltip" + i);
tooltip.setAttribute( tooltip.setAttribute(
"class", "class",
"px-2 py-1 text-sm bg-gray-900 text-white rounded max-w-xs z-50" "absolute px-2 py-1 text-sm bg-gray-900 text-white rounded max-w-xs z-50"
); );
tooltip.innerHTML = tooltipContent; tooltip.innerHTML = tooltipContent;
const arrowElement = document.createElement("div");
arrowElement.setAttribute(
"class",
"absolute bg-gray-900 w-2 h-2 rotate-45"
);
arrowElement.setAttribute("id", "arrow" + i);
tooltip.appendChild(arrowElement);
const popper = createPopper(tooltipReference, tooltip, { const update = () => {
computePosition(tooltipReference, tooltip, {
placement: tooltipReference.dataset.tooltip as Placement, placement: tooltipReference.dataset.tooltip as Placement,
modifiers: [ middleware: [
{ flip(),
name: "offset", shift(),
options: { offset(8),
offset: [0, 8], arrow({ element: arrowElement }),
},
},
], ],
}).then(({ x, y, placement, middlewareData }) => {
Object.assign(tooltip.style, {
left: `${x}px`,
top: `${y}px`,
}); });
const show = () => { // Accessing the data
const { x: arrowX, y: arrowY } = middlewareData.arrow as Coords;
const staticSide = {
top: "bottom",
right: "left",
bottom: "top",
left: "right",
}[placement.split("-")[0]];
Object.assign(arrowElement.style, {
left: arrowX != null ? `${arrowX}px` : "",
top: arrowY != null ? `${arrowY}px` : "",
right: "",
bottom: "",
[staticSide as string]: "-4px",
});
});
};
const showTooltip = () => {
tooltipReference.removeAttribute("title"); tooltipReference.removeAttribute("title");
tooltipReference.setAttribute("aria-describedby", "tooltip" + i); tooltipReference.setAttribute("aria-describedby", "tooltip" + i);
document.body.appendChild(tooltip); document.body.appendChild(tooltip);
popper.update(); update();
}; };
const hide = () => { const hideTooltip = () => {
const element = document.getElementById("tooltip" + i); const element = document.getElementById("tooltip" + i);
tooltipReference.removeAttribute("aria-describedby"); tooltipReference.removeAttribute("aria-describedby");
tooltipReference.setAttribute("title", tooltipContent); tooltipReference.setAttribute("title", tooltipContent);
@ -48,11 +86,11 @@ const Tooltip = (): void => {
const hideEvents = ["mouseleave", "blur"]; const hideEvents = ["mouseleave", "blur"];
showEvents.forEach((event) => { showEvents.forEach((event) => {
tooltipReference.addEventListener(event, show); tooltipReference.addEventListener(event, showTooltip);
}); });
hideEvents.forEach((event) => { hideEvents.forEach((event) => {
tooltipReference.addEventListener(event, hide); tooltipReference.addEventListener(event, hideTooltip);
}); });
} }
}; };

View File

@ -1,8 +1,8 @@
@layer base { @layer base {
[data-dropdown="menu"] { [data-dropdown="menu"] {
@apply z-50; @apply absolute z-50;
} }
[data-dropdown="menu"]:not([data-show]) { [data-dropdown="menu"]:not([data-show]) {
@apply absolute top-0 left-0 invisible pointer-events-none; @apply hidden;
} }
} }

39
package-lock.json generated
View File

@ -16,11 +16,11 @@
"@codemirror/lang-xml": "^0.19.2", "@codemirror/lang-xml": "^0.19.2",
"@codemirror/state": "^0.19.6", "@codemirror/state": "^0.19.6",
"@codemirror/view": "^0.19.32", "@codemirror/view": "^0.19.32",
"@floating-ui/dom": "^0.1.10",
"@github/clipboard-copy-element": "^1.1.2", "@github/clipboard-copy-element": "^1.1.2",
"@github/hotkey": "^1.6.1", "@github/hotkey": "^1.6.1",
"@github/markdown-toolbar-element": "^2.1.0", "@github/markdown-toolbar-element": "^2.1.0",
"@github/time-elements": "^3.1.2", "@github/time-elements": "^3.1.2",
"@popperjs/core": "^2.11.0",
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
"@vime/core": "^5.3.0", "@vime/core": "^5.3.0",
"choices.js": "^9.0.1", "choices.js": "^9.0.1",
@ -2355,6 +2355,19 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.3.1.tgz",
"integrity": "sha512-ensKY7Ub59u16qsVIFEo2hwTCqZ/r9oZZFh51ivcLGHfUwTn8l1Xzng8RJUe91H/UP8PeqeBronAGx0qmzwk2g=="
},
"node_modules/@floating-ui/dom": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.1.10.tgz",
"integrity": "sha512-4kAVoogvQm2N0XE0G6APQJuCNuErjOfPW8Ux7DFxh8+AfugWflwVJ5LDlHOwrwut7z/30NUvdtHzQ3zSip4EzQ==",
"dependencies": {
"@floating-ui/core": "^0.3.0"
}
},
"node_modules/@foliojs-fork/fontkit": { "node_modules/@foliojs-fork/fontkit": {
"version": "1.9.1", "version": "1.9.1",
"license": "MIT", "license": "MIT",
@ -2606,14 +2619,6 @@
"@octokit/openapi-types": "^11.2.0" "@octokit/openapi-types": "^11.2.0"
} }
}, },
"node_modules/@popperjs/core": {
"version": "2.11.0",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
@ -18285,6 +18290,19 @@
} }
} }
}, },
"@floating-ui/core": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.3.1.tgz",
"integrity": "sha512-ensKY7Ub59u16qsVIFEo2hwTCqZ/r9oZZFh51ivcLGHfUwTn8l1Xzng8RJUe91H/UP8PeqeBronAGx0qmzwk2g=="
},
"@floating-ui/dom": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.1.10.tgz",
"integrity": "sha512-4kAVoogvQm2N0XE0G6APQJuCNuErjOfPW8Ux7DFxh8+AfugWflwVJ5LDlHOwrwut7z/30NUvdtHzQ3zSip4EzQ==",
"requires": {
"@floating-ui/core": "^0.3.0"
}
},
"@foliojs-fork/fontkit": { "@foliojs-fork/fontkit": {
"version": "1.9.1", "version": "1.9.1",
"requires": { "requires": {
@ -18491,9 +18509,6 @@
"@octokit/openapi-types": "^11.2.0" "@octokit/openapi-types": "^11.2.0"
} }
}, },
"@popperjs/core": {
"version": "2.11.0"
},
"@rollup/plugin-babel": { "@rollup/plugin-babel": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",

View File

@ -34,11 +34,11 @@
"@codemirror/lang-xml": "^0.19.2", "@codemirror/lang-xml": "^0.19.2",
"@codemirror/state": "^0.19.6", "@codemirror/state": "^0.19.6",
"@codemirror/view": "^0.19.32", "@codemirror/view": "^0.19.32",
"@floating-ui/dom": "^0.1.10",
"@github/clipboard-copy-element": "^1.1.2", "@github/clipboard-copy-element": "^1.1.2",
"@github/hotkey": "^1.6.1", "@github/hotkey": "^1.6.1",
"@github/markdown-toolbar-element": "^2.1.0", "@github/markdown-toolbar-element": "^2.1.0",
"@github/time-elements": "^3.1.2", "@github/time-elements": "^3.1.2",
"@popperjs/core": "^2.11.0",
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
"@vime/core": "^5.3.0", "@vime/core": "^5.3.0",
"choices.js": "^9.0.1", "choices.js": "^9.0.1",