Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85830516
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/App.js b/src/App.js
index 61b5eec1c6..041e131a57 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,123 +1,125 @@
import UserPanel from './components/user_panel/user_panel.vue'
import NavPanel from './components/nav_panel/nav_panel.vue'
import Notifications from './components/notifications/notifications.vue'
import SearchBar from './components/search_bar/search_bar.vue'
import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
import FeaturesPanel from './components/features_panel/features_panel.vue'
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
import ChatPanel from './components/chat_panel/chat_panel.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
import MobileNav from './components/mobile_nav/mobile_nav.vue'
import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
+import PopoverTarget from './components/popover/popover_target.vue'
import { windowWidth } from './services/window_utils/window_utils'
export default {
name: 'app',
components: {
UserPanel,
NavPanel,
Notifications,
SearchBar,
InstanceSpecificPanel,
FeaturesPanel,
WhoToFollowPanel,
ChatPanel,
MediaModal,
SideDrawer,
MobilePostStatusButton,
MobileNav,
UserReportingModal,
- PostStatusModal
+ PostStatusModal,
+ PopoverTarget
},
data: () => ({
mobileActivePanel: 'timeline',
searchBarHidden: true,
supportsMask: window.CSS && window.CSS.supports && (
window.CSS.supports('mask-size', 'contain') ||
window.CSS.supports('-webkit-mask-size', 'contain') ||
window.CSS.supports('-moz-mask-size', 'contain') ||
window.CSS.supports('-ms-mask-size', 'contain') ||
window.CSS.supports('-o-mask-size', 'contain')
)
}),
created () {
// Load the locale from the storage
this.$i18n.locale = this.$store.getters.mergedConfig.interfaceLanguage
window.addEventListener('resize', this.updateMobileState)
},
destroyed () {
window.removeEventListener('resize', this.updateMobileState)
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
background () {
return this.currentUser.background_image || this.$store.state.instance.background
},
enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
logoStyle () {
return {
'visibility': this.enableMask ? 'hidden' : 'visible'
}
},
logoMaskStyle () {
return this.enableMask ? {
'mask-image': `url(${this.$store.state.instance.logo})`
} : {
'background-color': this.enableMask ? '' : 'transparent'
}
},
logoBgStyle () {
return Object.assign({
'margin': `${this.$store.state.instance.logoMargin} 0`,
opacity: this.searchBarHidden ? 1 : 0
}, this.enableMask ? {} : {
'background-color': this.enableMask ? '' : 'transparent'
})
},
logo () { return this.$store.state.instance.logo },
bgStyle () {
return {
'background-image': `url(${this.background})`
}
},
bgAppStyle () {
return {
'--body-background-image': `url(${this.background})`
}
},
sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
hideSitename () { return this.$store.state.instance.hideSitename },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
!this.$store.getters.mergedConfig.hideISP &&
this.$store.state.instance.instanceSpecificPanelContent
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
isMobileLayout () { return this.$store.state.interface.mobileLayout },
privateMode () { return this.$store.state.instance.private }
},
methods: {
scrollToTop () {
window.scrollTo(0, 0)
},
logout () {
this.$router.replace('/main/public')
this.$store.dispatch('logout')
},
onSearchBarToggled (hidden) {
this.searchBarHidden = hidden
},
updateMobileState () {
const mobileLayout = windowWidth() <= 800
const changed = mobileLayout !== this.isMobileLayout
if (changed) {
this.$store.dispatch('setMobileLayout', mobileLayout)
}
}
}
}
diff --git a/src/App.vue b/src/App.vue
index 1b1c2648ca..de15802fa8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,130 +1,131 @@
<template>
<div
id="app"
:style="bgAppStyle"
>
<div
id="app_bg_wrapper"
class="app-bg-wrapper"
:style="bgStyle"
/>
<MobileNav v-if="isMobileLayout" />
<nav
v-else
id="nav"
class="nav-bar container"
@click="scrollToTop()"
>
<div class="inner-nav">
<div
class="logo"
:style="logoBgStyle"
>
<div
class="mask"
:style="logoMaskStyle"
/>
<img
:src="logo"
:style="logoStyle"
>
</div>
<div class="item">
<router-link
v-if="!hideSitename"
class="site-name"
:to="{ name: 'root' }"
active-class="home"
>
{{ sitename }}
</router-link>
</div>
<div class="item right">
<search-bar
v-if="currentUser || !privateMode"
class="nav-icon mobile-hidden"
@toggled="onSearchBarToggled"
@click.stop.native
/>
<router-link
class="mobile-hidden"
:to="{ name: 'settings'}"
>
<i
class="button-icon icon-cog nav-icon"
:title="$t('nav.preferences')"
/>
</router-link>
<a
v-if="currentUser && currentUser.role === 'admin'"
href="/pleroma/admin/#/login-pleroma"
class="mobile-hidden"
target="_blank"
><i
class="button-icon icon-gauge nav-icon"
:title="$t('nav.administration')"
/></a>
<a
v-if="currentUser"
href="#"
class="mobile-hidden"
@click.prevent="logout"
><i
class="button-icon icon-logout nav-icon"
:title="$t('login.logout')"
/></a>
</div>
</div>
</nav>
<div
id="content"
class="container"
>
<div class="sidebar-flexer mobile-hidden">
<div class="sidebar-bounds">
<div class="sidebar-scroller">
<div class="sidebar">
<user-panel />
<div v-if="!isMobileLayout">
<nav-panel />
<instance-specific-panel v-if="showInstanceSpecificPanel" />
<features-panel v-if="!currentUser && showFeaturesPanel" />
<who-to-follow-panel v-if="currentUser && suggestionsEnabled" />
<notifications v-if="currentUser" />
</div>
</div>
</div>
</div>
</div>
<div class="main">
<div
v-if="!currentUser"
class="login-hint panel panel-default"
>
<router-link
:to="{ name: 'login' }"
class="panel-body"
>
{{ $t("login.hint") }}
</router-link>
</div>
<transition name="fade">
<router-view />
</transition>
</div>
<media-modal />
</div>
<chat-panel
v-if="currentUser && chat"
:floating="true"
class="floating-chat mobile-hidden"
/>
<MobilePostStatusButton />
<UserReportingModal />
<PostStatusModal />
<portal-target name="modal" />
+ <PopoverTarget />
</div>
</template>
<script src="./App.js"></script>
<style lang="scss" src="./App.scss"></style>
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 5ac73e975a..3748538322 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -1,50 +1,53 @@
+import Popover from '../popover/popover.vue'
+
const ExtraButtons = {
props: [ 'status' ],
+ components: { Popover },
methods: {
deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm'))
if (confirmed) {
this.$store.dispatch('deleteStatus', { id: this.status.id })
}
},
pinStatus () {
this.$store.dispatch('pinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
unpinStatus () {
this.$store.dispatch('unpinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
muteConversation () {
this.$store.dispatch('muteConversation', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
unmuteConversation () {
this.$store.dispatch('unmuteConversation', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
}
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
canDelete () {
if (!this.currentUser) { return }
const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
return superuser || this.status.user.id === this.currentUser.id
},
ownStatus () {
return this.status.user.id === this.currentUser.id
},
canPin () {
return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')
},
canMute () {
return !!this.currentUser
}
}
}
export default ExtraButtons
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 746f1c9175..3f00342528 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -1,71 +1,75 @@
<template>
- <v-popover
+ <Popover
v-if="canDelete || canMute || canPin"
trigger="click"
- placement="top"
+ placement="bottom"
class="extra-button-popover"
>
- <div slot="popover">
+ <div slot="content">
<div class="dropdown-menu">
<button
v-if="canMute && !status.thread_muted"
class="dropdown-item dropdown-item-icon"
@click.prevent="muteConversation"
>
<i class="icon-eye-off" /><span>{{ $t("status.mute_conversation") }}</span>
</button>
<button
v-if="canMute && status.thread_muted"
class="dropdown-item dropdown-item-icon"
@click.prevent="unmuteConversation"
>
<i class="icon-eye-off" /><span>{{ $t("status.unmute_conversation") }}</span>
</button>
<button
v-if="!status.pinned && canPin"
v-close-popover
class="dropdown-item dropdown-item-icon"
@click.prevent="pinStatus"
>
<i class="icon-pin" /><span>{{ $t("status.pin") }}</span>
</button>
<button
v-if="status.pinned && canPin"
v-close-popover
class="dropdown-item dropdown-item-icon"
@click.prevent="unpinStatus"
>
<i class="icon-pin" /><span>{{ $t("status.unpin") }}</span>
</button>
<button
v-if="canDelete"
v-close-popover
class="dropdown-item dropdown-item-icon"
@click.prevent="deleteStatus"
>
<i class="icon-cancel" /><span>{{ $t("status.delete") }}</span>
</button>
</div>
</div>
- <div class="button-icon">
+ <div slot="trigger" class="button-icon">
<i class="icon-ellipsis" />
</div>
- </v-popover>
+ </Popover>
</template>
<script src="./extra_buttons.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
@import '../popper/popper.scss';
+.dropdown-menu {
+ flex-shrink: 0;
+}
+
.icon-ellipsis {
cursor: pointer;
&:hover,
.extra-button-popover.open & {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
</style>
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
new file mode 100644
index 0000000000..4ee083232f
--- /dev/null
+++ b/src/components/popover/popover.js
@@ -0,0 +1,113 @@
+
+const Popover = {
+ name: 'Popover',
+ props: [
+ 'trigger',
+ 'placement',
+ 'show'
+ ],
+ data () {
+ return {
+ hidden: true,
+ styles: { opacity: 0 }
+ }
+ },
+ computed: {
+ display () {
+ return !this.hidden
+ }
+ },
+ methods: {
+ /*
+ registerPopover (e) {
+ if (!this.targetId) {
+ this.$store.dispatch('registerPopover', e.target).then(id => this.targetId = id)
+ }
+ },
+ unregisterPopover () {
+ if (this.targetId) {
+ this.$store.dispatch('unregisterPopover', this.targetId)
+ this.targetId = null
+ }
+ },
+ */
+ showPopover () {
+ this.hidden = false
+ this.$nextTick(function () {
+ if (this.hidden) return { opacity: 0 }
+
+ const anchorEl = this.$refs.trigger || this.$el
+ console.log(anchorEl)
+ const screenBox = anchorEl.getBoundingClientRect()
+ const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top}
+ const content = this.$refs.content
+ let horizOffset = 0
+
+ if ((origin.x - content.offsetWidth * 0.5) < 25) {
+ horizOffset += -(origin.x - content.offsetWidth * 0.5) + 25
+ }
+
+ console.log((origin.x + content.offsetWidth * 0.5), (window.innerWidth - 25))
+ if ((origin.x + content.offsetWidth * 0.5) > window.innerWidth - 25) {
+ horizOffset -= (origin.x + content.offsetWidth * 0.5) - (window.innerWidth - 25)
+ }
+ // 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 > (window.innerHeight - 25)) usingTop = true
+ if (origin.y - content.offsetHeight < 50) usingTop = false
+
+ const vertAlign = usingTop ?
+ {
+ bottom: `${anchorEl.offsetHeight}px`
+ } :
+ {
+ top: `${anchorEl.offsetHeight}px`
+ }
+ this.styles = {
+ opacity: '100%',
+ left: `${(anchorEl.offsetLeft + anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset}px`,
+ ...vertAlign
+ }
+ })
+ },
+ hidePopover () {
+ this.hidden = true
+ this.styles = { opacity: 0 }
+ },
+ onMouseenter (e) {
+ console.log(this.trigger)
+ 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
+ console.log(e.target)
+ this.hidePopover()
+ }
+ },
+ created () {
+ document.addEventListener("click", this.onClickOutside)
+ },
+ destroyed () {
+ document.removeEventListener("click", this.onClickOutside)
+ this.hidePopover()
+ }
+}
+
+export default Popover
diff --git a/src/components/popper/popper.scss b/src/components/popover/popover.vue
similarity index 76%
copy from src/components/popper/popper.scss
copy to src/components/popover/popover.vue
index 06daa8714a..273a0a69fe 100644
--- a/src/components/popper/popper.scss
+++ b/src/components/popover/popover.vue
@@ -1,147 +1,195 @@
+<template>
+ <!-- This is for the weird portal shit
+ <div
+ @mouseenter="registerPopover"
+ @mouseleave="unregisterPopover"
+ >
+ <slot name="trigger"></slot>
+ <portal
+ v-if="targetId"
+ :to="targetId"
+ >
+ <slot name="content"></slot>
+ </portal>
+ </div>
+ -->
+ <div
+ @mouseenter="onMouseenter"
+ @mouseleave="onMouseleave"
+ >
+ <div @click="onClick" ref="trigger">
+ <slot name="trigger"></slot>
+ </div>
+ <div
+ v-if="display"
+ :style="styles"
+ class="popover"
+ >
+ <div
+ ref="content"
+ class="popover-inner"
+ >
+ <!-- onSuccess is to mimic basic functionality of v-popover -->
+ <slot
+ name="content"
+ @onSuccess="hidePopover"
+ ></slot>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script src="./popover.js" />
+
+<style lang=scss>
@import '../../_variables.scss';
-.tooltip.popover {
+.popover {
z-index: 8;
+ position: absolute;
+ min-width: 0;
.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(--bg, $fallback--bg);
}
.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;
+ 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: normal;
+ white-space: nowrap;
border: none;
border-radius: 0px;
background-color: transparent;
box-shadow: none;
width: 100%;
height: 100%;
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
}
}
&:hover {
// TODO: improve the look on breeze themes
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
box-shadow: none;
}
}
}
+</style>
diff --git a/src/components/popover/popover_target.js b/src/components/popover/popover_target.js
new file mode 100644
index 0000000000..60bed5c792
--- /dev/null
+++ b/src/components/popover/popover_target.js
@@ -0,0 +1,23 @@
+const PopoverTarget = {
+ name: 'PopoverTarget',
+ computed: {
+ ids () {
+ const popovers = this.$store.state.popover.popovers
+ return Object.keys(popovers)
+ },
+ styles () {
+ return this.ids.reduce((acc, id) => {
+ const el = this.$store.state.popover.popovers[id]
+ const box = el.getBoundingClientRect()
+ acc[id] = {
+ top: `${box.y}px`,
+ left: `${box.x}px`
+ }
+ console.log(acc)
+ return acc
+ }, {})
+ }
+ }
+}
+
+export default PopoverTarget
diff --git a/src/components/popover/popover_target.vue b/src/components/popover/popover_target.vue
new file mode 100644
index 0000000000..f012823c33
--- /dev/null
+++ b/src/components/popover/popover_target.vue
@@ -0,0 +1,25 @@
+<template>
+ <div class="popover-targets">
+ <portal-target
+ v-for="id in ids"
+ :key="id"
+ :name="id"
+ :style="styles[id]"
+ class="popover-target"
+ />
+ </div>
+</template>
+<script src="./popover_target.js" />
+<style lang="scss">
+
+.popover-targets {
+ top: 0;
+ left: 0;
+ position: fixed;
+}
+
+.popover-target {
+ position: fixed;
+}
+
+</style>
diff --git a/src/components/popper/popper.scss b/src/components/popper/popper.scss
index 06daa8714a..e69de29bb2 100644
--- a/src/components/popper/popper.scss
+++ b/src/components/popper/popper.scss
@@ -1,147 +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(--bg, $fallback--bg);
- }
-
- .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%;
-
- &-icon {
- padding-left: 0.5rem;
-
- i {
- margin-right: 0.25rem;
- }
- }
-
- &:hover {
- // TODO: improve the look on breeze themes
- background-color: $fallback--fg;
- background-color: var(--btn, $fallback--fg);
- box-shadow: none;
- }
- }
-}
diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js
index 19f16bd980..4055e5cb48 100644
--- a/src/components/status_popover/status_popover.js
+++ b/src/components/status_popover/status_popover.js
@@ -1,34 +1,35 @@
import { find } from 'lodash'
const StatusPopover = {
name: 'StatusPopover',
props: [
'statusId'
],
data () {
return {
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
}
},
computed: {
status () {
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
}
},
components: {
- Status: () => import('../status/status.vue')
+ Status: () => import('../status/status.vue'),
+ Popover: () => import('../popover/popover.vue')
},
methods: {
enter () {
if (!this.status) {
this.$store.dispatch('fetchStatus', this.statusId)
}
}
}
}
export default StatusPopover
diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue
index eacf4c0675..9f37e4e7a1 100644
--- a/src/components/status_popover/status_popover.vue
+++ b/src/components/status_popover/status_popover.vue
@@ -1,85 +1,81 @@
<template>
- <v-popover
- popover-class="status-popover"
- placement="top-start"
- :popper-options="popperOptions"
- @show="enter()"
- >
- <template slot="popover">
+ <Popover trigger="hover">
+ <template slot="trigger">
+ <slot />
+ </template>
+ <div slot="content" class="status-popover">
<Status
v-if="status"
:is-preview="true"
:statusoid="status"
:compact="true"
/>
<div
v-else
class="status-preview-loading"
>
<i class="icon-spin4 animate-spin" />
</div>
- </template>
-
- <slot />
- </v-popover>
+ </div>
+ </Popover>
</template>
<script src="./status_popover.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
-.tooltip.popover.status-popover {
+.status-popover {
font-size: 1rem;
min-width: 15em;
max-width: 95%;
- margin-left: 0.5em;
+ margin: 0.5em 0;
.popover-inner {
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-style: solid;
border-width: 1px;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
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 {
border: none;
}
.status-preview-loading {
padding: 1em;
text-align: center;
i {
font-size: 2em;
}
}
}
</style>
diff --git a/src/main.js b/src/main.js
index baf73ac8b5..d74ff63ad7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,106 +1,108 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import 'custom-event-polyfill'
import './lib/event_target_polyfill.js'
import interfaceModule from './modules/interface.js'
import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
import chatModule from './modules/chat.js'
import oauthModule from './modules/oauth.js'
import authFlowModule from './modules/auth_flow.js'
import mediaViewerModule from './modules/media_viewer.js'
import oauthTokensModule from './modules/oauth_tokens.js'
import reportsModule from './modules/reports.js'
import pollsModule from './modules/polls.js'
import postStatusModule from './modules/postStatus.js'
+import popoverModule from './modules/popover.js'
import VueI18n from 'vue-i18n'
import createPersistedState from './lib/persisted_state.js'
import pushNotifications from './lib/push_notifications_plugin.js'
import messages from './i18n/messages.js'
import VueChatScroll from 'vue-chat-scroll'
import VueClickOutside from 'v-click-outside'
import PortalVue from 'portal-vue'
import VBodyScrollLock from './directives/body_scroll_lock'
import VTooltip from 'v-tooltip'
import afterStoreSetup from './boot/after_store.js'
const currentLocale = (window.navigator.language || 'en').split('-')[0]
Vue.use(Vuex)
Vue.use(VueRouter)
Vue.use(VueI18n)
Vue.use(VueChatScroll)
Vue.use(VueClickOutside)
Vue.use(PortalVue)
Vue.use(VBodyScrollLock)
Vue.use(VTooltip, {
popover: {
defaultTrigger: 'hover click',
defaultContainer: false,
defaultOffset: 5
}
})
const i18n = new VueI18n({
// By default, use the browser locale, we will update it if neccessary
locale: currentLocale,
fallbackLocale: 'en',
messages
})
const persistedStateOptions = {
paths: [
'config',
'users.lastLoginName',
'oauth'
]
};
(async () => {
const persistedState = await createPersistedState(persistedStateOptions)
const store = new Vuex.Store({
modules: {
i18n: {
getters: {
i18n: () => i18n
}
},
interface: interfaceModule,
instance: instanceModule,
statuses: statusesModule,
users: usersModule,
api: apiModule,
config: configModule,
chat: chatModule,
oauth: oauthModule,
authFlow: authFlowModule,
mediaViewer: mediaViewerModule,
oauthTokens: oauthTokensModule,
reports: reportsModule,
polls: pollsModule,
- postStatus: postStatusModule
+ postStatus: postStatusModule,
+ popover: popoverModule
},
plugins: [persistedState, pushNotifications],
strict: false // Socket modifies itself, let's ignore this for now.
// strict: process.env.NODE_ENV !== 'production'
})
afterStoreSetup({ store, i18n })
})()
// These are inlined by webpack's DefinePlugin
/* eslint-disable */
window.___pleromafe_mode = process.env
window.___pleromafe_commit_hash = COMMIT_HASH
window.___pleromafe_dev_overrides = DEV_OVERRIDES
diff --git a/src/modules/popover.js b/src/modules/popover.js
new file mode 100644
index 0000000000..5b01ccbb45
--- /dev/null
+++ b/src/modules/popover.js
@@ -0,0 +1,31 @@
+import { omit } from 'lodash'
+import { set } from 'vue'
+
+const popover = {
+ state: {
+ popovers: {}
+ },
+ mutations: {
+ registerPopover (state, { id, el }) {
+ set(state.popovers, id, el)
+ },
+ unregisterPopover (state, { id }) {
+ state.popovers = omit(state.popovers, id)
+ }
+ },
+ actions: {
+ registerPopover (store, el) {
+ // Generate unique id, it will be used to link portal and portal-target
+ // popover-target will make portal targets for each registered popover
+ // el will be used by popover target to put popovers in their places.
+ let id = Math.floor(Math.random() * 1000000).toString()
+ store.commit('registerPopover', { id, el })
+ return id
+ },
+ unregisterPopover (store, id) {
+ store.commit('unregisterPopover', { id })
+ }
+ }
+}
+
+export default popover
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 1, 5:11 AM (13 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1802509
Default Alt Text
(33 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline