Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop

This commit is contained in:
sadposter 2021-06-25 10:07:29 +01:00
commit 214d85189c
84 changed files with 2237 additions and 1153 deletions

View file

@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added a quick settings to timeline header for easier access
- Added option to mark posts as sensitive by default
- Added quick filters for notifications
- Implemented user option to change sidebar position to the right side
- Implemented user option to hide floating shout panel
### Fixed
- Fixed follow request count showing in the wrong location in mobile view
## [2.3.0] - 2021-03-01
### Fixed

View file

@ -21,6 +21,7 @@ var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
writeToDisk: true,
stats: {
colors: true,
chunks: false

View file

@ -3,6 +3,7 @@ var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
var CopyPlugin = require('copy-webpack-plugin');
var env = process.env.NODE_ENV
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
@ -93,6 +94,19 @@ module.exports = {
new ServiceWorkerWebpackPlugin({
entry: path.join(__dirname, '..', 'src/sw.js'),
filename: 'sw-pleroma.js'
}),
// This copies Ruffle's WASM to a directory so that JS side can access it
new CopyPlugin({
patterns: [
{
from: "node_modules/ruffle-mirror/*",
to: "static/ruffle",
flatten: true
},
],
options: {
concurrency: 100,
},
})
]
}

View file

@ -32,6 +32,7 @@
"phoenix": "^1.3.0",
"portal-vue": "^2.1.4",
"punycode.js": "^2.1.0",
"ruffle-mirror": "^2021.4.10",
"v-click-outside": "^2.1.1",
"vue": "^2.6.11",
"vue-i18n": "^7.3.2",
@ -57,6 +58,7 @@
"chalk": "^1.1.3",
"chromedriver": "^87.0.1",
"connect-history-api-fallback": "^1.1.0",
"copy-webpack-plugin": "^6.4.1",
"cross-spawn": "^4.0.2",
"css-loader": "^0.28.0",
"custom-event-polyfill": "^1.0.7",
@ -111,7 +113,7 @@
"url-loader": "^1.1.2",
"vue-loader": "^14.0.0",
"vue-style-loader": "^4.0.0",
"webpack": "^4.0.0",
"webpack": "^4.44.0",
"webpack-dev-middleware": "^3.6.0",
"webpack-hot-middleware": "^2.12.2",
"webpack-merge": "^0.14.1"

View file

