polish the poll form
This commit is contained in:
parent
e2dc0d85fc
commit
1a989d0661
|
@ -264,6 +264,15 @@ option {
|
|||
background-color: var(--bg, $fallback--bg);
|
||||
}
|
||||
|
||||
.hide-number-spinner {
|
||||
-moz-appearance: textfield;
|
||||
&[type=number]::-webkit-inner-spin-button,
|
||||
&[type=number]::-webkit-outer-spin-button {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
i[class*=icon-] {
|
||||
color: $fallback--icon;
|
||||
color: var(--icon, $fallback--icon)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="poll-form" v-if="visible">
|
||||
<hr>
|
||||
<div class="poll-option" v-for="(option, index) in options" :key="index">
|
||||
<div class="input-container">
|
||||
<input
|
||||
|
@ -8,23 +7,28 @@
|
|||
type="text"
|
||||
:placeholder="$t('polls.option')"
|
||||
:maxlength="maxLength"
|
||||
:id="`poll-${index}`"
|
||||
v-model="options[index]"
|
||||
@change="updatePollToParent"
|
||||
@keydown.enter.stop.prevent="nextOption(index)"
|
||||
>
|
||||
</div>
|
||||
<div class="icon-container" v-if="options.length > 2">
|
||||
<i class="icon-cancel" @click="deleteOption(index)"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default add-option"
|
||||
type="button"
|
||||
<a
|
||||
v-if="options.length < maxOptions"
|
||||
class="add-option"
|
||||
@click="addOption"
|
||||
>{{ $t("polls.add_option") }}</button>
|
||||
>
|
||||
<i class="icon-plus" />
|
||||
{{ $t("polls.add_option") }}
|
||||
</a>
|
||||
<div class="poll-type-expiry">
|
||||
<div class="poll-type">
|
||||
<label for="poll-type-selector" class="select">
|
||||
<select id="poll-type-selector" v-model="pollType" @change="updatePollToParent">
|
||||
<select class="select" v-model="pollType" @change="updatePollToParent">
|
||||
<option value="single">{{$t('polls.single_choice')}}</option>
|
||||
<option value="multiple">{{$t('polls.multiple_choices')}}</option>
|
||||
</select>
|
||||
|
@ -32,10 +36,18 @@
|
|||
</label>
|
||||
</div>
|
||||
<div class="poll-expiry">
|
||||
<label for="poll-expiry-selector" class="select">
|
||||
<select id="poll-expiry-selector" v-model="pollExpiry" @change="updatePollToParent">
|
||||
<option v-for="(value, key) in expiryOptions" :value="key" v-bind:key="key">
|
||||
{{ value }}
|
||||
<input
|
||||
type="number"
|
||||
class="expiry-amount hide-number-spinner"
|
||||
min="1"
|
||||
max="120"
|
||||
v-model="expiryAmount"
|
||||
@change="expiryAmountChange"
|
||||
>
|
||||
<label class="expiry-unit select">
|
||||
<select v-model="expiryUnit" @change="updatePollToParent">
|
||||
<option v-for="unit in expiryUnits" :value="unit">
|
||||
{{ $t(`time.${unit}_short`, ['']) }}
|
||||
</option>
|
||||
</select>
|
||||
<i class="icon-down-open"/>
|
||||
|
@ -46,15 +58,17 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { pickBy } from 'lodash'
|
||||
import * as DateUtils from 'src/services/date_utils/date_utils'
|
||||
|
||||
export default {
|
||||
name: 'PollForm',
|
||||
props: ['visible'],
|
||||
data: () => ({
|
||||
pollType: 'single',
|
||||
pollExpiry: '86400',
|
||||
options: ['', '']
|
||||
options: ['', ''],
|
||||
expiryAmount: 1,
|
||||
expiryUnit: 'minutes',
|
||||
expiryUnits: ['minutes', 'hours', 'days']
|
||||
}),
|
||||
computed: {
|
||||
pollLimits () {
|
||||
|
@ -65,39 +79,57 @@ export default {
|
|||
},
|
||||
maxLength () {
|
||||
return this.pollLimits.max_option_chars
|
||||
},
|
||||
expiryOptions () {
|
||||
const minExpiration = this.pollLimits.min_expiration
|
||||
const maxExpiration = this.pollLimits.max_expiration
|
||||
const expiryOptions = this.$t('polls.expiry_options')
|
||||
|
||||
return pickBy(expiryOptions, (_value, key) => {
|
||||
if (key === 'custom') {
|
||||
return true
|
||||
}
|
||||
|
||||
const parsedKey = parseInt(key)
|
||||
|
||||
return (parsedKey >= minExpiration && parsedKey <= maxExpiration)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clear () {
|
||||
this.pollType = 'single'
|
||||
this.options = ['', '']
|
||||
this.expiryAmount = 1
|
||||
this.expiryUnit = 'minutes'
|
||||
},
|
||||
nextOption (index) {
|
||||
const element = this.$el.querySelector(`#poll-${index+1}`)
|
||||
if (element) {
|
||||
element.focus()
|
||||
} else {
|
||||
// Try adding an option and try focusing on it
|
||||
const addedOption = this.addOption()
|
||||
if (addedOption) {
|
||||
this.$nextTick(function () {
|
||||
this.nextOption(index)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
addOption () {
|
||||
if (this.options.length < this.maxOptions) {
|
||||
this.options.push('')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
deleteOption (index, event) {
|
||||
if (this.options.length > 2) {
|
||||
this.options.splice(index, 1)
|
||||
}
|
||||
},
|
||||
expiryAmountChange () {
|
||||
this.expiryAmount = Math.max(1, this.expiryAmount)
|
||||
this.expiryAmount = Math.min(120, this.expiryAmount)
|
||||
this.updatePollToParent()
|
||||
},
|
||||
updatePollToParent () {
|
||||
const unitMultiplier = this.expiryUnit === 'minutes' ? 60
|
||||
: this.expiryUnit === 'hours' ? 60 * 60
|
||||
: 60 * 60 * 24
|
||||
|
||||
const expiresIn = this.expiryAmount * unitMultiplier
|
||||
|
||||
this.$emit('update-poll', {
|
||||
options: this.options,
|
||||
expiresIn: this.pollExpiry,
|
||||
multiple: this.pollType === 'multiple'
|
||||
multiple: this.pollType === 'multiple',
|
||||
expiresIn
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -105,39 +137,69 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../../_variables.scss';
|
||||
|
||||
.poll-form {
|
||||
padding: 0 0.5em 0.6em;
|
||||
hr {
|
||||
margin: 0 0 0.8em;
|
||||
border: solid 1px #1c2735;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 0.5em 0.5em;
|
||||
|
||||
.add-option {
|
||||
margin: 0.8em 0 0.8em;
|
||||
width: 94%;
|
||||
align-self: flex-start;
|
||||
padding-top: 0.25em;
|
||||
cursor: pointer;
|
||||
color: $fallback--faint;
|
||||
color: var(--faint, $fallback--faint);
|
||||
}
|
||||
|
||||
.poll-option {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
width: 94%;
|
||||
width: 100%;
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
width: 5%;
|
||||
// Move the icon over the input box
|
||||
width: 2em;
|
||||
margin-left: -2em;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.poll-type-expiry {
|
||||
margin-top: 0.5em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 0.6em;
|
||||
width: 100%;
|
||||
}
|
||||
.poll-expiry-custom {
|
||||
display: none;
|
||||
input {
|
||||
width: 100%;
|
||||
|
||||
.poll-type {
|
||||
margin-right: 0.75em;
|
||||
flex: 1 1 60%;
|
||||
.select {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-expiry {
|
||||
display: flex;
|
||||
|
||||
.expiry-amount {
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
.expiry-unit {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ const PostStatusForm = {
|
|||
let statusText = preset || ''
|
||||
|
||||
const scopeCopy = typeof this.$store.state.config.scopeCopy === 'undefined'
|
||||
? this.$store.state.instance.scopeCopy
|
||||
: this.$store.state.config.scopeCopy
|
||||
? this.$store.state.instance.scopeCopy
|
||||
: this.$store.state.config.scopeCopy
|
||||
|
||||
if (this.replyTo) {
|
||||
const currentUser = this.$store.state.users.currentUser
|
||||
|
@ -59,8 +59,8 @@ const PostStatusForm = {
|
|||
}
|
||||
|
||||
const scope = (this.copyMessageScope && scopeCopy || this.copyMessageScope === 'direct')
|
||||
? this.copyMessageScope
|
||||
: this.$store.state.users.currentUser.default_scope
|
||||
? this.copyMessageScope
|
||||
: this.$store.state.users.currentUser.default_scope
|
||||
|
||||
const contentType = typeof this.$store.state.config.postContentType === 'undefined'
|
||||
? this.$store.state.instance.postContentType
|
||||
|
@ -82,7 +82,7 @@ const PostStatusForm = {
|
|||
contentType
|
||||
},
|
||||
caret: 0,
|
||||
pollFormVisible: false
|
||||
pollFormVisible: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -138,8 +138,8 @@ const PostStatusForm = {
|
|||
},
|
||||
showAllScopes () {
|
||||
const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined'
|
||||
? this.$store.state.instance.minimalScopesMode
|
||||
: this.$store.state.config.minimalScopesMode
|
||||
? this.$store.state.instance.minimalScopesMode
|
||||
: this.$store.state.config.minimalScopesMode
|
||||
return !minimalScopesMode
|
||||
},
|
||||
emoji () {
|
||||
|
@ -259,6 +259,8 @@ const PostStatusForm = {
|
|||
}
|
||||
}
|
||||
|
||||
const poll = this.pollFormVisible ? this.poll : {}
|
||||
|
||||
this.posting = true
|
||||
statusPoster.postStatus({
|
||||
status: newStatus.status,
|
||||
|
@ -266,10 +268,10 @@ const PostStatusForm = {
|
|||
visibility: newStatus.visibility,
|
||||
sensitive: newStatus.nsfw,
|
||||
media: newStatus.files,
|
||||
poll: newStatus.poll,
|
||||
store: this.$store,
|
||||
inReplyToStatusId: this.replyTo,
|
||||
contentType: newStatus.contentType
|
||||
contentType: newStatus.contentType,
|
||||
poll
|
||||
}).then((data) => {
|
||||
if (!data.error) {
|
||||
this.newStatus = {
|
||||
|
@ -282,6 +284,7 @@ const PostStatusForm = {
|
|||
}
|
||||
this.pollFormVisible = false
|
||||
this.$refs.mediaUpload.clearFile()
|
||||
this.$refs.pollForm.clear()
|
||||
this.$emit('posted')
|
||||
let el = this.$el.querySelector('textarea')
|
||||
el.style.height = 'auto'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="post-status-form">
|
||||
<form @submit.prevent="postStatus(newStatus)">
|
||||
<form @submit.prevent="postStatus(newStatus)" autocomplete="off">
|
||||
<div class="form-group" >
|
||||
<i18n
|
||||
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private'"
|
||||
|
@ -92,7 +92,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<poll-form v-if="pollsAvailable" :visible="pollFormVisible" @update-poll="setPoll" />
|
||||
<poll-form
|
||||
ref="pollForm"
|
||||
v-if="pollsAvailable"
|
||||
:visible="pollFormVisible"
|
||||
@update-poll="setPoll"
|
||||
/>
|
||||
<div class='form-bottom'>
|
||||
<media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
|
||||
<div v-if="pollsAvailable" class="poll-icon">
|
||||
|
@ -100,7 +105,7 @@
|
|||
class="btn btn-default"
|
||||
:title="$t('tool_tip.poll')"
|
||||
@click="togglePollForm">
|
||||
<i class="icon-chart-bar"></i>
|
||||
<i class="icon-chart-bar" :class="pollFormVisible && 'selected'" />
|
||||
</label>
|
||||
</div>
|
||||
<p v-if="isOverLengthLimit" class="error">{{ charactersLeft }}</p>
|
||||
|
@ -300,6 +305,11 @@
|
|||
.poll-icon {
|
||||
font-size: 26px;
|
||||
flex: 1;
|
||||
|
||||
.selected {
|
||||
color: $fallback--lightText;
|
||||
color: var(--lightText, $fallback--lightText);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-chart-bar {
|
||||
|
|
|
@ -84,17 +84,8 @@
|
|||
"option": "Option",
|
||||
"votes": "votes",
|
||||
"vote": "Vote",
|
||||
"single_choice": "Allow one choice",
|
||||
"multiple_choices": "Allow multiple choices",
|
||||
"expiry_options": {
|
||||
"300": "5 minutes",
|
||||
"1800": "30 minutes",
|
||||
"3600": "1 hour",
|
||||
"21600": "6 hours",
|
||||
"86400": "1 day",
|
||||
"259200": "3 days",
|
||||
"604800": "7 days"
|
||||
}
|
||||
"single_choice": "Single choice",
|
||||
"multiple_choices": "Multiple choices"
|
||||
},
|
||||
"interactions": {
|
||||
"favs_repeats": "Repeats and Favorites",
|
||||
|
|
|
@ -12,7 +12,6 @@ import chatModule from './modules/chat.js'
|
|||
import oauthModule from './modules/oauth.js'
|
||||
import mediaViewerModule from './modules/media_viewer.js'
|
||||
import oauthTokensModule from './modules/oauth_tokens.js'
|
||||
import pollModule from './modules/poll.js'
|
||||
import reportsModule from './modules/reports.js'
|
||||
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
@ -69,7 +68,6 @@ const persistedStateOptions = {
|
|||
oauth: oauthModule,
|
||||
mediaViewer: mediaViewerModule,
|
||||
oauthTokens: oauthTokensModule,
|
||||
poll: pollModule,
|
||||
reports: reportsModule
|
||||
},
|
||||
plugins: [persistedState, pushNotifications],
|
||||
|
|
|
@ -60,7 +60,7 @@ const defaultState = {
|
|||
max_options: 4,
|
||||
max_option_chars: 255,
|
||||
min_expiration: 60,
|
||||
max_expiration: 300
|
||||
max_expiration: 600
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
const poll = {
|
||||
state: {
|
||||
options: ['', ''],
|
||||
multiple: false,
|
||||
expiresIn: '86400'
|
||||
},
|
||||
mutations: {
|
||||
ADD_OPTION (state, { option }) {
|
||||
state.options.push(option)
|
||||
},
|
||||
UPDATE_OPTION (state, { index, option }) {
|
||||
state.options[index] = option
|
||||
},
|
||||
DELETE_OPTION (state, { index }) {
|
||||
state.options.splice(index, 1)
|
||||
},
|
||||
SWAP_OPTIONS (state, { options }) {
|
||||
state.options = options
|
||||
},
|
||||
SET_MULTIPLE (state, { multiple }) {
|
||||
state.multiple = multiple
|
||||
},
|
||||
SET_EXPIRES_IN (state, { expiresIn }) {
|
||||
state.expiresIn = expiresIn
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addPollOption (store, { option }) {
|
||||
store.commit('ADD_OPTION', { option })
|
||||
},
|
||||
updatePollOption (store, { index, option }) {
|
||||
store.commit('UPDATE_OPTION', { index, option })
|
||||
},
|
||||
deletePollOption (store, { index }) {
|
||||
store.commit('DELETE_OPTION', { index })
|
||||
},
|
||||
swapPollOptions (store, { options }) {
|
||||
store.commit('SWAP_OPTIONS', { options })
|
||||
},
|
||||
setMultiple (store, { multiple }) {
|
||||
store.commit('SET_MULTIPLE', { multiple })
|
||||
},
|
||||
setExpiresIn (store, { expiresIn }) {
|
||||
store.commit('SET_EXPIRES_IN', { expiresIn })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default poll
|
Loading…
Reference in New Issue