From 6d8e0c60c2dffbabf850da0317962045c7dbed28 Mon Sep 17 00:00:00 2001 From: Dan Cormier Date: Tue, 15 Mar 2022 18:52:32 -0400 Subject: [PATCH 1/3] Rough swap of popperjs with floating-ui https://github.com/StackExchange/Stacks/issues/872 --- Gruntfile.js | 2 +- lib/ts/controllers/s-popover.ts | 64 +++++++++++++++++---------------- package-lock.json | 32 +++++++++++++++-- package.json | 2 +- 4 files changed, 66 insertions(+), 34 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 727a037cfa..4ba8410f4e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -88,7 +88,7 @@ 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.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..de47ac3cdd 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,9 @@ namespace Stacks { */ disconnect() { this.hide(); - if (this.popper) { - this.popper.destroy(); - delete this.popper; + if (this.floatingUI) { + this.floatingUI.destroy(); + delete this.floatingUI; } super.disconnect(); } @@ -110,8 +110,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 +136,10 @@ namespace Stacks { this.popoverElement.classList.remove("is-visible"); - if (this.popper) { + if (this.floatingUI) { // 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,25 +180,29 @@ namespace Stacks { /** * Initializes the Popper for this instance */ - private initializePopper() { + private initializeFloatingUI() { // @ts-ignore - this.popper = Popper.createPopper(this.referenceElement, this.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" - }, - }, - ] - }); + // this.floatingUI = FloatingUICore.computePosition(this.refrenceElement, this.popoverElement, { + // // @ts-ignore + // middleware: [ FloatingUICore.offset(), FloatingUICore.flip(), FloatingUICore.shift() ] + // }); + // this.floatingUI = FloatingUICore.createPopper(this.referenceElement, this.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" + // }, + // }, + // ] + // }); } /** @@ -262,8 +266,8 @@ 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) { + 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": { From 232f47b8108404d2b4275a636c97321bffb6357f Mon Sep 17 00:00:00 2001 From: Dan Cormier Date: Thu, 17 Mar 2022 19:05:28 -0400 Subject: [PATCH 2/3] Use FloatingUIDOM; implement popover middleware --- Gruntfile.js | 3 +- lib/ts/controllers/s-popover.ts | 66 +++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4ba8410f4e..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/@floating-ui/core/dist/floating-ui.core.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 de47ac3cdd..6811364efd 100644 --- a/lib/ts/controllers/s-popover.ts +++ b/lib/ts/controllers/s-popover.ts @@ -85,7 +85,8 @@ namespace Stacks { disconnect() { this.hide(); if (this.floatingUI) { - this.floatingUI.destroy(); + // TODO: See if something similar is still necessary + // this.floatingUI.destroy(); delete this.floatingUI; } super.disconnect(); @@ -137,8 +138,9 @@ namespace Stacks { this.popoverElement.classList.remove("is-visible"); 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.floatingUI.destroy(); + // this.floatingUI.destroy(); delete this.floatingUI; } @@ -181,28 +183,43 @@ namespace Stacks { * Initializes the Popper for this instance */ private initializeFloatingUI() { + var popoverElement = this.popoverElement; + var arrowElement = popoverElement.querySelector(".s-popover--arrow"); // @ts-ignore - // this.floatingUI = FloatingUICore.computePosition(this.refrenceElement, this.popoverElement, { - // // @ts-ignore - // middleware: [ FloatingUICore.offset(), FloatingUICore.flip(), FloatingUICore.shift() ] - // }); - // this.floatingUI = FloatingUICore.createPopper(this.referenceElement, this.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" - // }, - // }, - // ] - // }); + this.floatingUI = FloatingUIDOM.computePosition(this.referenceElement, popoverElement, { + placement: this.data.get("placement") || "bottom", + middleware: [ + // @ts-ignore + FloatingUIDOM.offset(10), FloatingUIDOM.flip(), FloatingUIDOM.shift({ padding: 10 }), + // @ts-ignore + FloatingUIDOM.arrow({ element: arrowEl }), + ], + // @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', + }); + } + }); } /** @@ -267,7 +284,8 @@ namespace Stacks { */ protected scheduleUpdate() { if (this.floatingUI && this.isVisible) { - this.floatingUI.update(); + // TODO reimplement update + // this.floatingUI.update(); } } } From 3d524006e556826faca5a370fa71ecd8882aaffa Mon Sep 17 00:00:00 2001 From: Dan Cormier Date: Fri, 25 Mar 2022 10:41:49 -0400 Subject: [PATCH 3/3] Fix typo --- lib/ts/controllers/s-popover.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ts/controllers/s-popover.ts b/lib/ts/controllers/s-popover.ts index 6811364efd..e22fe66428 100644 --- a/lib/ts/controllers/s-popover.ts +++ b/lib/ts/controllers/s-popover.ts @@ -192,7 +192,7 @@ namespace Stacks { // @ts-ignore FloatingUIDOM.offset(10), FloatingUIDOM.flip(), FloatingUIDOM.shift({ padding: 10 }), // @ts-ignore - FloatingUIDOM.arrow({ element: arrowEl }), + FloatingUIDOM.arrow({ element: arrowElement }), ], // @ts-ignore }).then(({middlewareData, placement, x, y}) => {