mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.3.2
* Fixed: Maintain the mouse hover state for chat when moving the mouse over the context menu from an in-line chat action. * Fixed: Do not allow mouse interaction with the notice at the bottom of chat when chat if frozen, ensuring that users can interact with the bottom chat line. * Fixed: Block and Hide Thumbnails buttons not appearing on Directory pages. * API Fixed: `deep_copy()` converting `null` into `{}`. * API Changed: Tooltips can now emit hover events.
This commit is contained in:
parent
21ee6fcfb7
commit
734a73eb0e
9 changed files with 119 additions and 26 deletions
|
@ -151,7 +151,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 3, revision: 1,
|
major: 4, minor: 3, revision: 2,
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
|
@ -331,6 +331,7 @@ export default class Actions extends Module {
|
||||||
manual: true,
|
manual: true,
|
||||||
live: false,
|
live: false,
|
||||||
html: true,
|
html: true,
|
||||||
|
hover_events: true,
|
||||||
|
|
||||||
tooltipClass: 'ffz-action-balloon tw-balloon tw-block tw-border tw-elevation-1 tw-border-radius-small tw-c-background-base',
|
tooltipClass: 'ffz-action-balloon tw-balloon tw-block tw-border tw-elevation-1 tw-border-radius-small tw-c-background-base',
|
||||||
arrowClass: 'tw-balloon__tail tw-overflow-hidden tw-absolute',
|
arrowClass: 'tw-balloon__tail tw-overflow-hidden tw-absolute',
|
||||||
|
@ -350,10 +351,18 @@ export default class Actions extends Module {
|
||||||
},
|
},
|
||||||
|
|
||||||
content,
|
content,
|
||||||
onShow: (t, tip) =>
|
onShow: async (t, tip) => {
|
||||||
setTimeout(() => {
|
await tip.waitForDom();
|
||||||
target._ffz_outside = new ClickOutside(tip.outer, destroy)
|
target._ffz_outside = new ClickOutside(tip.outer, destroy)
|
||||||
}),
|
},
|
||||||
|
|
||||||
|
onMove: (target, tip, event) => {
|
||||||
|
this.emit('tooltips:mousemove', target, tip, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onLeave: (target, tip, event) => {
|
||||||
|
this.emit('tooltips:leave', target, tip, event);
|
||||||
|
},
|
||||||
|
|
||||||
onHide: destroy
|
onHide: destroy
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,6 +45,7 @@ export default class TooltipProvider extends Module {
|
||||||
delayShow: this.checkDelayShow.bind(this),
|
delayShow: this.checkDelayShow.bind(this),
|
||||||
content: this.process.bind(this),
|
content: this.process.bind(this),
|
||||||
interactive: this.checkInteractive.bind(this),
|
interactive: this.checkInteractive.bind(this),
|
||||||
|
hover_events: this.checkHoverEvents.bind(this),
|
||||||
popper: {
|
popper: {
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
modifiers: {
|
modifiers: {
|
||||||
|
@ -55,6 +56,14 @@ export default class TooltipProvider extends Module {
|
||||||
boundariesElement: container
|
boundariesElement: container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onHover: (target, tip, event) => {
|
||||||
|
this.emit(':hover', target, tip, event)
|
||||||
|
},
|
||||||
|
|
||||||
|
onLeave: (target, tip, event) => {
|
||||||
|
this.emit(':leave', target, tip, event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -95,6 +104,16 @@ export default class TooltipProvider extends Module {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkHoverEvents(target, tip) {
|
||||||
|
const type = target.dataset.tooltipType,
|
||||||
|
handler = this.types[type];
|
||||||
|
|
||||||
|
if ( has(handler, 'hover_events') )
|
||||||
|
return maybe_call(handler.hover_events, null, target, tip);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
process(target, tip) {
|
process(target, tip) {
|
||||||
const type = target.dataset.tooltipType || 'text',
|
const type = target.dataset.tooltipType || 'text',
|
||||||
handler = this.types[type];
|
handler = this.types[type];
|
||||||
|
@ -103,7 +122,7 @@ export default class TooltipProvider extends Module {
|
||||||
return [
|
return [
|
||||||
createElement('strong', null, 'Unhandled Tooltip Type'),
|
createElement('strong', null, 'Unhandled Tooltip Type'),
|
||||||
createElement('code', {
|
createElement('code', {
|
||||||
className: 'block pd-t-05 border-t mg-t-05',
|
className: 'tw-block pd-t-05 border-t mg-t-05',
|
||||||
style: {
|
style: {
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
textAlign: 'left'
|
textAlign: 'left'
|
||||||
|
|
|
@ -15,6 +15,8 @@ const SCROLL_EVENTS = [
|
||||||
'DOMMouseScroll'
|
'DOMMouseScroll'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let last_id = 0;
|
||||||
|
|
||||||
export default class Scroller extends Module {
|
export default class Scroller extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -221,6 +223,11 @@ export default class Scroller extends Module {
|
||||||
this._ffz_installed = true;
|
this._ffz_installed = true;
|
||||||
const inst = this;
|
const inst = this;
|
||||||
|
|
||||||
|
this._ffz_accessor = `_ffz_contains_${last_id++}`;
|
||||||
|
|
||||||
|
t.on('tooltips:mousemove', this.ffzTooltipHover, this);
|
||||||
|
t.on('tooltips:leave', this.ffzTooltipLeave, this);
|
||||||
|
|
||||||
inst.ffz_oldScrollEvent = inst.handleScrollEvent;
|
inst.ffz_oldScrollEvent = inst.handleScrollEvent;
|
||||||
inst.ffz_oldScroll = inst.scrollToBottom;
|
inst.ffz_oldScroll = inst.scrollToBottom;
|
||||||
|
|
||||||
|
@ -443,6 +450,28 @@ export default class Scroller extends Module {
|
||||||
this.ffzUpdateKeyTags();
|
this.ffzUpdateKeyTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzTooltipHover = function(target, tip, event) {
|
||||||
|
if ( target[this._ffz_accessor] == null ) {
|
||||||
|
const scroller = this.scroll && this.scroll.scrollContent;
|
||||||
|
target[this._ffz_accessor] = scroller ? scroller.contains(target) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( target[this._ffz_accessor] )
|
||||||
|
this.ffzMouseMove(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzTooltipLeave = function(target) {
|
||||||
|
if ( this.ffz_outside )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( target[this._ffz_accessor] == null ) {
|
||||||
|
const scroller = this.scroll && this.scroll.scrollContent;
|
||||||
|
target[this._ffz_accessor] = scroller ? scroller.contains(target) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( target[this._ffz_accessor] )
|
||||||
|
this.ffzMouseLeave();
|
||||||
|
}
|
||||||
|
|
||||||
// Keyboard Stuff
|
// Keyboard Stuff
|
||||||
|
|
||||||
|
@ -509,7 +538,7 @@ export default class Scroller extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.prototype.listFooter = function() {
|
cls.prototype.listFooter = function() {
|
||||||
let msg;
|
let msg, cls = '';
|
||||||
if ( this.state.isPaused ) {
|
if ( this.state.isPaused ) {
|
||||||
const f = t.pause,
|
const f = t.pause,
|
||||||
reason = f === 2 ? t.i18n.t('key.ctrl', 'Ctrl Key') :
|
reason = f === 2 ? t.i18n.t('key.ctrl', 'Ctrl Key') :
|
||||||
|
@ -523,6 +552,7 @@ export default class Scroller extends Module {
|
||||||
t.i18n.t('key.mouse', 'Mouse Movement');
|
t.i18n.t('key.mouse', 'Mouse Movement');
|
||||||
|
|
||||||
msg = t.i18n.t('chat.paused', '(Chat Paused Due to {reason})', {reason});
|
msg = t.i18n.t('chat.paused', '(Chat Paused Due to {reason})', {reason});
|
||||||
|
cls = 'ffz--freeze-indicator';
|
||||||
|
|
||||||
} else if ( this.state.isAutoScrolling )
|
} else if ( this.state.isAutoScrolling )
|
||||||
return null;
|
return null;
|
||||||
|
@ -530,7 +560,7 @@ export default class Scroller extends Module {
|
||||||
msg = t.i18n.t('chat.messages-below', 'More messages below.');
|
msg = t.i18n.t('chat.messages-below', 'More messages below.');
|
||||||
|
|
||||||
return createElement('div', {
|
return createElement('div', {
|
||||||
className: 'chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-full-width tw-justify-content-center tw-pd-05',
|
className: `chat-list__list-footer tw-absolute tw-align-items-center tw-border-radius-medium tw-bottom-0 tw-flex tw-full-width tw-justify-content-center tw-pd-05 ${cls}`,
|
||||||
onClick: this.ffzFastResume
|
onClick: this.ffzFastResume
|
||||||
}, createElement('div', null, msg));
|
}, createElement('div', null, msg));
|
||||||
}
|
}
|
||||||
|
@ -615,6 +645,9 @@ export default class Scroller extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmount(inst) { // eslint-disable-line class-methods-use-this
|
onUnmount(inst) { // eslint-disable-line class-methods-use-this
|
||||||
|
this.off('tooltips:mousemove', inst.ffzTooltipHover, inst);
|
||||||
|
this.off('tooltips:leave', inst.ffzTooltipLeave, inst);
|
||||||
|
|
||||||
window.removeEventListener('keydown', inst.ffzHandleKey);
|
window.removeEventListener('keydown', inst.ffzHandleKey);
|
||||||
window.removeEventListener('keyup', inst.ffzHandleKey);
|
window.removeEventListener('keyup', inst.ffzHandleKey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default class Game extends SiteModule {
|
||||||
|
|
||||||
this.GameHeader = this.fine.define(
|
this.GameHeader = this.fine.define(
|
||||||
'game-header',
|
'game-header',
|
||||||
n => n.props && n.props.data && n.renderDropsAvailable,
|
n => n.props && n.props.data && n.getBannerImage && n.getCategoryDisplayNameAndFollowButton,
|
||||||
['dir-game-index', 'dir-community', 'dir-game-videos', 'dir-game-clips', 'dir-game-details']
|
['dir-game-index', 'dir-community', 'dir-game-videos', 'dir-game-clips', 'dir-game-details']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export default class Game extends SiteModule {
|
||||||
if ( get('data.game', inst.props) == null || ! container || ! container.querySelector )
|
if ( get('data.game', inst.props) == null || ! container || ! container.querySelector )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const buttons = container.querySelector('.tw-flex > .tw-inline-flex');
|
const buttons = container.querySelector('.tw-flex > .tw-flex-column');
|
||||||
if ( ! buttons )
|
if ( ! buttons )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ export default class Game extends SiteModule {
|
||||||
};
|
};
|
||||||
|
|
||||||
block_btn = (<button
|
block_btn = (<button
|
||||||
class="tw-mg-l-1 tw-button ffz-directory-toggle-block"
|
class="tw-mg-r-1 tw-button ffz-directory-toggle-block"
|
||||||
onClick={this.generateClickHandler('directory.game.blocked-games', game, update_block)}
|
onClick={this.generateClickHandler('directory.game.blocked-games', game, update_block)}
|
||||||
>
|
>
|
||||||
{block_label = <span class="tw-button__text" />}
|
{block_label = <span class="tw-button__text" />}
|
||||||
|
@ -116,7 +116,7 @@ export default class Game extends SiteModule {
|
||||||
update_block();
|
update_block();
|
||||||
|
|
||||||
hidden_btn = (<button
|
hidden_btn = (<button
|
||||||
class="tw-mg-l-1 tw-button ffz-directory-toggle-thumbnail"
|
class="tw-button ffz-directory-toggle-thumbnail"
|
||||||
onClick={this.generateClickHandler('directory.game.hidden-thumbnails', game, update_hidden)}
|
onClick={this.generateClickHandler('directory.game.hidden-thumbnails', game, update_hidden)}
|
||||||
>
|
>
|
||||||
{hidden_label = <span class="tw-button__text" />}
|
{hidden_label = <span class="tw-button__text" />}
|
||||||
|
|
|
@ -9,11 +9,6 @@
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-directory-buttons {
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Color variables
|
// TODO: Color variables
|
||||||
|
|
||||||
.ffz-directory-toggle-thumbnail {
|
.ffz-directory-toggle-thumbnail {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ffz--autocomplete tw-relative">
|
<div class="ffz--autocomplete tw-relative">
|
||||||
<div class="tw-search-input" data-a-target="dropdown-search-input">
|
<div class="tw-search-input" data-a-target="dropdown-search-input">
|
||||||
<label v-if="placeholder" :for="'ffz-autocomplete$' + id" class="tw-hide-accessible">{{ placeholder }}</label>
|
<label v-if="placeholder" :for="_id" class="tw-hide-accessible">{{ placeholder }}</label>
|
||||||
<div class="tw-relative">
|
<div class="tw-relative">
|
||||||
<div v-if="hasIcon" class="tw-absolute tw-align-items-center tw-c-text-alt-2 tw-flex tw-full-height tw-input__icon tw-justify-content-center tw-left-0 tw-top-0 tw-z-default">
|
<div v-if="hasIcon" class="tw-absolute tw-align-items-center tw-c-text-alt-2 tw-flex tw-full-height tw-input__icon tw-justify-content-center tw-left-0 tw-top-0 tw-z-default">
|
||||||
<figure :class="icon" />
|
<figure :class="icon" />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
:id="'ffz-autocomplete$' + id"
|
:id="_id"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:class="[hasIcon ? 'tw-pd-l-3' : 'tw-pd-l-1']"
|
:class="[hasIcon ? 'tw-pd-l-3' : 'tw-pd-l-1']"
|
||||||
v-model="search"
|
v-model="search"
|
||||||
|
@ -72,6 +72,10 @@ let last_id = 0;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
inputId: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
items: {
|
items: {
|
||||||
type: [Array, Function],
|
type: [Array, Function],
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -145,6 +149,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
_id() {
|
||||||
|
if ( this.inputId && this.inputId.length )
|
||||||
|
return this.inputId;
|
||||||
|
|
||||||
|
return `ffz-autocomplete$${this.id}`;
|
||||||
|
},
|
||||||
|
|
||||||
hasIcon() {
|
hasIcon() {
|
||||||
return this.icon && this.icon.length > 0
|
return this.icon && this.icon.length > 0
|
||||||
},
|
},
|
||||||
|
@ -190,8 +201,8 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.maybeClose = debounce(this.maybeClose, 25);
|
this.maybeClose = debounce(this.maybeClose, 250);
|
||||||
this.updateCache = debounce(this.updateCache, 250, 2);
|
this.updateCache = debounce(this.updateCache, 500, 2);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -307,6 +307,11 @@ export function get(path, object) {
|
||||||
|
|
||||||
|
|
||||||
export function deep_copy(object, seen) {
|
export function deep_copy(object, seen) {
|
||||||
|
if ( object === null )
|
||||||
|
return null;
|
||||||
|
else if ( object === undefined )
|
||||||
|
return undefined;
|
||||||
|
|
||||||
if ( typeof object !== 'object' )
|
if ( typeof object !== 'object' )
|
||||||
return object;
|
return object;
|
||||||
|
|
||||||
|
|
|
@ -241,21 +241,39 @@ export class Tooltip {
|
||||||
tip.add_class = undefined;
|
tip.add_class = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const interactive = maybe_call(opts.interactive, null, target, tip);
|
const interactive = maybe_call(opts.interactive, null, target, tip),
|
||||||
|
hover_events = maybe_call(opts.hover_events, null, target, tip);
|
||||||
|
|
||||||
el.classList.toggle('interactive', interactive || false);
|
el.classList.toggle('interactive', interactive || false);
|
||||||
|
|
||||||
if ( ! opts.manual ) {
|
if ( ! opts.manual || (hover_events && (opts.onHover || opts.onLeave || opts.onMove)) ) {
|
||||||
el.addEventListener('mouseover', el._ffz_over_handler = () => {
|
if ( hover_events && opts.onMove )
|
||||||
|
el.addEventListener('mousemove', el._ffz_move_handler = event => {
|
||||||
|
opts.onMove(target, tip, event);
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener('mouseover', el._ffz_over_handler = event => {
|
||||||
if ( ! document.contains(target) )
|
if ( ! document.contains(target) )
|
||||||
this.hide(tip);
|
this.hide(tip);
|
||||||
|
|
||||||
else if ( maybe_call(opts.interactive, null, target, tip) )
|
if ( hover_events && opts.onHover )
|
||||||
|
opts.onHover(target, tip, event);
|
||||||
|
|
||||||
|
if ( opts.manual ) {
|
||||||
|
/* no-op */
|
||||||
|
} else if ( maybe_call(opts.interactive, null, target, tip) )
|
||||||
this._enter(target);
|
this._enter(target);
|
||||||
else
|
else
|
||||||
this._exit(target);
|
this._exit(target);
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('mouseout', el._ffz_out_handler = () => this._exit(target));
|
el.addEventListener('mouseout', el._ffz_out_handler = event => {
|
||||||
|
if ( hover_events && opts.onLeave )
|
||||||
|
opts.onLeave(target, tip, event);
|
||||||
|
|
||||||
|
if ( ! opts.manual )
|
||||||
|
this._exit(target);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign our content. If there's a Promise, we'll need
|
// Assign our content. If there's a Promise, we'll need
|
||||||
|
@ -342,6 +360,9 @@ export class Tooltip {
|
||||||
if ( el._ffz_out_handler )
|
if ( el._ffz_out_handler )
|
||||||
el.removeEventListener('mouseout', el._ffz_out_handler);
|
el.removeEventListener('mouseout', el._ffz_out_handler);
|
||||||
|
|
||||||
|
if ( el._ffz_move_handler )
|
||||||
|
el.removeEventListener('mousemove', el._ffz_move_handler);
|
||||||
|
|
||||||
el.remove();
|
el.remove();
|
||||||
tip.outer = el._ffz_out_handler = el._ffz_over_handler = null;
|
tip.outer = el._ffz_out_handler = el._ffz_over_handler = null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue