Merge branch 'fix/popover-performance' into 'develop'

Fix/popover performance

See merge request pleroma/pleroma-fe!1066
This commit is contained in:
Shpuld Shpludson 2020-02-28 16:39:47 +00:00
commit 111c1ced67
20 changed files with 393 additions and 330 deletions

View file

@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Registration fixed - Registration fixed
- Deactivation of remote accounts from frontend - Deactivation of remote accounts from frontend
- Fixed NSFW unhiding not working with videos when using one-click unhiding/displaying - Fixed NSFW unhiding not working with videos when using one-click unhiding/displaying
- Improved performance of anything that uses popovers (most notably statuses)
## [1.1.7 and earlier] - 2019-12-14 ## [1.1.7 and earlier] - 2019-12-14
### Added ### Added

View file

@ -29,7 +29,6 @@
"portal-vue": "^2.1.4", "portal-vue": "^2.1.4",
"sanitize-html": "^1.13.0", "sanitize-html": "^1.13.0",
"v-click-outside": "^2.1.1", "v-click-outside": "^2.1.1",
"v-tooltip": "^2.0.2",
"vue": "^2.5.13", "vue": "^2.5.13",
"vue-chat-scroll": "^1.2.1", "vue-chat-scroll": "^1.2.1",
"vue-i18n": "^7.3.2", "vue-i18n": "^7.3.2",

View file