@ -4,7 +4,7 @@ import Notifications from './components/notifications/notifications.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 ShoutPanel from './components/shout_panel/shout_panel.vue'
import SettingsModal from './components/settings_modal/settings_modal.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
@ -26,7 +26,7 @@ export default {
InstanceSpecificPanel,
FeaturesPanel,
WhoToFollowPanel,
ChatPanel,
ShoutPanel,
MediaModal,
SideDrawer,
MobilePostStatusButton,
@ -65,7 +65,7 @@ export default {
}
}
},
chat () { return this.$store.state.chat.channel.state === 'joined' },
shout () { return this.$store.state.shout.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
@ -73,11 +73,14 @@ export default {
this.$store.state.instance.instanceSpecificPanelContent
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
hideShoutbox () {
return this.$store.getters.mergedConfig.hideShoutbox
},
isMobileLayout () { return this.$store.state.interface.mobileLayout },
privateMode () { return this.$store.state.instance.private },
sidebarAlign () {
return {
'order': this.$store.state.instance.sidebarRight ? 99 : 0
'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0
}
},
...mapGetters(['mergedConfig'])

View file

@ -187,7 +187,7 @@ a {
}
}
input, textarea, .select, .input {
input, textarea, .input {
&.unstyled {
border-radius: 0;
@ -217,47 +217,11 @@ input, textarea, .select, .input {
hyphens: none;
padding: 8px .5em;
&.select {
padding: 0;
}
&:disabled, &[disabled=disabled] {
&:disabled, &[disabled=disabled], &.disabled {
cursor: not-allowed;
opacity: 0.5;
}
.select-down-icon {
position: absolute;
top: 0;
bottom: 0;
right: 5px;
height: 100%;
color: $fallback--text;
color: var(--inputText, $fallback--text);
line-height: 28px;
z-index: 0;
pointer-events: none;
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
color: $fallback--text;
color: var(--inputText, --text, $fallback--text);
margin: 0;
padding: 0 2em 0 .2em;
font-family: sans-serif;
font-family: var(--inputFont, sans-serif);
font-size: 14px;
width: 100%;
z-index: 1;
height: 28px;
line-height: 16px;
}
&[type=range] {
background: none;
border: none;
@ -830,13 +794,6 @@ nav {
}
}
.select-multiple {
display: flex;
.option-list {
margin: 0;
padding-left: .5em;
}
}
.setting-list,
.option-list{
list-style-type: none;

View file

@ -49,10 +49,10 @@
</div>
<media-modal />
</div>
<chat-panel
v-if="currentUser && chat"
<shout-panel
v-if="currentUser && shout && !hideShoutbox"
:floating="true"
class="floating-chat mobile-hidden"
class="floating-shout mobile-hidden"
/>
<MobilePostStatusButton />
<UserReportingModal />

View file

@ -240,7 +240,7 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })
store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') })
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })

View file

@ -16,7 +16,7 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue'
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
import Notifications from 'components/notifications/notifications.vue'
import AuthForm from 'components/auth_form/auth_form.js'
import ChatPanel from 'components/chat_panel/chat_panel.vue'
import ShoutPanel from 'components/shout_panel/shout_panel.vue'
import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
import About from 'components/about/about.vue'
import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue'
@ -64,7 +64,7 @@ export default (store) => {
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
{ name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute },
{ name: 'login', path: '/login', component: AuthForm },
{ name: 'chat-panel', path: '/chat-panel', component: ChatPanel, props: () => ({ floating: false }) },
{ name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) },
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
{ name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },

View file

@ -6,10 +6,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
class="account-tools-popover"
>
<template v-slot:content>
<div class="dropdown-menu">
<template v-if="relationship.following">
<button
@ -59,16 +56,15 @@
{{ $t('user_card.message') }}
</button>
</div>
</div>
<div
slot="trigger"
class="ellipsis-button"
>
<FAIcon
class="icon"
icon="ellipsis-v"
/>
</div>
</template>
<template v-slot:trigger>
<button class="button-unstyled ellipsis-button">
<FAIcon
class="icon"
icon="ellipsis-v"
/>
</button>
</template>
</Popover>
</div>
</template>
@ -83,7 +79,6 @@
}
.ellipsis-button {
cursor: pointer;
width: 2.5em;
margin: -0.5em 0;
padding: 0.5em 0;

View file

@ -1,4 +1,5 @@
import StillImage from '../still-image/still-image.vue'
import Flash from '../flash/flash.vue'
import VideoAttachment from '../video_attachment/video_attachment.vue'
import nsfwImage from '../../assets/nsfw.png'
import fileTypeService from '../../services/file_type/file_type.service.js'
@ -43,6 +44,7 @@ const Attachment = {
}
},
components: {
Flash,
StillImage,
VideoAttachment
},

View file

@ -117,6 +117,11 @@
<!-- eslint-enable vue/no-v-html -->
</div>
</div>
<Flash
v-if="type === 'flash'"
:src="attachment.large_thumb_url || attachment.url"
/>
</div>
</template>
@ -172,6 +177,7 @@
}
.non-gallery.attachment {
&.flash,
&.video {
flex: 1 0 40%;
}

View file

@ -23,10 +23,7 @@
class="timeline"
>
<List :items="sortedChatList">
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<ChatListItem
:key="item.id"
:compact="false"

View file

@ -50,7 +50,7 @@
@show="menuOpened = true"
@close="menuOpened = false"
>
<div slot="content">
<template v-slot:content>
<div class="dropdown-menu">
<button
class="button-default dropdown-item dropdown-item-icon"
@ -59,26 +59,28 @@
<FAIcon icon="times" /> {{ $t("chats.delete") }}
</button>
</div>
</div>
<button
slot="trigger"
class="button-default menu-icon"
:title="$t('chats.more')"
>
<FAIcon icon="ellipsis-h" />
</button>
</template>
<template v-slot:trigger>
<button
class="button-default menu-icon"
:title="$t('chats.more')"
>
<FAIcon icon="ellipsis-h" />
</button>
</template>
</Popover>
</div>
<StatusContent
:status="messageForStatusContent"
:full-content="true"
>
<span
slot="footer"
class="created-at"
>
{{ createdAt }}
</span>
<template v-slot:footer>
<span
class="created-at"
>
{{ createdAt }}
</span>
</template>
</StatusContent>
</div>
</div>

View file

@ -9,7 +9,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('domain_mute_card.unmute_progress') }}
</template>
</ProgressButton>
@ -19,7 +19,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.mute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('domain_mute_card.mute_progress') }}
</template>
</ProgressButton>

