diff --git a/Gruntfile.js b/Gruntfile.js index 727a037cfa..59cf8227f6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -88,7 +88,8 @@ module.exports = function(grunt) { files: { 'dist/js/stacks.js': [ 'node_modules/stimulus/dist/stimulus.umd.js', - 'node_modules/@popperjs/core/dist/umd/popper.js', + 'node_modules/@floating-ui/core/dist/floating-ui.core.min.js', + 'node_modules/@floating-ui/dom/dist/floating-ui.dom.min.js', 'build/lib/ts/stacks.js', 'build/lib/ts/controllers/**/*.js', 'build/lib/ts/finalize.js' diff --git a/lib/ts/controllers/s-popover.ts b/lib/ts/controllers/s-popover.ts index 56f5b8ca82..e22fe66428 100644 --- a/lib/ts/controllers/s-popover.ts +++ b/lib/ts/controllers/s-popover.ts @@ -4,7 +4,7 @@ namespace Stacks { export abstract class BasePopoverController extends StacksController { // @ts-ignore - private popper!: Popper; + private floatingUI!: FloatingUICore; protected popoverElement!: HTMLElement; @@ -71,7 +71,7 @@ namespace Stacks { this.validate(); if (this.isVisible) { // just call initialize here, not show. This keeps already visible popovers from adding/firing document events - this.initializePopper(); + this.initializeFloatingUI(); } else if (this.data.get("auto-show") === "true") { this.show(null); } @@ -84,9 +84,10 @@ namespace Stacks { */ disconnect() { this.hide(); - if (this.popper) { - this.popper.destroy(); - delete this.popper; + if (this.floatingUI) { + // TODO: See if something similar is still necessary + // this.floatingUI.destroy(); + delete this.floatingUI; } super.disconnect(); } @@ -110,8 +111,8 @@ namespace Stacks { dispatcher: dispatcherElement }).defaultPrevented) { return; } - if (!this.popper) { - this.initializePopper(); + if (!this.floatingUI) { + this.initializeFloatingUI(); } this.popoverElement!.classList.add("is-visible"); @@ -136,10 +137,11 @@ namespace Stacks { this.popoverElement.classList.remove("is-visible"); - if (this.popper) { + if (this.floatingUI) { + // TODO: See if something similar is still necessary // completely destroy the popper on hide; this is in line with Popper.js's performance recommendations - this.popper.destroy(); - delete this.popper; + // this.floatingUI.destroy(); + delete this.floatingUI; } // on first interaction, hide-on-outside-click with value "after-dismissal" reverts to the default behavior @@ -180,24 +182,43 @@ namespace Stacks { /** * Initializes the Popper for this instance */ - private initializePopper() { + private initializeFloatingUI() { + var popoverElement = this.popoverElement; + var arrowElement = popoverElement.querySelector(".s-popover--arrow"); // @ts-ignore - this.popper = Popper.createPopper(this.referenceElement, this.popoverElement, { + this.floatingUI = FloatingUIDOM.computePosition(this.referenceElement, popoverElement, { placement: this.data.get("placement") || "bottom", - modifiers: [ - { - name: "offset", - options: { - offset: [0, 10], // The entire popover should be 10px away from the element - } - }, - { - name: "arrow", - options: { - element: ".s-popover--arrow" - }, - }, - ] + middleware: [ + // @ts-ignore + FloatingUIDOM.offset(10), FloatingUIDOM.flip(), FloatingUIDOM.shift({ padding: 10 }), + // @ts-ignore + FloatingUIDOM.arrow({ element: arrowElement }), + ], + // @ts-ignore + }).then(({middlewareData, placement, x, y}) => { + const {x: arrowX, y: arrowY} = middlewareData.arrow; + + Object.assign(popoverElement.style, { + left: `${x}px`, + top: `${y}px`, + }); + + // @ts-ignore + const staticSide = { + top: 'bottom', + right: 'left', + bottom: 'top', + left: 'right', + }[placement.split('-')[0]]; + + if (arrowElement) { + // @ts-ignore + Object.assign(arrowElement.style, { + // TODO: Fix arrow positioning + transform: `translateX(${(arrowX || 0)}px) translateY(${(arrowY || 0)}px)`, + [staticSide]: '-4px', + }); + } }); } @@ -262,8 +283,9 @@ namespace Stacks { * Schedules the popover to update on the next animation frame if visible */ protected scheduleUpdate() { - if (this.popper && this.isVisible) { - this.popper.update(); + if (this.floatingUI && this.isVisible) { + // TODO reimplement update + // this.floatingUI.update(); } } } diff --git a/package-lock.json b/package-lock.json index 2ce2f587ea..d75a4a0b4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.75.0", "license": "MIT", "dependencies": { - "@popperjs/core": "^2.11.3", + "@floating-ui/dom": "^0.4.1", "stimulus": "^2.0.0" }, "devDependencies": { @@ -285,6 +285,19 @@ "node": ">=6.9.0" } }, + "node_modules/@floating-ui/core": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.6.1.tgz", + "integrity": "sha512-Y30eVMcZva8o84c0HcXAtDO4BEzPJMvF6+B7x7urL2xbAqVsGJhojOyHLaoQHQYjb6OkqRq5kO+zeySycQwKqg==" + }, + "node_modules/@floating-ui/dom": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.4.1.tgz", + "integrity": "sha512-VAdIkn+zc4VB3pSemBk2JwjXlbVEBUbQft4V5jGpUeA9Vxd1fQD5eL3dMIMLVW003NYCOoW42JjjyBPNTpr8uQ==", + "dependencies": { + "@floating-ui/core": "^0.6.1" + } + }, "node_modules/@highlightjs/cdn-assets": { "version": "11.5.0", "resolved": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.5.0.tgz", @@ -358,6 +371,7 @@ "version": "2.11.3", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==", + "dev": true, "hasInstallScript": true, "funding": { "type": "opencollective", @@ -11671,6 +11685,19 @@ "to-fast-properties": "^2.0.0" } }, + "@floating-ui/core": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.6.1.tgz", + "integrity": "sha512-Y30eVMcZva8o84c0HcXAtDO4BEzPJMvF6+B7x7urL2xbAqVsGJhojOyHLaoQHQYjb6OkqRq5kO+zeySycQwKqg==" + }, + "@floating-ui/dom": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.4.1.tgz", + "integrity": "sha512-VAdIkn+zc4VB3pSemBk2JwjXlbVEBUbQft4V5jGpUeA9Vxd1fQD5eL3dMIMLVW003NYCOoW42JjjyBPNTpr8uQ==", + "requires": { + "@floating-ui/core": "^0.6.1" + } + }, "@highlightjs/cdn-assets": { "version": "11.5.0", "resolved": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.5.0.tgz", @@ -11730,7 +11757,8 @@ "@popperjs/core": { "version": "2.11.3", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", - "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==" + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==", + "dev": true }, "@sindresorhus/slugify": { "version": "1.1.2", diff --git a/package.json b/package.json index 4041f0f344..f790c82a5f 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "license": "MIT", "dependencies": { - "@popperjs/core": "^2.11.3", + "@floating-ui/dom": "^0.4.1", "stimulus": "^2.0.0" }, "devDependencies": {