@ -1,4 +1,5 @@
import ProgressButton from '../progress_button/progress_button.vue' import ProgressButton from '../progress_button/progress_button.vue'
import Popover from '../popover/popover.vue'
const AccountActions = { const AccountActions = {
props: [ props: [
@ -8,7 +9,8 @@ const AccountActions = {
return { } return { }
}, },
components: { components: {
ProgressButton ProgressButton,
Popover
}, },
methods: { methods: {
showRepeats () { showRepeats () {

View file

@ -1,13 +1,13 @@
<template> <template>
<div class="account-actions"> <div class="account-actions">
<v-popover <Popover
trigger="click" trigger="click"
class="account-tools-popover" placement="bottom"
:container="false"
placement="bottom-end"
:offset="5"
> >
<div slot="popover"> <div
slot="content"
class="account-tools-popover"
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<template v-if="user.following"> <template v-if="user.following">
<button <button
@ -51,10 +51,13 @@
</button> </button>
</div> </div>
</div> </div>
<div class="btn btn-default ellipsis-button"> <div
slot="trigger"
class="btn btn-default ellipsis-button"
>
<i class="icon-ellipsis trigger-button" /> <i class="icon-ellipsis trigger-button" />
</div> </div>
</v-popover> </Popover>
</div> </div>
</template> </template>
@ -62,7 +65,6 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.account-actions { .account-actions {
margin: 0 .8em; margin: 0 .8em;
} }
@ -70,6 +72,7 @@
.account-actions button.dropdown-item { .account-actions button.dropdown-item {
margin-left: 0; margin-left: 0;
} }
.account-actions .trigger-button { .account-actions .trigger-button {
color: $fallback--lightText; color: $fallback--lightText;
color: var(--lightText, $fallback--lightText); color: var(--lightText, $fallback--lightText);

View file

@ -1,20 +1,17 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import Popover from '../popover/popover.vue'
const EMOJI_REACTION_COUNT_CUTOFF = 12 const EMOJI_REACTION_COUNT_CUTOFF = 12
const EmojiReactions = { const EmojiReactions = {
name: 'EmojiReactions', name: 'EmojiReactions',
components: { components: {
UserAvatar UserAvatar,
Popover
}, },
props: ['status'], props: ['status'],
data: () => ({ data: () => ({
showAll: false, showAll: false
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
}), }),
computed: { computed: {
tooManyReactions () { tooManyReactions () {

View file

@ -1,15 +1,14 @@
<template> <template>
<div class="emoji-reactions"> <div class="emoji-reactions">
<v-popover <Popover
v-for="(reaction) in emojiReactions" v-for="(reaction) in emojiReactions"
:key="reaction.name" :key="reaction.name"
:popper-options="popperOptions"
trigger="hover" trigger="hover"
placement="top" placement="top"
:offset="{ y: 5 }"
> >
<div <div
slot="popover" slot="content"
class="reacted-users" class="reacted-users"
> >
<div v-if="accountsForEmoji[reaction.name].length"> <div v-if="accountsForEmoji[reaction.name].length">
@ -24,7 +23,12 @@
:compact="true" :compact="true"
/> />
<div class="reacted-user-names"> <div class="reacted-user-names">
<span class="reacted-user-name" v-html="account.name_html" /> <!-- eslint-disable vue/no-v-html -->
<span
class="reacted-user-name"
v-html="account.name_html"
/>
<!-- eslint-enable vue/no-v-html -->
<span class="reacted-user-screen-name">{{ account.screen_name }}</span> <span class="reacted-user-screen-name">{{ account.screen_name }}</span>
</div> </div>
</div> </div>
@ -34,6 +38,7 @@
</div> </div>
</div> </div>
<button <button
slot="trigger"
class="emoji-reaction btn btn-default" class="emoji-reaction btn btn-default"
:class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }" :class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
@click="emojiOnClick(reaction.name, $event)" @click="emojiOnClick(reaction.name, $event)"
@ -42,17 +47,16 @@
<span class="reaction-emoji">{{ reaction.name }}</span> <span class="reaction-emoji">{{ reaction.name }}</span>
<span>{{ reaction.count }}</span> <span>{{ reaction.count }}</span>
</button> </button>
</v-popover> </Popover>
<a <a
v-if="tooManyReactions" v-if="tooManyReactions"
@click="toggleShowAll" class="emoji-reaction-expand faint"
class="emoji-reaction-expand faint" href="javascript:void(0)"
href='javascript:void(0)' @click="toggleShowAll"
> >
{{ showAll ? $t('general.show_less') : showMoreString }} {{ showAll ? $t('general.show_less') : showMoreString }}
</a> </a>
</div> </div>
</template> </template>
<script src="./emoji_reactions.js" ></script> <script src="./emoji_reactions.js" ></script>
@ -78,6 +82,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-left: 0.5em; margin-left: 0.5em;
min-width: 5em;
img { img {
width: 1em; width: 1em;

View file

@ -1,5 +1,8 @@
import Popover from '../popover/popover.vue'
const ExtraButtons = { const ExtraButtons = {
props: [ 'status' ], props: [ 'status' ],
components: { Popover },
methods: { methods: {
deleteStatus () { deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm')) const confirmed = window.confirm(this.$t('status.delete_confirm'))

View file

@ -1,11 +1,11 @@
<template> <template>
<v-popover <Popover
v-if="canDelete || canMute || canPin" v-if="canDelete || canMute || canPin"
trigger="click" trigger="click"
placement="top" placement="top"
class="extra-button-popover" class="extra-button-popover"
> >
<div slot="popover"> <div slot="content">
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
v-if="canMute && !status.thread_muted" v-if="canMute && !status.thread_muted"
@ -47,17 +47,17 @@
</button> </button>
</div> </div>
</div> </div>
<div class="button-icon"> <i
<i class="icon-ellipsis" /> slot="trigger"
</div> class="icon-ellipsis button-icon"
</v-popover> />
</Popover>
</template> </template>
<script src="./extra_buttons.js" ></script> <script src="./extra_buttons.js" ></script>
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.icon-ellipsis { .icon-ellipsis {
cursor: pointer; cursor: pointer;

View file

@ -1,4 +1,5 @@
import DialogModal from '../dialog_modal/dialog_modal.vue' import DialogModal from '../dialog_modal/dialog_modal.vue'
import Popover from '../popover/popover.vue'
const FORCE_NSFW = 'mrf_tag:media-force-nsfw' const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
const STRIP_MEDIA = 'mrf_tag:media-strip' const STRIP_MEDIA = 'mrf_tag:media-strip'
@ -14,7 +15,6 @@ const ModerationTools = {
], ],
data () { data () {
return { return {
showDropDown: false,
tags: { tags: {
FORCE_NSFW, FORCE_NSFW,
STRIP_MEDIA, STRIP_MEDIA,
@ -24,11 +24,13 @@ const ModerationTools = {
SANDBOX, SANDBOX,
QUARANTINE QUARANTINE
}, },
showDeleteUserDialog: false showDeleteUserDialog: false,
toggled: false
} }
}, },
components: { components: {
DialogModal DialogModal,
Popover
}, },
computed: { computed: {
tagsSet () { tagsSet () {
@ -89,6 +91,9 @@ const ModerationTools = {
window.history.back() window.history.back()
} }
}) })
},
setToggled (value) {
this.toggled = value
} }
} }
} }

View file

@ -1,13 +1,14 @@
<template> <template>
<div> <div>
<v-popover <Popover
trigger="click" trigger="click"
class="moderation-tools-popover" class="moderation-tools-popover"
placement="bottom-end" placement="bottom"
@show="showDropDown = true" :offset="{ y: 5 }"
@hide="showDropDown = false" @show="setToggled(true)"
@close="setToggled(false)"
> >
<div slot="popover"> <div slot="content">
<div class="dropdown-menu"> <div class="dropdown-menu">
<span v-if="user.is_local"> <span v-if="user.is_local">
<button <button
@ -122,12 +123,13 @@
</div> </div>
</div> </div>
<button <button
slot="trigger"
class="btn btn-default btn-block" class="btn btn-default btn-block"
:class="{ toggled: showDropDown }" :class="{ toggled }"
> >
{{ $t('user_card.admin_menu.moderation') }} {{ $t('user_card.admin_menu.moderation') }}
</button> </button>
</v-popover> </Popover>
<portal to="modal"> <portal to="modal">
<DialogModal <DialogModal
v-if="showDeleteUserDialog" v-if="showDeleteUserDialog"
@ -160,7 +162,6 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.menu-checkbox { .menu-checkbox {
float: right; float: right;

View file

@ -0,0 +1,156 @@
const Popover = {
name: 'Popover',
props: {
// Action to trigger popover: either 'hover' or 'click'
trigger: String,
// Either 'top' or 'bottom'
placement: String,
// Takes object with properties 'x' and 'y', values of these can be
// 'container' for using offsetParent as boundaries for either axis
// or 'viewport'
boundTo: Object,
// Takes a top/bottom/left/right object, how much space to leave
// between boundary and popover element
margin: Object,
// Takes a x/y object and tells how many pixels to offset from
// anchor point on either axis
offset: Object,
// Additional styles you may want for the popover container
popoverClass: String
},
data () {
return {
hidden: true,
styles: { opacity: 0 },
oldSize: { width: 0, height: 0 }
}
},
methods: {
updateStyles () {
if (this.hidden) {
this.styles = {
opacity: 0
}
return
}
// Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one slot="trigger".
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
const screenBox = anchorEl.getBoundingClientRect()
// Screen position of the origin point for popover
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
const content = this.$refs.content
// Minor optimization, don't call a slow reflow call if we don't have to
const parentBounds = this.boundTo &&
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
this.$el.offsetParent.getBoundingClientRect()
const margin = this.margin || {}
// What are the screen bounds for the popover? Viewport vs container
// when using viewport, using default margin values to dodge the navbar
const xBounds = this.boundTo && this.boundTo.x === 'container' ? {
min: parentBounds.left + (margin.left || 0),
max: parentBounds.right - (margin.right || 0)
} : {
min: 0 + (margin.left || 10),
max: window.innerWidth - (margin.right || 10)
}
const yBounds = this.boundTo && this.boundTo.y === 'container' ? {
min: parentBounds.top + (margin.top || 0),
max: parentBounds.bottom - (margin.bottom || 0)
} : {
min: 0 + (margin.top || 50),
max: window.innerHeight - (margin.bottom || 5)
}
let horizOffset = 0
// If overflowing from left, move it so that it doesn't
if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) {
horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min
}
// If overflowing from right, move it so that it doesn't
if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) {
horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max
}
// Default to whatever user wished with placement prop
let usingTop = this.placement !== 'bottom'
// Handle special cases, first force to displaying on top if there's not space on bottom,
// regardless of what placement value was. Then check if there's not space on top, and
// force to bottom, again regardless of what placement value was.
if (origin.y + content.offsetHeight > yBounds.max) usingTop = true
if (origin.y - content.offsetHeight < yBounds.min) usingTop = false
const yOffset = (this.offset && this.offset.y) || 0
const translateY = usingTop
? -anchorEl.offsetHeight - yOffset - content.offsetHeight
: yOffset
const xOffset = (this.offset && this.offset.x) || 0
const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset
// Note, separate translateX and translateY avoids blurry text on chromium,
// single translate or translate3d resulted in blurry text.
this.styles = {
opacity: 1,
transform: `translateX(${Math.floor(translateX)}px) translateY(${Math.floor(translateY)}px)`
}
},
showPopover () {
if (this.hidden) this.$emit('show')
this.hidden = false
this.$nextTick(this.updateStyles)
},
hidePopover () {
if (!this.hidden) this.$emit('close')
this.hidden = true
this.styles = { opacity: 0 }
},
onMouseenter (e) {
if (this.trigger === 'hover') this.showPopover()
},
onMouseleave (e) {
if (this.trigger === 'hover') this.hidePopover()
},
onClick (e) {
if (this.trigger === 'click') {
if (this.hidden) {
this.showPopover()
} else {
this.hidePopover()
}
}
},
onClickOutside (e) {
if (this.hidden) return
if (this.$el.contains(e.target)) return
this.hidePopover()
}
},
updated () {
// Monitor changes to content size, update styles only when content sizes have changed,
// that should be the only time we need to move the popover box if we don't care about scroll
// or resize
const content = this.$refs.content
if (!content) return
if (this.oldSize.width !== content.offsetWidth || this.oldSize.height !== content.offsetHeight) {
this.updateStyles()
this.oldSize = { width: content.offsetWidth, height: content.offsetHeight }
}
},
created () {
document.addEventListener('click', this.onClickOutside)
},
destroyed () {
document.removeEventListener('click', this.onClickOutside)
this.hidePopover()
}
}
export default Popover

View file

@ -0,0 +1,118 @@
<template>
<div
@mouseenter="onMouseenter"
@mouseleave="onMouseleave"
>
<div
ref="trigger"
@click="onClick"
>
<slot name="trigger" />
</div>
<div
v-if="!hidden"
ref="content"
:style="styles"
class="popover"
:class="popoverClass"
>
<slot
name="content"
class="popover-inner"
:close="hidePopover"
/>
</div>
</div>
</template>
<script src="./popover.js" />
<style lang=scss>
@import '../../_variables.scss';
.popover {
z-index: 8;
position: absolute;
min-width: 0;
transition: opacity 0.3s;
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
box-shadow: var(--panelShadow);
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--text;
color: var(--popoverText, $fallback--text);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
}
.dropdown-menu {
display: block;
padding: .5rem 0;
font-size: 1rem;
text-align: left;
list-style: none;
max-width: 100vw;
z-index: 10;
white-space: nowrap;
.dropdown-divider {
height: 0;
margin: .5rem 0;
overflow: hidden;
border-top: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border);
}
.dropdown-item {
line-height: 21px;
margin-right: 5px;
overflow: auto;
display: block;
padding: .25rem 1.0rem .25rem 1.5rem;
clear: both;
font-weight: 400;
text-align: inherit;
white-space: nowrap;
border: none;
border-radius: 0px;
background-color: transparent;
box-shadow: none;
width: 100%;
height: 100%;
--btnText: var(--popoverText, $fallback--text);
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
color: var(--menuPopoverIcon, $fallback--icon)
}
}
&:active, &:hover {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
i {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
}
}
}
}
</style>

View file

@ -1,164 +0,0 @@
@import '../../_variables.scss';
.tooltip.popover {
z-index: 8;
.popover-inner {
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
box-shadow: var(--panelShadow);
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--text;
color: var(--popoverText, $fallback--text);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
}
.popover-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: $fallback--bg;
border-color: var(--bg, $fallback--bg);
}
&[x-placement^="top"] {
margin-bottom: 5px;
.popover-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -4px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="bottom"] {
margin-top: 5px;
.popover-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -4px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="right"] {
margin-left: 5px;
.popover-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -4px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[x-placement^="left"] {
margin-right: 5px;
.popover-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -4px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}
.dropdown-menu {
display: block;
padding: .5rem 0;
font-size: 1rem;
text-align: left;
list-style: none;
max-width: 100vw;
z-index: 10;
.dropdown-divider {
height: 0;
margin: .5rem 0;
overflow: hidden;
border-top: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border);
}
.dropdown-item {
line-height: 21px;
margin-right: 5px;
overflow: auto;
display: block;
padding: .25rem 1.0rem .25rem 1.5rem;
clear: both;
font-weight: 400;
text-align: inherit;
white-space: normal;
border: none;
border-radius: 0px;
background-color: transparent;
box-shadow: none;
width: 100%;
height: 100%;
--btnText: var(--popoverText, $fallback--text);
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
}
}
&:active, &:hover {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
i {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
}
}
}
}

View file

@ -1,34 +1,25 @@
import Popover from '../popover/popover.vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
const ReactButton = { const ReactButton = {
props: ['status', 'loggedIn'], props: ['status', 'loggedIn'],
data () { data () {
return { return {
showTooltip: false, filterWord: ''
filterWord: '',
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
} }
}, },
components: {
Popover
},
methods: { methods: {
openReactionSelect () { addReaction (event, emoji, close) {
this.showTooltip = true
this.filterWord = ''
},
closeReactionSelect () {
this.showTooltip = false
},
addReaction (event, emoji) {
const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji) const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)
if (existingReaction && existingReaction.me) { if (existingReaction && existingReaction.me) {
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
} else { } else {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
} }
this.closeReactionSelect() close()
} }
}, },
computed: { computed: {

View file

@ -1,13 +1,14 @@
<template> <template>
<v-popover <Popover
:popper-options="popperOptions" trigger="click"
:open="showTooltip"
trigger="manual"
placement="top" placement="top"
:offset="{ y: 5 }"
class="react-button-popover" class="react-button-popover"
@hide="closeReactionSelect"
> >
<div slot="popover"> <div
slot="content"
slot-scope="{close}"
>
<div class="reaction-picker-filter"> <div class="reaction-picker-filter">
<input <input
v-model="filterWord" v-model="filterWord"
@ -19,7 +20,7 @@
v-for="emoji in commonEmojis" v-for="emoji in commonEmojis"
:key="emoji" :key="emoji"
class="emoji-button" class="emoji-button"
@click="addReaction($event, emoji)" @click="addReaction($event, emoji, close)"
> >
{{ emoji }} {{ emoji }}
</span> </span>
@ -28,23 +29,20 @@
v-for="(emoji, key) in emojis" v-for="(emoji, key) in emojis"
:key="key" :key="key"
class="emoji-button" class="emoji-button"
@click="addReaction($event, emoji.replacement)" @click="addReaction($event, emoji.replacement, close)"
> >
{{ emoji.replacement }} {{ emoji.replacement }}
</span> </span>
<div class="reaction-bottom-fader" /> <div class="reaction-bottom-fader" />
</div> </div>
</div> </div>
<div <i
v-if="loggedIn" v-if="loggedIn"
@click.prevent="openReactionSelect" slot="trigger"
> class="icon-smile button-icon add-reaction-button"
<i :title="$t('tool_tip.add_reaction')"
class="icon-smile button-icon add-reaction-button" />
:title="$t('tool_tip.add_reaction')" </Popover>
/>
</div>
</v-popover>
</template> </template>
<script src="./react_button.js" ></script> <script src="./react_button.js" ></script>

View file

@ -177,6 +177,8 @@
<StatusPopover <StatusPopover
v-if="!isPreview" v-if="!isPreview"
:status-id="status.in_reply_to_status_id" :status-id="status.in_reply_to_status_id"
class="reply-to-popover"
style="min-width: 0"
> >
<a <a
class="reply-to" class="reply-to"
@ -572,11 +574,10 @@ $status-margin: 0.75em;
align-items: stretch; align-items: stretch;
> .reply-to-and-accountname > a { > .reply-to-and-accountname > a {
overflow: hidden;
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap; white-space: nowrap;
display: inline-block;
word-break: break-all; word-break: break-all;
} }
} }
@ -585,7 +586,6 @@ $status-margin: 0.75em;
display: flex; display: flex;
height: 18px; height: 18px;
margin-right: 0.5em; margin-right: 0.5em;
overflow: hidden;
max-width: 100%; max-width: 100%;
.icon-reply { .icon-reply {
transform: scaleX(-1); transform: scaleX(-1);
@ -596,6 +596,10 @@ $status-margin: 0.75em;
display: flex; display: flex;
} }
.reply-to-popover {
min-width: 0;
}
.reply-to { .reply-to {
display: flex; display: flex;
} }
@ -603,6 +607,7 @@ $status-margin: 0.75em;
.reply-to-text { .reply-to-text {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
margin: 0 0.4em 0 0.2em; margin: 0 0.4em 0 0.2em;
} }

View file

@ -5,22 +5,14 @@ const StatusPopover = {
props: [ props: [
'statusId' 'statusId'
], ],
data () {
return {
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
}
},
computed: { computed: {
status () { status () {
return find(this.$store.state.statuses.allStatuses, { id: this.statusId }) return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
} }
}, },
components: { components: {
Status: () => import('../status/status.vue') Status: () => import('../status/status.vue'),
Popover: () => import('../popover/popover.vue')
}, },
methods: { methods: {
enter () { enter () {

View file

@ -1,11 +1,16 @@
<template> <template>
<v-popover <Popover
trigger="hover"
popover-class="status-popover" popover-class="status-popover"
placement="top-start" :bound-to="{ x: 'container' }"
:popper-options="popperOptions" @show="enter"
@show="enter()"
> >
<template slot="popover"> <template slot="trigger">
<slot />
</template>
<div
slot="content"
>
<Status <Status
v-if="status" v-if="status"
:is-preview="true" :is-preview="true"
@ -18,10 +23,8 @@
> >
<i class="icon-spin4 animate-spin" /> <i class="icon-spin4 animate-spin" />
</div> </div>
</template> </div>
</Popover>
<slot />
</v-popover>
</template> </template>
<script src="./status_popover.js" ></script> <script src="./status_popover.js" ></script>
@ -29,44 +32,19 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
.tooltip.popover.status-popover { .status-popover {
font-size: 1rem; font-size: 1rem;
min-width: 15em; min-width: 15em;
max-width: 95%; max-width: 95%;
margin-left: 0.5em;
.popover-inner { border-color: $fallback--border;
border-color: $fallback--border; border-color: var(--border, $fallback--border);
border-color: var(--border, $fallback--border); border-style: solid;
border-style: solid; border-width: 1px;
border-width: 1px; border-radius: $fallback--tooltipRadius;
border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
border-radius: var(--tooltipRadius, $fallback--tooltipRadius); box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); box-shadow: var(--popupShadow);
box-shadow: var(--popupShadow);
}
.popover-arrow::before {
position: absolute;
content: '';
left: -7px;
border: solid 7px transparent;
z-index: -1;
}
&[x-placement^="bottom-start"] .popover-arrow::before {
top: -2px;
border-top-width: 0;
border-bottom-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border);
}
&[x-placement^="top-start"] .popover-arrow::before {
bottom: -2px;
border-bottom-width: 0;
border-top-color: $fallback--border;
border-top-color: var(--border, $fallback--border);
}
.status-el.status-el { .status-el.status-el {
border: none; border: none;

View file

@ -31,7 +31,6 @@ import VueChatScroll from 'vue-chat-scroll'
import VueClickOutside from 'v-click-outside' import VueClickOutside from 'v-click-outside'
import PortalVue from 'portal-vue' import PortalVue from 'portal-vue'
import VBodyScrollLock from './directives/body_scroll_lock' import VBodyScrollLock from './directives/body_scroll_lock'
import VTooltip from 'v-tooltip'
import afterStoreSetup from './boot/after_store.js' import afterStoreSetup from './boot/after_store.js'
@ -44,13 +43,6 @@ Vue.use(VueChatScroll)
Vue.use(VueClickOutside) Vue.use(VueClickOutside)
Vue.use(PortalVue) Vue.use(PortalVue)
Vue.use(VBodyScrollLock) Vue.use(VBodyScrollLock)
Vue.use(VTooltip, {
popover: {
defaultTrigger: 'hover click',
defaultContainer: false,
defaultOffset: 5
}
})
const i18n = new VueI18n({ const i18n = new VueI18n({
// By default, use the browser locale, we will update it if neccessary // By default, use the browser locale, we will update it if neccessary

View file

@ -5941,11 +5941,6 @@ pngjs@^3.3.0:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
popper.js@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"
integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==
portal-vue@^2.1.4: portal-vue@^2.1.4:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.4.tgz#1fc679d77e294dc8d026f1eb84aa467de11b392e" resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.4.tgz#1fc679d77e294dc8d026f1eb84aa467de11b392e"
@ -7823,15 +7818,6 @@ v-click-outside@^2.1.1:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.3.tgz#b7297abe833a439dc0895e6418a494381e64b5e7" resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.3.tgz#b7297abe833a439dc0895e6418a494381e64b5e7"
v-tooltip@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/v-tooltip/-/v-tooltip-2.0.2.tgz#8610d9eece2cc44fd66c12ef2f12eec6435cab9b"
integrity sha512-xQ+qzOFfywkLdjHknRPgMMupQNS8yJtf9Utd5Dxiu/0n4HtrxqsgDtN2MLZ0LKbburtSAQgyypuE/snM8bBZhw==
dependencies:
lodash "^4.17.11"
popper.js "^1.15.0"
vue-resize "^0.4.5"
validate-npm-package-license@^3.0.1: validate-npm-package-license@^3.0.1:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@ -7906,11 +7892,6 @@ vue-loader@^14.0.0:
vue-style-loader "^4.0.1" vue-style-loader "^4.0.1"
vue-template-es2015-compiler "^1.6.0" vue-template-es2015-compiler "^1.6.0"
vue-resize@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.5.tgz#4777a23042e3c05620d9cbda01c0b3cc5e32dcea"
integrity sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==
vue-router@^3.0.1: vue-router@^3.0.1:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"