View file

@ -57,6 +57,7 @@ const EmojiInput = {
required: true,
type: Function
},
// TODO VUE3: change to modelValue, change 'input' event to 'input'
value: {
/**
* Used for v-model
@ -143,32 +144,31 @@ const EmojiInput = {
}
},
mounted () {
const slots = this.$slots.default
if (!slots || slots.length === 0) return
const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag))
const { root } = this.$refs
const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea')
if (!input) return
this.input = input
this.resize()
input.elm.addEventListener('blur', this.onBlur)
input.elm.addEventListener('focus', this.onFocus)
input.elm.addEventListener('paste', this.onPaste)
input.elm.addEventListener('keyup', this.onKeyUp)
input.elm.addEventListener('keydown', this.onKeyDown)
input.elm.addEventListener('click', this.onClickInput)
input.elm.addEventListener('transitionend', this.onTransition)
input.elm.addEventListener('input', this.onInput)
input.addEventListener('blur', this.onBlur)
input.addEventListener('focus', this.onFocus)
input.addEventListener('paste', this.onPaste)
input.addEventListener('keyup', this.onKeyUp)
input.addEventListener('keydown', this.onKeyDown)
input.addEventListener('click', this.onClickInput)
input.addEventListener('transitionend', this.onTransition)
input.addEventListener('input', this.onInput)
},
unmounted () {
const { input } = this
if (input) {
input.elm.removeEventListener('blur', this.onBlur)
input.elm.removeEventListener('focus', this.onFocus)
input.elm.removeEventListener('paste', this.onPaste)
input.elm.removeEventListener('keyup', this.onKeyUp)
input.elm.removeEventListener('keydown', this.onKeyDown)
input.elm.removeEventListener('click', this.onClickInput)
input.elm.removeEventListener('transitionend', this.onTransition)
input.elm.removeEventListener('input', this.onInput)
input.removeEventListener('blur', this.onBlur)
input.removeEventListener('focus', this.onFocus)
input.removeEventListener('paste', this.onPaste)
input.removeEventListener('keyup', this.onKeyUp)
input.removeEventListener('keydown', this.onKeyDown)
input.removeEventListener('click', this.onClickInput)
input.removeEventListener('transitionend', this.onTransition)
input.removeEventListener('input', this.onInput)
}
},
watch: {
@ -216,7 +216,7 @@ const EmojiInput = {
}, 0)
},
togglePicker () {
this.input.elm.focus()
this.input.focus()
this.showPicker = !this.showPicker
if (this.showPicker) {
this.scrollIntoView()
@ -262,13 +262,13 @@ const EmojiInput = {
this.$emit('input', newValue)
const position = this.caret + (insertion + spaceAfter + spaceBefore).length
if (!keepOpen) {
this.input.elm.focus()
this.input.focus()
}
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
// Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position)
this.input.setSelectionRange(position, position)
this.caret = position
})
},
@ -285,9 +285,9 @@ const EmojiInput = {
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
this.input.elm.focus()
this.input.focus()
// Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position)
this.input.setSelectionRange(position, position)
this.caret = position
})
e.preventDefault()
@ -349,7 +349,7 @@ const EmojiInput = {
}
this.$nextTick(() => {
const { offsetHeight } = this.input.elm
const { offsetHeight } = this.input
const { picker } = this.$refs
const pickerBottom = picker.$el.getBoundingClientRect().bottom
if (pickerBottom > window.innerHeight) {
@ -414,8 +414,8 @@ const EmojiInput = {
// Scroll the input element to the position of the cursor
this.$nextTick(() => {
this.input.elm.blur()
this.input.elm.focus()
this.input.blur()
this.input.focus()
})
}
// Disable suggestions hotkeys if suggestions are hidden
@ -444,7 +444,7 @@ const EmojiInput = {
// de-focuses the element (i.e. default browser behavior)
if (key === 'Escape') {
if (!this.temporarilyHideSuggestions) {
this.input.elm.focus()
this.input.focus()
}
}
@ -480,7 +480,7 @@ const EmojiInput = {
if (!panel) return
const picker = this.$refs.picker.$el
const panelBody = this.$refs['panel-body']
const { offsetHeight, offsetTop } = this.input.elm
const { offsetHeight, offsetTop } = this.input
const offsetBottom = offsetTop + offsetHeight
this.setPlacement(panelBody, panel, offsetBottom)
@ -494,7 +494,7 @@ const EmojiInput = {
if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) {
target.style.top = 'auto'
target.style.bottom = this.input.elm.offsetHeight + 'px'
target.style.bottom = this.input.offsetHeight + 'px'
}
},
overflowsBottom (el) {

View file

@ -3,6 +3,7 @@
v-click-outside="onClickOutside"
class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }"
ref='root'
>
<slot />
<template v-if="enableEmojiPicker">

View file

@ -7,10 +7,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
slot-scope="{close}"
>
<template v-slot:content="{close}">
<div class="dropdown-menu">
<button
v-if="canMute && !status.thread_muted"
@ -120,16 +117,15 @@
/><span>{{ $t("user_card.report") }}</span>
</button>
</div>
</div>
<span
slot="trigger"
class="popover-trigger"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</span>
</template>
<template v-slot:trigger>
<button class="button-unstyled popover-trigger">
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</button>
</template>
</Popover>
</template>

View file

@ -2,7 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
const FeaturesPanel = {
computed: {
chat: function () { return this.$store.state.instance.chatAvailable },
shout: function () { return this.$store.state.instance.shoutAvailable },
pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable },
gopher: function () { return this.$store.state.instance.gopherAvailable },
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },

View file

@ -8,8 +8,8 @@
</div>
<div class="panel-body features-panel">
<ul>
<li v-if="chat">
{{ $t('features_panel.chat') }}
<li v-if="shout">
{{ $t('features_panel.shout') }}
</li>
<li v-if="pleromaChatMessages">
{{ $t('features_panel.pleroma_chat_messages') }}

View file

@ -0,0 +1,52 @@
import RuffleService from '../../services/ruffle_service/ruffle_service.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faStop,
faExclamationTriangle
} from '@fortawesome/free-solid-svg-icons'
library.add(
faStop,
faExclamationTriangle
)
const Flash = {
props: [ 'src' ],
data () {
return {
player: false, // can be true, "hidden", false. hidden = element exists
loaded: false,
ruffleInstance: null
}
},
methods: {
openPlayer () {
if (this.player) return // prevent double-loading, or re-loading on failure
this.player = 'hidden'
RuffleService.getRuffle().then((ruffle) => {
const player = ruffle.newest().createPlayer()
player.config = {
letterbox: 'on'
}
const container = this.$refs.container
container.appendChild(player)
player.style.width = '100%'
player.style.height = '100%'
player.load(this.src).then(() => {
this.player = true
}).catch((e) => {
console.error('Error loading ruffle', e)
this.player = 'error'
})
this.ruffleInstance = player
})
},
closePlayer () {
console.log(this.ruffleInstance)
this.ruffleInstance.remove()
this.player = false
}
}
}
export default Flash

View file

@ -0,0 +1,88 @@
<template>
<div class="Flash">
<div
v-if="player === true || player === 'hidden'"
ref="container"
class="player"
:class="{ hidden: player === 'hidden' }"
/>
<button
v-if="player !== true"
class="button-unstyled placeholder"
@click="openPlayer"
>
<span
v-if="player === 'hidden'"
class="label"
>
{{ $t('general.loading') }}
</span>
<span
v-if="player === 'error'"
class="label"
>
{{ $t('general.flash_fail') }}
</span>
<span
v-else
class="label"
>
<p>
{{ $t('general.flash_content') }}
</p>
<p>
<FAIcon icon="exclamation-triangle" />
{{ $t('general.flash_security') }}
</p>
</span>
</button>
<button
v-if="player"
class="button-unstyled hider"
@click="closePlayer"
>
<FAIcon icon="stop" />
</button>
</div>
</template>
<script src="./flash.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.Flash {
width: 100%;
height: 260px;
position: relative;
.player {
height: 100%;
width: 100%;
}
.hider {
top: 0;
}
.label {
text-align: center;
flex: 1 1 0;
line-height: 1.2;
white-space: normal;
word-wrap: normal;
}
.hidden {
display: none;
visibility: 'hidden';
}
.placeholder {
height: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View file

@ -1,14 +1,10 @@
import { set } from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
import Select from '../select/select.vue'
export default {
components: {
Select
},
props: [
'name', 'label', 'value', 'fallback', 'options', 'no-inherit'
],

View file

@ -22,30 +22,20 @@
class="opt-l"
:for="name + '-o'"
/>
<label
:for="name + '-font-switcher'"
class="select"
<Select
:id="name + '-font-switcher'"
v-model="preset"
:disabled="!present"
class="font-switcher"
>
<select
:id="name + '-font-switcher'"
v-model="preset"
:disabled="!present"
class="font-switcher"
<option
v-for="option in availableOptions"
:key="option"
:value="option"
>
<option
v-for="option in availableOptions"
:key="option"
:value="option"
>
{{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
</option>
</Select>
<input
v-if="isCustom"
:id="name"
@ -65,7 +55,8 @@
min-width: 10em;
}
&.custom {
.select {
/* TODO Should make proper joiners... */
.font-switcher {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

View file

@ -3,27 +3,18 @@
<label for="interface-language-switcher">
{{ $t('settings.interfaceLanguage') }}
</label>
<label
for="interface-language-switcher"
class="select"
<Select
id="interface-language-switcher"
v-model="language"
>
<select
id="interface-language-switcher"
v-model="language"
<option
v-for="lang in languages"
:key="lang.code"
:value="lang.code"
>
<option
v-for="lang in languages"
:key="lang.code"
:value="lang.code"
>
{{ lang.name }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ lang.name }}
</option>
</Select>
</div>
</template>
@ -32,16 +23,12 @@ import languagesObject from '../../i18n/messages'
import localeService from '../../services/locale/locale.service.js'
import ISO6391 from 'iso-639-1'
import _ from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
import Select from '../select/select.vue'
export default {
components: {
Select
},
computed: {
languages () {
return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))

View file

@ -8,7 +8,7 @@
@show="setToggled(true)"
@close="setToggled(false)"
>
<div slot="content">
<template v-slot:content>
<div class="dropdown-menu">
<span v-if="user.is_local">
<button
@ -121,26 +121,27 @@
</button>
</span>
</div>
</div>
<button
slot="trigger"
class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
<FAIcon icon="chevron-down" />
</button>
</template>
<template v-slot:trigger>
<button
class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
<FAIcon icon="chevron-down" />
</button>
</template>
</Popover>
<portal to="modal">
<DialogModal
v-if="showDeleteUserDialog"
:on-cancel="deleteUserDialog.bind(this, false)"
>
<template slot="header">
<template v-slot:header>
{{ $t('user_card.admin_menu.delete_user') }}
</template>
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
<template slot="footer">
<template v-slot:footer>
<button
class="btn button-default"
@click="deleteUserDialog(false)"

View file

@ -5,9 +5,7 @@
placement="bottom"
:bound-to="{ x: 'container' }"
>
<template
v-slot:content
>
<template v-slot:content>
<div class="dropdown-menu">
<button
class="button-default dropdown-item"
@ -66,7 +64,9 @@
</div>
</template>
<template v-slot:trigger>
<FAIcon icon="filter" />
<button class="button-unstyled">
<FAIcon icon="filter" />
</button>
</template>
</Popover>
</template>

View file

@ -1,19 +1,21 @@
import * as DateUtils from 'src/services/date_utils/date_utils.js'
import { uniq } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core'
import Select from '../select/select.vue'
import {
faTimes,
faChevronDown,
faPlus
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes,
faChevronDown,
faPlus
)
export default {
components: {
Select
},
name: 'PollForm',
props: ['visible'],
data: () => ({

View file

@ -46,23 +46,19 @@
class="poll-type"
:title="$t('polls.type')"
>
<label
for="poll-type-selector"
class="select"
<Select
v-model="pollType"
class="poll-type-select"
unstyled="true"
@change="updatePollToParent"
>
<select
v-model="pollType"
class="select"
@change="updatePollToParent"
>
<option value="single">{{ $t('polls.single_choice') }}</option>
<option value="multiple">{{ $t('polls.multiple_choices') }}</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
<option value="single">
{{ $t('polls.single_choice') }}
</option>
<option value="multiple">
{{ $t('polls.multiple_choices') }}
</option>
</Select>
</div>
<div
class="poll-expiry"
@ -76,24 +72,20 @@
:max="maxExpirationInCurrentUnit"
@change="expiryAmountChange"
>
<label class="expiry-unit select">
<select
v-model="expiryUnit"
@change="expiryAmountChange"
<Select
v-model="expiryUnit"
unstyled="true"
class="expiry-unit"
@change="expiryAmountChange"
>
<option
v-for="unit in expiryUnits"
:key="unit"
:value="unit"
>
<option
v-for="unit in expiryUnits"
:key="unit"
:value="unit"
>
{{ $t(`time.${unit}_short`, ['']) }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ $t(`time.${unit}_short`, ['']) }}
</option>
</Select>
</div>
</div>
</div>
@ -147,10 +139,8 @@
.poll-type {
margin-right: 0.75em;
flex: 1 1 60%;
.select {
border: none;
box-shadow: none;
background-color: transparent;
.poll-type-select {
padding-right: 0.75em;
}
}
@ -162,12 +152,6 @@
width: 3em;
text-align: right;
}
.expiry-unit {
border: none;
box-shadow: none;
background-color: transparent;
}
}
}
</style>

View file

@ -54,7 +54,7 @@ const Popover = {
}
// 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".
// its children are what are inside the slot. Expect only one v-slot:trigger.
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
// SVGs don't have offsetWidth/Height, use fallback
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth

View file

@ -11,10 +11,10 @@ import { reject, map, uniqBy, debounce } from 'lodash'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown,
faSmileBeam,
faPollH,
faUpload,
@ -24,7 +24,6 @@ import {
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown,
faSmileBeam,
faPollH,
faUpload,
@ -84,6 +83,7 @@ const PostStatusForm = {
PollForm,
ScopeSelector,
Checkbox,
Select,
Attachment,
StatusContent
},

View file

@ -189,28 +189,19 @@
v-if="postFormats.length > 1"
class="text-format"
>
<label
for="post-content-type"
class="select"
<Select
id="post-content-type"
v-model="newStatus.contentType"
class="form-control"
>
<select
id="post-content-type"
v-model="newStatus.contentType"
class="form-control"
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
</option>
</Select>
</div>
<div
v-if="postFormats.length === 1 && postFormats[0] !== 'text/plain'"

View file

@ -8,10 +8,7 @@
remove-padding
@show="focusInput"
>
<div
slot="content"
slot-scope="{close}"
>
<template v-slot:content="{close}">
<div class="reaction-picker-filter">
<input
v-model="filterWord"
@ -41,17 +38,18 @@
</span>
<div class="reaction-bottom-fader" />
</div>
</div>