Merge branch 'drafts-improvements' into 'develop'
Drafts improvements See merge request pleroma/pleroma-fe!1964
This commit is contained in:
commit
5c6d29193e
13 changed files with 547 additions and 348 deletions
0
changelog.d/drafts-imp.skip
Normal file
0
changelog.d/drafts-imp.skip
Normal file
25
src/App.scss
25
src/App.scss
|
@ -390,6 +390,24 @@ nav {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
line-height: var(--__line-height);
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
a,
|
||||||
|
button:not(.button-default) {
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.menu-item,
|
.menu-item,
|
||||||
.list-item {
|
.list-item {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -397,10 +415,6 @@ nav {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
font-size: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: 400;
|
|
||||||
cursor: pointer;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
clear: both;
|
clear: both;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -410,7 +424,6 @@ nav {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-top-width: 1px;
|
border-top-width: 1px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
line-height: var(--__line-height);
|
|
||||||
padding: var(--__vertical-gap) var(--__horizontal-gap);
|
padding: var(--__vertical-gap) var(--__horizontal-gap);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
|
@ -450,10 +463,8 @@ nav {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
display: inline;
|
display: inline;
|
||||||
font-size: 100%;
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
line-height: unset;
|
line-height: unset;
|
||||||
color: var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
|
|
|
@ -2,13 +2,25 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
|
||||||
import EditStatusForm from 'src/components/edit_status_form/edit_status_form.vue'
|
import EditStatusForm from 'src/components/edit_status_form/edit_status_form.vue'
|
||||||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||||
|
import Gallery from 'src/components/gallery/gallery.vue'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {
|
||||||
|
faPollH
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faPollH
|
||||||
|
)
|
||||||
|
|
||||||
const Draft = {
|
const Draft = {
|
||||||
components: {
|
components: {
|
||||||
PostStatusForm,
|
PostStatusForm,
|
||||||
EditStatusForm,
|
EditStatusForm,
|
||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
StatusContent
|
StatusContent,
|
||||||
|
Gallery
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
draft: {
|
draft: {
|
||||||
|
@ -18,6 +30,7 @@ const Draft = {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
referenceDraft: cloneDeep(this.draft),
|
||||||
editing: false,
|
editing: false,
|
||||||
showingConfirmDialog: false
|
showingConfirmDialog: false
|
||||||
}
|
}
|
||||||
|
@ -32,6 +45,11 @@ const Draft = {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
safeToSave () {
|
||||||
|
return this.draft.status ||
|
||||||
|
this.draft.files?.length ||
|
||||||
|
this.draft.hasPoll
|
||||||
|
},
|
||||||
postStatusFormProps () {
|
postStatusFormProps () {
|
||||||
return {
|
return {
|
||||||
draftId: this.draft.id,
|
draftId: this.draft.id,
|
||||||
|
@ -40,6 +58,28 @@ const Draft = {
|
||||||
},
|
},
|
||||||
refStatus () {
|
refStatus () {
|
||||||
return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined
|
return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined
|
||||||
|
},
|
||||||
|
localCollapseSubjectDefault () {
|
||||||
|
return this.$store.getters.mergedConfig.collapseMessageWithSubject
|
||||||
|
},
|
||||||
|
nsfwClickthrough () {
|
||||||
|
if (!this.draft.nsfw) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this.draft.summary && this.localCollapseSubjectDefault) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
editing (newVal) {
|
||||||
|
if (newVal) return
|
||||||
|
if (this.safeToSave) {
|
||||||
|
this.$store.dispatch('addOrSaveDraft', { draft: this.draft })
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch('addOrSaveDraft', { draft: this.referenceDraft })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,21 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<article class="Draft">
|
<article class="Draft">
|
||||||
<div class="actions">
|
|
||||||
<button
|
|
||||||
class="btn button-default"
|
|
||||||
:class="{ toggled: editing }"
|
|
||||||
:aria-expanded="editing"
|
|
||||||
@click.prevent.stop="toggleEditing"
|
|
||||||
>
|
|
||||||
{{ $t('drafts.continue') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn button-default"
|
|
||||||
@click.prevent.stop="abandon"
|
|
||||||
>
|
|
||||||
{{ $t('drafts.abandon') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="!editing"
|
v-if="!editing"
|
||||||
class="status-content"
|
class="status-content"
|
||||||
|
@ -42,15 +26,50 @@
|
||||||
:compact="true"
|
:compact="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p>{{ draft.status }}</p>
|
<div class="status-preview">
|
||||||
|
<span class="status_content">
|
||||||
|
<p v-if="draft.spoilerText">
|
||||||
|
<i>
|
||||||
|
{{ draft.spoilerText }}:
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
<p v-if="draft.status">{{ draft.status }}</p>
|
||||||
|
<p v-else class="faint">{{ $t('drafts.empty') }}</p>
|
||||||
|
</span>
|
||||||
|
<gallery
|
||||||
|
v-if="draft.files?.length !== 0"
|
||||||
|
class="attachments media-body"
|
||||||
|
:compact="true"
|
||||||
|
:nsfw="nsfwClickthrough"
|
||||||
|
:attachments="draft.files"
|
||||||
|
:limit="1"
|
||||||
|
size="small"
|
||||||
|
@play="$emit('mediaplay', attachment.id)"
|
||||||
|
@pause="$emit('mediapause', attachment.id)"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="draft.poll.options"
|
||||||
|
class="poll-indicator-container"
|
||||||
|
:title="$t('drafts.poll_tooltip')"
|
||||||
|
>
|
||||||
|
<div class="poll-indicator">
|
||||||
|
<FAIcon
|
||||||
|
icon="poll-h"
|
||||||
|
size="3x"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editing">
|
<div v-if="editing">
|
||||||
<PostStatusForm
|
<PostStatusForm
|
||||||
v-if="draft.type !== 'edit'"
|
v-if="draft.type !== 'edit'"
|
||||||
|
:disable-draft="true"
|
||||||
v-bind="postStatusFormProps"
|
v-bind="postStatusFormProps"
|
||||||
/>
|
/>
|
||||||
<EditStatusForm
|
<EditStatusForm
|
||||||
v-else
|
v-else
|
||||||
|
:disable-draft="true"
|
||||||
:params="postStatusFormProps"
|
:params="postStatusFormProps"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,6 +85,21 @@
|
||||||
{{ $t('drafts.abandon_confirm') }}
|
{{ $t('drafts.abandon_confirm') }}
|
||||||
</confirm-modal>
|
</confirm-modal>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
<div class="actions">
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
:aria-expanded="editing"
|
||||||
|
@click.prevent.stop="toggleEditing"
|
||||||
|
>
|
||||||
|
{{ editing ? $t('drafts.save') : $t('drafts.continue') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn button-default"
|
||||||
|
@click.prevent.stop="abandon"
|
||||||
|
>
|
||||||
|
{{ $t('drafts.abandon') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -73,17 +107,55 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.Draft {
|
.Draft {
|
||||||
margin: 1em;
|
position: relative;
|
||||||
|
|
||||||
.status-content {
|
.status-content {
|
||||||
border: 1px solid;
|
|
||||||
border-color: var(--faint);
|
|
||||||
border-radius: var(--inputRadius);
|
|
||||||
color: var(--text);
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-preview {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-auto-columns: 10em;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-gap: 0.5em;
|
||||||
|
align-items: start;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
p {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-indicator-container {
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
align-self: start;
|
||||||
|
height: 0;
|
||||||
|
padding-bottom: 62.5%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-indicator {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -93,7 +165,6 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
max-width: 10em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
<p v-if="drafts.length === 0">
|
||||||
|
{{ $t('drafts.no_drafts') }}
|
||||||
|
</p>
|
||||||
<List
|
<List
|
||||||
|
v-else
|
||||||
:items="drafts"
|
:items="drafts"
|
||||||
|
:non-interactive="true"
|
||||||
>
|
>
|
||||||
<template #item="{ item: draft }">
|
<template #item="{ item: draft }">
|
||||||
<Draft
|
<Draft
|
||||||
|
class="draft"
|
||||||
:draft="draft"
|
:draft="draft"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -22,3 +28,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./drafts.js"></script>
|
<script src="./drafts.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.draft {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -11,8 +11,9 @@ export default {
|
||||||
'Avatar'
|
'Avatar'
|
||||||
],
|
],
|
||||||
states: {
|
states: {
|
||||||
hover: ':hover',
|
hover: ':hover:not(.disabled)',
|
||||||
active: '.-active'
|
active: '.-active',
|
||||||
|
disabled: '.disabled'
|
||||||
},
|
},
|
||||||
defaultRules: [
|
defaultRules: [
|
||||||
{
|
{
|
||||||
|
@ -85,6 +86,28 @@ export default {
|
||||||
textColor: '--link',
|
textColor: '--link',
|
||||||
textAuto: 'no-preserve'
|
textAuto: 'no-preserve'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Text',
|
||||||
|
parent: {
|
||||||
|
component: 'MenuItem',
|
||||||
|
state: ['disabled']
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
textOpacity: 0.25,
|
||||||
|
textOpacityMode: 'blend'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Icon',
|
||||||
|
parent: {
|
||||||
|
component: 'MenuItem',
|
||||||
|
state: ['disabled']
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
textOpacity: 0.25,
|
||||||
|
textOpacityMode: 'blend'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,9 @@
|
||||||
.title {
|
.title {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
margin-left: 0.6em;
|
margin-left: 0.6em;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,11 +154,6 @@
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timelines-chevron {
|
|
||||||
margin-left: 0.8em;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timelines-background {
|
.timelines-background {
|
||||||
padding: 0 0 0 0.6em;
|
padding: 0 0 0 0.6em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,12 @@
|
||||||
--__horizontal-gap: 0.5em;
|
--__horizontal-gap: 0.5em;
|
||||||
--__vertical-gap: 0.4em;
|
--__vertical-gap: 0.4em;
|
||||||
|
|
||||||
padding: 0;
|
padding: var(--__vertical-gap) var(--__horizontal-gap);
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-auto-columns: var(--__line-height);
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-gap: var(--__horizontal-gap);
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
|
||||||
&[aria-expanded] {
|
&[aria-expanded] {
|
||||||
|
@ -93,8 +97,6 @@
|
||||||
.main-link {
|
.main-link {
|
||||||
line-height: var(--__line-height);
|
line-height: var(--__line-height);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex: 1;
|
|
||||||
padding: var(--__vertical-gap) var(--__horizontal-gap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
|
@ -104,26 +106,16 @@
|
||||||
margin-right: var(--__horizontal-gap);
|
margin-right: var(--__horizontal-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timelines-chevron {
|
.timelines-chevron,
|
||||||
line-height: var(--__line-height);
|
|
||||||
padding: 0;
|
|
||||||
width: var(--__line-height);
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra-button {
|
.extra-button {
|
||||||
line-height: var(--__line-height);
|
line-height: var(--__line-height);
|
||||||
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: var(--__line-height);
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: calc(-1 * var(--__horizontal-gap));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
margin: 0 var(--__horizontal-gap);
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconEmoji {
|
.iconEmoji {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import PollForm from '../poll/poll_form.vue'
|
||||||
import Attachment from '../attachment/attachment.vue'
|
import Attachment from '../attachment/attachment.vue'
|
||||||
import Gallery from 'src/components/gallery/gallery.vue'
|
import Gallery from 'src/components/gallery/gallery.vue'
|
||||||
import StatusContent from '../status_content/status_content.vue'
|
import StatusContent from '../status_content/status_content.vue'
|
||||||
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||||
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
|
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
|
||||||
|
@ -25,7 +26,10 @@ import {
|
||||||
faUpload,
|
faUpload,
|
||||||
faBan,
|
faBan,
|
||||||
faTimes,
|
faTimes,
|
||||||
faCircleNotch
|
faCircleNotch,
|
||||||
|
faChevronDown,
|
||||||
|
faChevronLeft,
|
||||||
|
faChevronRight
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
|
@ -34,7 +38,10 @@ library.add(
|
||||||
faUpload,
|
faUpload,
|
||||||
faBan,
|
faBan,
|
||||||
faTimes,
|
faTimes,
|
||||||
faCircleNotch
|
faCircleNotch,
|
||||||
|
faChevronDown,
|
||||||
|
faChevronLeft,
|
||||||
|
faChevronRight
|
||||||
)
|
)
|
||||||
|
|
||||||
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
|
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
|
||||||
|
@ -111,7 +118,8 @@ const PostStatusForm = {
|
||||||
'resize',
|
'resize',
|
||||||
'mediaplay',
|
'mediaplay',
|
||||||
'mediapause',
|
'mediapause',
|
||||||
'can-close'
|
'can-close',
|
||||||
|
'update'
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
MediaUpload,
|
MediaUpload,
|
||||||
|
@ -123,7 +131,8 @@ const PostStatusForm = {
|
||||||
Attachment,
|
Attachment,
|
||||||
StatusContent,
|
StatusContent,
|
||||||
Gallery,
|
Gallery,
|
||||||
DraftCloser
|
DraftCloser,
|
||||||
|
Popover
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.updateIdempotencyKey()
|
this.updateIdempotencyKey()
|
||||||
|
@ -210,7 +219,7 @@ const PostStatusForm = {
|
||||||
emojiInputShown: false,
|
emojiInputShown: false,
|
||||||
idempotencyKey: '',
|
idempotencyKey: '',
|
||||||
saveInhibited: true,
|
saveInhibited: true,
|
||||||
savable: false
|
saveable: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -335,7 +344,7 @@ const PostStatusForm = {
|
||||||
return this.$store.getters.mergedConfig.autoSaveDraft
|
return this.$store.getters.mergedConfig.autoSaveDraft
|
||||||
},
|
},
|
||||||
autoSaveState () {
|
autoSaveState () {
|
||||||
if (this.savable) {
|
if (this.saveable) {
|
||||||
return this.$t('post_status.auto_save_saving')
|
return this.$t('post_status.auto_save_saving')
|
||||||
} else if (this.newStatus.id) {
|
} else if (this.newStatus.id) {
|
||||||
return this.$t('post_status.auto_save_saved')
|
return this.$t('post_status.auto_save_saved')
|
||||||
|
@ -343,6 +352,12 @@ const PostStatusForm = {
|
||||||
return this.$t('post_status.auto_save_nothing_new')
|
return this.$t('post_status.auto_save_nothing_new')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
safeToSaveDraft () {
|
||||||
|
return this.newStatus.status ||
|
||||||
|
this.newStatus.spoilerText ||
|
||||||
|
this.newStatus.files?.length ||
|
||||||
|
this.newStatus.hasPoll
|
||||||
|
},
|
||||||
...mapGetters(['mergedConfig']),
|
...mapGetters(['mergedConfig']),
|
||||||
...mapState({
|
...mapState({
|
||||||
mobileLayout: state => state.interface.mobileLayout
|
mobileLayout: state => state.interface.mobileLayout
|
||||||
|
@ -355,7 +370,7 @@ const PostStatusForm = {
|
||||||
this.statusChanged()
|
this.statusChanged()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
savable (val) {
|
saveable (val) {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
|
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
|
||||||
// MDN says we'd better add the beforeunload event listener only when needed, and remove it when it's no longer needed
|
// MDN says we'd better add the beforeunload event listener only when needed, and remove it when it's no longer needed
|
||||||
if (val) {
|
if (val) {
|
||||||
|
@ -374,7 +389,7 @@ const PostStatusForm = {
|
||||||
this.autoPreview()
|
this.autoPreview()
|
||||||
this.updateIdempotencyKey()
|
this.updateIdempotencyKey()
|
||||||
this.debouncedMaybeAutoSaveDraft()
|
this.debouncedMaybeAutoSaveDraft()
|
||||||
this.savable = true
|
this.saveable = true
|
||||||
this.saveInhibited = false
|
this.saveInhibited = false
|
||||||
},
|
},
|
||||||
clearStatus () {
|
clearStatus () {
|
||||||
|
@ -403,7 +418,7 @@ const PostStatusForm = {
|
||||||
el.style.height = undefined
|
el.style.height = undefined
|
||||||
this.error = null
|
this.error = null
|
||||||
if (this.preview) this.previewStatus()
|
if (this.preview) this.previewStatus()
|
||||||
this.savable = false
|
this.saveable = false
|
||||||
},
|
},
|
||||||
async postStatus (event, newStatus, opts = {}) {
|
async postStatus (event, newStatus, opts = {}) {
|
||||||
if (this.posting && !this.optimisticPosting) { return }
|
if (this.posting && !this.optimisticPosting) { return }
|
||||||
|
@ -738,21 +753,19 @@ const PostStatusForm = {
|
||||||
saveDraft () {
|
saveDraft () {
|
||||||
if (!this.disableDraft &&
|
if (!this.disableDraft &&
|
||||||
!this.saveInhibited) {
|
!this.saveInhibited) {
|
||||||
if (this.newStatus.status ||
|
if (this.safeToSaveDraft) {
|
||||||
this.newStatus.files?.length ||
|
|
||||||
this.newStatus.hasPoll) {
|
|
||||||
return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
|
return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
|
||||||
.then(id => {
|
.then(id => {
|
||||||
if (this.newStatus.id !== id) {
|
if (this.newStatus.id !== id) {
|
||||||
this.newStatus.id = id
|
this.newStatus.id = id
|
||||||
}
|
}
|
||||||
this.savable = false
|
this.saveable = false
|
||||||
})
|
})
|
||||||
} else if (this.newStatus.id) {
|
} else if (this.newStatus.id) {
|
||||||
// There is a draft, but there is nothing in it, clear it
|
// There is a draft, but there is nothing in it, clear it
|
||||||
return this.abandonDraft()
|
return this.abandonDraft()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.savable = false
|
this.saveable = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -780,7 +793,7 @@ const PostStatusForm = {
|
||||||
// No draft available, fall back
|
// No draft available, fall back
|
||||||
},
|
},
|
||||||
requestClose () {
|
requestClose () {
|
||||||
if (!this.savable) {
|
if (!this.saveable) {
|
||||||
this.$emit('can-close')
|
this.$emit('can-close')
|
||||||
} else {
|
} else {
|
||||||
this.$refs.draftCloser.requestClose()
|
this.$refs.draftCloser.requestClose()
|
||||||
|
|
258
src/components/post_status_form/post_status_form.scss
Normal file
258
src/components/post_status_form/post_status_form.scss
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
.post-status-form {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.attachments {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-post-actions {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.5em;
|
||||||
|
height: 2.5em;
|
||||||
|
|
||||||
|
.post-button-group {
|
||||||
|
width: 10em;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.post-button {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-post-actions {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0.35em;
|
||||||
|
padding: 0.35em;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-bottom-left {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 7px;
|
||||||
|
margin-right: 7px;
|
||||||
|
max-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-heading {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-toggle {
|
||||||
|
flex: 10 0 auto;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg,
|
||||||
|
i {
|
||||||
|
margin-left: 0.2em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-error {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--textFaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-status {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-or-quote-selector {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-format {
|
||||||
|
.only-format {
|
||||||
|
color: var(--textFaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-tray {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-top: 5px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-notice.edit-warning {
|
||||||
|
> :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order is not necessary but a good indicator
|
||||||
|
.media-upload-icon {
|
||||||
|
order: 1;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
order: 2;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-icon {
|
||||||
|
order: 3;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-upload-icon,
|
||||||
|
.poll-icon,
|
||||||
|
.emoji-icon {
|
||||||
|
font-size: 1.85em;
|
||||||
|
line-height: 1.1;
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 0.1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-upload-wrapper {
|
||||||
|
margin-right: 0.2em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
width: 18em;
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
object-fit: contain;
|
||||||
|
max-height: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
max-height: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn[disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0.6em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.25em 0.5em 0.5em;
|
||||||
|
line-height: 1.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input.form-post-body {
|
||||||
|
// TODO: make a resizable textarea component?
|
||||||
|
box-sizing: content-box; // needed for easier computation of dynamic size
|
||||||
|
overflow: hidden;
|
||||||
|
transition: min-height 200ms 100ms;
|
||||||
|
// stock padding + 1 line of text (for counter)
|
||||||
|
padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
|
||||||
|
// two lines of text
|
||||||
|
height: calc(var(--post-line-height) * 1em);
|
||||||
|
min-height: calc(var(--post-line-height) * 1em);
|
||||||
|
resize: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&.scrollable-form {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-input {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-counter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
color: var(--cRed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 0.6; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
from { opacity: 0.6; }
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop-indicator {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 5em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.6;
|
||||||
|
color: var(--text);
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
border: 2px dashed var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-save-status {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -264,6 +264,12 @@
|
||||||
:visible="pollFormVisible"
|
:visible="pollFormVisible"
|
||||||
:params="newStatus.poll"
|
:params="newStatus.poll"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
v-if="!disableDraft && shouldAutoSaveDraft"
|
||||||
|
class="auto-save-status"
|
||||||
|
>
|
||||||
|
{{ autoSaveState }}
|
||||||
|
</span>
|
||||||
<div
|
<div
|
||||||
ref="bottom"
|
ref="bottom"
|
||||||
class="form-bottom"
|
class="form-bottom"
|
||||||
|
@ -296,41 +302,54 @@
|
||||||
<FAIcon icon="poll-h" />
|
<FAIcon icon="poll-h" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<div class="btn-group post-button-group">
|
||||||
v-if="!disableDraft && shouldAutoSaveDraft"
|
<button
|
||||||
class="auto-save-status"
|
class="btn button-default post-button"
|
||||||
>
|
:disabled="isOverLengthLimit || posting || uploadingFiles || disableSubmit"
|
||||||
{{ autoSaveState }}
|
@click.stop.prevent="postStatus($event, newStatus)"
|
||||||
</span>
|
>
|
||||||
<button
|
<template v-if="posting">
|
||||||
v-else-if="!disableDraft"
|
{{ $t('post_status.posting') }}
|
||||||
class="btn button-default"
|
</template>
|
||||||
@click="saveDraft"
|
<template v-else>
|
||||||
>
|
{{ $t('post_status.post') }}
|
||||||
{{ $t('post_status.save_to_drafts_button') }}
|
</template>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<Popover
|
||||||
v-if="posting"
|
v-if="!disableDraft"
|
||||||
disabled
|
class="more-post-actions"
|
||||||
class="btn button-default"
|
:normal-button="true"
|
||||||
>
|
trigger="click"
|
||||||
{{ $t('post_status.posting') }}
|
placement="bottom"
|
||||||
</button>
|
:offset="{ y: 5 }"
|
||||||
<button
|
:bound-to="{ x: 'container' }"
|
||||||
v-else-if="isOverLengthLimit"
|
>
|
||||||
disabled
|
<template #trigger>
|
||||||
class="btn button-default"
|
<FAIcon
|
||||||
>
|
class="fa-scale-110 fa-old-padding"
|
||||||
{{ $t('post_status.post') }}
|
icon="chevron-down"
|
||||||
</button>
|
/>
|
||||||
<button
|
</template>
|
||||||
v-else
|
<template #content="{close}">
|
||||||
:disabled="uploadingFiles || disableSubmit"
|
<div
|
||||||
class="btn button-default"
|
class="dropdown-menu"
|
||||||
@click.stop.prevent="postStatus($event, newStatus)"
|
role="menu"
|
||||||
>
|
>
|
||||||
{{ $t('post_status.post') }}
|
<button
|
||||||
</button>
|
v-if="!disableDraft"
|
||||||
|
class="menu-item dropdown-item dropdown-item-icon"
|
||||||
|
role="menu"
|
||||||
|
:disabled="!safeToSaveDraft"
|
||||||
|
:class="{ disabled: !safeToSaveDraft }"
|
||||||
|
@click.prevent="saveDraft"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
{{ $t('post_status.save_to_drafts_button') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-show="showDropIcon !== 'hide'"
|
v-show="showDropIcon !== 'hide'"
|
||||||
|
@ -391,246 +410,4 @@
|
||||||
|
|
||||||
<script src="./post_status_form.js"></script>
|
<script src="./post_status_form.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style src="./post_status_form.scss" lang="scss"></style>
|
||||||
.post-status-form {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.attachments {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-bottom {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0.5em;
|
|
||||||
height: 2.5em;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.35em;
|
|
||||||
padding: 0.35em;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-bottom-left {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
padding-right: 7px;
|
|
||||||
margin-right: 7px;
|
|
||||||
max-width: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-heading {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-toggle {
|
|
||||||
flex: 10 0 auto;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
padding-left: 0.5em;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg,
|
|
||||||
i {
|
|
||||||
margin-left: 0.2em;
|
|
||||||
font-size: 0.8em;
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-container {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-error {
|
|
||||||
font-style: italic;
|
|
||||||
color: var(--textFaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-status {
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reply-or-quote-selector {
|
|
||||||
flex: 1 0 auto;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-format {
|
|
||||||
.only-format {
|
|
||||||
color: var(--textFaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.visibility-tray {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-top: 5px;
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.visibility-notice.edit-warning {
|
|
||||||
> :first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order is not necessary but a good indicator
|
|
||||||
.media-upload-icon {
|
|
||||||
order: 1;
|
|
||||||
justify-content: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.emoji-icon {
|
|
||||||
order: 2;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-icon {
|
|
||||||
order: 3;
|
|
||||||
justify-content: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-upload-icon,
|
|
||||||
.poll-icon,
|
|
||||||
.emoji-icon {
|
|
||||||
font-size: 1.85em;
|
|
||||||
line-height: 1.1;
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 0.1em;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-upload-wrapper {
|
|
||||||
margin-right: 0.2em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
width: 18em;
|
|
||||||
|
|
||||||
img,
|
|
||||||
video {
|
|
||||||
object-fit: contain;
|
|
||||||
max-height: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video {
|
|
||||||
max-height: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-input-wrapper {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn[disabled] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0.6em;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0.25em 0.5em 0.5em;
|
|
||||||
line-height: 1.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input.form-post-body {
|
|
||||||
// TODO: make a resizable textarea component?
|
|
||||||
box-sizing: content-box; // needed for easier computation of dynamic size
|
|
||||||
overflow: hidden;
|
|
||||||
transition: min-height 200ms 100ms;
|
|
||||||
// stock padding + 1 line of text (for counter)
|
|
||||||
padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
|
|
||||||
// two lines of text
|
|
||||||
height: calc(var(--post-line-height) * 1em);
|
|
||||||
min-height: calc(var(--post-line-height) * 1em);
|
|
||||||
resize: none;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
&.scrollable-form {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-input {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.character-counter {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
|
|
||||||
&.error {
|
|
||||||
color: var(--cRed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade-in {
|
|
||||||
from { opacity: 0; }
|
|
||||||
to { opacity: 0.6; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade-out {
|
|
||||||
from { opacity: 0.6; }
|
|
||||||
to { opacity: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.drop-indicator {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 5em;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
opacity: 0.6;
|
|
||||||
color: var(--text);
|
|
||||||
background-color: var(--bg);
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
border: 2px dashed var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.auto-save-status {
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1513,7 +1513,11 @@
|
||||||
},
|
},
|
||||||
"drafts": {
|
"drafts": {
|
||||||
"drafts": "Drafts",
|
"drafts": "Drafts",
|
||||||
|
"no_drafts": "You have no drafts",
|
||||||
|
"empty": "(No content)",
|
||||||
|
"poll_tooltip": "Draft contains a poll",
|
||||||
"continue": "Continue composing",
|
"continue": "Continue composing",
|
||||||
|
"save": "Save without posting",
|
||||||
"abandon": "Abandon draft",
|
"abandon": "Abandon draft",
|
||||||
"abandon_confirm_title": "Abandon confirmation",
|
"abandon_confirm_title": "Abandon confirmation",
|
||||||
"abandon_confirm": "Do you really want to abandon this draft?",
|
"abandon_confirm": "Do you really want to abandon this draft?",
|
||||||
|
|
Loading…
Add table
Reference in a new issue