diff --git a/package.json b/package.json index a1f14593..be8aa81f 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.29.3", + "version": "4.30.0", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", @@ -62,6 +62,7 @@ }, "dependencies": { "@ffz/icu-msgparser": "^2.0.0", + "@popperjs/core": "^2.10.2", "crypto-js": "^3.3.0", "dayjs": "^1.10.7", "displacejs": "^1.4.1", @@ -74,7 +75,6 @@ "markdown-it-link-attributes": "^3.0.0", "mnemonist": "^0.38.5", "path-to-regexp": "^3.2.0", - "popper.js": "^1.16.1", "raven-js": "^3.27.2", "react": "^17.0.2", "safe-regex": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cb6f3c9..346e34a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,7 @@ specifiers: '@babel/plugin-transform-react-jsx': ^7.16.0 '@ffz/fontello-cli': ^1.0.4 '@ffz/icu-msgparser': ^2.0.0 + '@popperjs/core': ^2.10.2 '@webpack-cli/serve': ^1.6.0 babel-loader: ^8.2.3 clean-webpack-plugin: ^3.0.0 @@ -41,7 +42,6 @@ specifiers: markdown-it-link-attributes: ^3.0.0 mnemonist: ^0.38.5 path-to-regexp: ^3.2.0 - popper.js: ^1.16.1 raven-js: ^3.27.2 raw-loader: ^3.1.0 react: ^17.0.2 @@ -69,6 +69,7 @@ specifiers: dependencies: '@ffz/icu-msgparser': 2.0.0 + '@popperjs/core': 2.10.2 crypto-js: 3.3.0 dayjs: 1.10.7 displacejs: 1.4.1 @@ -81,7 +82,6 @@ dependencies: markdown-it-link-attributes: 3.0.0 mnemonist: 0.38.5 path-to-regexp: 3.2.0 - popper.js: 1.16.1 raven-js: 3.27.2 react: 17.0.2 safe-regex: 2.1.1 @@ -616,6 +616,10 @@ packages: rimraf: 3.0.2 dev: true + /@popperjs/core/2.10.2: + resolution: {integrity: sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==} + dev: false + /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: @@ -4463,11 +4467,6 @@ packages: find-up: 4.1.0 dev: true - /popper.js/1.16.1: - resolution: {integrity: sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==} - deprecated: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1 - dev: false - /portfinder/1.0.28: resolution: {integrity: sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==} engines: {node: '>= 0.12.0'} diff --git a/src/clips.js b/src/clips.js index 91a4ad16..a720da2f 100644 --- a/src/clips.js +++ b/src/clips.js @@ -148,7 +148,7 @@ FrankerFaceZ.utilities = { tooltip: require('utilities/tooltip'), i18n: require('utilities/translation-core'), dayjs: require('dayjs'), - popper: require('popper.js').default + popper: require('@popperjs/core') } diff --git a/src/main.js b/src/main.js index ff108559..073f8036 100644 --- a/src/main.js +++ b/src/main.js @@ -185,7 +185,7 @@ FrankerFaceZ.utilities = { i18n: require('utilities/translation-core'), dayjs: require('dayjs'), filtering: require('utilities/filtering'), - popper: require('popper.js').default + popper: require('@popperjs/core') } diff --git a/src/modules/tooltips.js b/src/modules/tooltips.js index fc409c0a..af4aba2b 100644 --- a/src/modules/tooltips.js +++ b/src/modules/tooltips.js @@ -113,12 +113,7 @@ export default class TooltipProvider extends Module { popper: { placement: 'top', modifiers: { - flip: { - behavior: ['top', 'bottom', 'left', 'right'] - }, - preventOverflow: { - boundariesElement: container - } + flip: {} } }, diff --git a/src/player.js b/src/player.js index 431d01d6..71e6a7b3 100644 --- a/src/player.js +++ b/src/player.js @@ -143,7 +143,7 @@ FrankerFaceZ.utilities = { tooltip: require('utilities/tooltip'), i18n: require('utilities/translation-core'), dayjs: require('dayjs'), - popper: require('popper.js').default + popper: require('@popperjs/core') } diff --git a/src/sites/twitch-twilight/modules/chat/line.js b/src/sites/twitch-twilight/modules/chat/line.js index 15ff17e1..4ba4bf06 100644 --- a/src/sites/twitch-twilight/modules/chat/line.js +++ b/src/sites/twitch-twilight/modules/chat/line.js @@ -432,7 +432,7 @@ other {# messages were deleted by a moderator.} const u = t.site.getUser(), r = {id: room_id, login: room}; - const has_replies = this.chatRepliesTreatment ? this.chatRepliesTreatment !== 'control' : false, + const has_replies = this.props && !!(this.props.hasReply || this.props.reply || ! this.props.replyRestrictedReason), can_replies = has_replies && msg.message && ! msg.deleted && ! this.props.disableReplyClick, can_reply = can_replies && u && u.login !== msg.user?.login && ! msg.reply, twitch_clickable = reply_mode === 1 && can_replies && (!!msg.reply || can_reply); diff --git a/src/sites/twitch-twilight/modules/directory/following.jsx b/src/sites/twitch-twilight/modules/directory/following.jsx index 44eda4de..99ce9efc 100644 --- a/src/sites/twitch-twilight/modules/directory/following.jsx +++ b/src/sites/twitch-twilight/modules/directory/following.jsx @@ -8,7 +8,7 @@ import {SiteModule} from 'utilities/module'; import {createElement} from 'utilities/dom'; import {get} from 'utilities/object'; -import Popper from 'popper.js'; +import {createPopper} from '@popperjs/core'; import {makeReference} from 'utilities/tooltip'; export default class Following extends SiteModule { @@ -208,7 +208,7 @@ export default class Following extends SiteModule { const root = (document.body.querySelector('#root>div') || document.body); root.appendChild(this.hostMenu); - this.hostMenuPopper = new Popper( + this.hostMenuPopper = createPopper( makeReference(event.clientX - 60, event.clientY - 60), this.hostMenu, { diff --git a/src/utilities/dom.js b/src/utilities/dom.js index 718610fd..b90bb735 100644 --- a/src/utilities/dom.js +++ b/src/utilities/dom.js @@ -142,42 +142,33 @@ export function createElement(tag, props, ...children) { } export function setChildren(el, children, no_sanitize, no_empty) { - if ( typeof children === 'string' ) { - if ( no_empty ) { - el.appendChild(no_sanitize ? - range.createContextualFragment(children) : - document.createTextNode(children) - ) - - } else { - if ( no_sanitize ) - el.innerHTML = children; - else - el.textContent = children; - } - - } else if ( Array.isArray(children) ) { - if ( ! no_empty ) - el.innerHTML = ''; - - for(const child of children) - if ( typeof child === 'string' ) - el.appendChild(no_sanitize ? - range.createContextualFragment(child) : - document.createTextNode(child) - ); - - else if ( Array.isArray(child) ) - setChildren(el, child, no_sanitize, true); - - else if ( child ) - el.appendChild(child); - - } else if ( children ) { - if ( ! no_empty ) + if (children instanceof Node ) { + if (! no_empty ) el.innerHTML = ''; el.appendChild(children); + + } else if ( Array.isArray(children) ) { + if (! no_empty) + el.innerHTML = ''; + + for(const child of children) + if (child instanceof Node) + el.appendChild(child); + else if (Array.isArray(child)) + setChildren(el, child, no_sanitize, true); + else if (child) { + const val = typeof child === 'string' ? child : String(child); + + el.appendChild(no_sanitize ? + range.createContextualFragment(val) : document.createTextNode(val)); + } + + } else if (children) { + const val = typeof children === 'string' ? children : String(children); + + el.appendChild(no_sanitize ? + range.createContextualFragment(val) : document.createTextNode(val)); } } diff --git a/src/utilities/tooltip.js b/src/utilities/tooltip.js index e9c43d8c..e00f645c 100644 --- a/src/utilities/tooltip.js +++ b/src/utilities/tooltip.js @@ -9,9 +9,9 @@ // ============================================================================ import {createElement, setChildren} from 'utilities/dom'; -import {maybe_call, debounce} from 'utilities/object'; +import {maybe_call, debounce, has} from 'utilities/object'; -import Popper from 'popper.js'; +import {createPopper} from '@popperjs/core'; let last_id = 0; @@ -347,15 +347,24 @@ export class Tooltip { const use_html = maybe_call(opts.html, null, target, tip), setter = use_html ? 'innerHTML' : 'textContent'; - let pop_opts = Object.assign({ - modifiers: { - flip: { - behavior: ['top', 'bottom', 'left', 'right'] - } + const modifiers = { + flip: { + behavior: ['top', 'bottom', 'left', 'right'] }, - arrowElement: arrow + offset: { + offset: [0, 10] + }, + arrow: { + element: arrow + } + }; + + let pop_opts = Object.assign({ + modifiers }, opts.popper); + pop_opts.modifiers = Object.assign(modifiers, pop_opts.modifiers); + if ( opts.popperConfig ) pop_opts = opts.popperConfig(target, tip, pop_opts) ?? pop_opts; @@ -370,9 +379,7 @@ export class Tooltip { tip._update = () => { if ( tip.popper ) { - tip.popper.update(); - /*tip.popper.destroy(); - tip.popper = new Popper(popper_target, el, pop_opts);*/ + tip.popper.forceUpdate(); } } @@ -413,9 +420,11 @@ export class Tooltip { inner[setter] = content; } + // Format the modifiers how Popper wants them now. + pop_opts.modifiers = normalizeModifiers(pop_opts.modifiers); // Add everything to the DOM and create the Popper instance. - tip.popper = new Popper(popper_target, el, pop_opts); + tip.popper = createPopper(popper_target, el, pop_opts); this.container.appendChild(el); tip.visible = true; @@ -465,6 +474,30 @@ export class Tooltip { export default Tooltip; +export function normalizeModifiers(input) { + const output = []; + + for(const [key, val] of Object.entries(input)) { + const thing = { + name: key + }; + + if (val && typeof val === 'object' && ! Array.isArray(val)) { + if (has(val, 'enabled')) + thing.enabled = val.enabled; + + const keys = Object.keys(val); + if (keys.length > 1 || (keys.length === 1 && keys[0] !== 'enabled')) + thing.options = val; + } + + output.push(thing); + } + + return output; +} + + export function makeReference(x, y, height=0, width=0) { if ( x instanceof Node ) { const rect = x.getBoundingClientRect(); diff --git a/styles/tooltips.scss b/styles/tooltips.scss index 19dbbb4a..845af422 100644 --- a/styles/tooltips.scss +++ b/styles/tooltips.scss @@ -34,7 +34,7 @@ body { .ffz-balloon { - &[x-placement^="bottom"] > .ffz-balloon__tail { + &[data-popper-placement^="bottom"] > .ffz-balloon__tail { bottom: 100%; .ffz-balloon__tail-symbol { @@ -44,7 +44,7 @@ body { } } - &[x-placement^="top"] > .ffz-balloon__tail { + &[data-popper-placement^="top"] > .ffz-balloon__tail { top: 100%; .ffz-balloon__tail-symbol { @@ -54,7 +54,7 @@ body { } } - &[x-placement^="right"] > .ffz-balloon__tail { + &[data-popper-placement^="right"] > .ffz-balloon__tail { right: 100%; .ffz-balloon__tail-symbol { @@ -64,7 +64,7 @@ body { } } - &[x-placement^="left"] > .ffz-balloon__tail { + &[data-popper-placement^="left"] > .ffz-balloon__tail { left: 100%; .ffz-balloon__tail-symbol { @@ -110,35 +110,40 @@ body { .ffz__tooltip--arrow { position: absolute; - width: 6px; height: 6px; - transform: rotate(45deg); - z-index: -1; - background: var(--color-background-tooltip); + &:before { + content: ''; + position: absolute; + top: 0; left: 0; + width: 6px; height: 6px; + transform: rotate(45deg); + background: var(--color-background-tooltip); + z-index: -1; + } } - &[x-placement^="bottom"] { + &[data-popper-placement^="bottom"] { .ffz__tooltip--arrow { top: -3px; border-radius: 2px 0 0; } } - &[x-placement^="top"] { + &[data-popper-placement^="top"] { .ffz__tooltip--arrow { - bottom: -3px; + bottom: 3px; border-radius: 0 0 2px; } } - &[x-placement^="right"] { + &[data-popper-placement^="right"] { .ffz__tooltip--arrow { left: -3px; border-radius: 0 2px 0 0; } } - &[x-placement^="left"] { + &[data-popper-placement^="left"] { .ffz__tooltip--arrow { right: -3px; border-radius: 0 0 0 2px; diff --git a/webpack.web.dev.js b/webpack.web.dev.js index 2f4795df..335b3c51 100644 --- a/webpack.web.dev.js +++ b/webpack.web.dev.js @@ -27,6 +27,11 @@ module.exports = merge(common, { ], devServer: { + client: false, + webSocketServer: false, + + magicHtml: false, + liveReload: false, hot: false, https: true, port: 8000,