Merge branch 'from/develop/tusooa/autocomplete-accessibility' into 'develop'
Autocomplete accessibility Closes #1219 See merge request pleroma/pleroma-fe!1771
This commit is contained in:
commit
f229c4a106
|
@ -1,6 +1,7 @@
|
||||||
import Completion from '../../services/completion/completion.js'
|
import Completion from '../../services/completion/completion.js'
|
||||||
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
||||||
import Popover from 'src/components/popover/popover.vue'
|
import Popover from 'src/components/popover/popover.vue'
|
||||||
|
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
||||||
import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue'
|
import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||||
import { take } from 'lodash'
|
import { take } from 'lodash'
|
||||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||||
|
@ -109,9 +110,10 @@ const EmojiInput = {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
randomSeed: `${Math.random()}`.replace('.', '-'),
|
||||||
input: undefined,
|
input: undefined,
|
||||||
caretEl: undefined,
|
caretEl: undefined,
|
||||||
highlighted: 0,
|
highlighted: -1,
|
||||||
caret: 0,
|
caret: 0,
|
||||||
focused: false,
|
focused: false,
|
||||||
blurTimeout: null,
|
blurTimeout: null,
|
||||||
|
@ -125,7 +127,8 @@ const EmojiInput = {
|
||||||
components: {
|
components: {
|
||||||
Popover,
|
Popover,
|
||||||
EmojiPicker,
|
EmojiPicker,
|
||||||
UnicodeDomainIndicator
|
UnicodeDomainIndicator,
|
||||||
|
ScreenReaderNotice
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
padEmoji () {
|
padEmoji () {
|
||||||
|
@ -203,6 +206,12 @@ const EmojiInput = {
|
||||||
top: this.input.scrollTop,
|
top: this.input.scrollTop,
|
||||||
left: this.input.scrollLeft
|
left: this.input.scrollLeft
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
suggestionListId () {
|
||||||
|
return `suggestions-${this.randomSeed}`
|
||||||
|
},
|
||||||
|
suggestionItemId () {
|
||||||
|
return (index) => `suggestion-item-${index}-${this.randomSeed}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
@ -278,6 +287,11 @@ const EmojiInput = {
|
||||||
...rest,
|
...rest,
|
||||||
img: imageUrl || ''
|
img: imageUrl || ''
|
||||||
}))
|
}))
|
||||||
|
this.highlighted = -1
|
||||||
|
this.$refs.screenReaderNotice.announce(
|
||||||
|
this.$tc('tool_tip.autocomplete_available',
|
||||||
|
this.suggestions.length,
|
||||||
|
{ number: this.suggestions.length }))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -374,26 +388,27 @@ const EmojiInput = {
|
||||||
},
|
},
|
||||||
cycleBackward (e) {
|
cycleBackward (e) {
|
||||||
const len = this.suggestions.length || 0
|
const len = this.suggestions.length || 0
|
||||||
if (len > 1) {
|
|
||||||
this.highlighted -= 1
|
this.highlighted -= 1
|
||||||
if (this.highlighted < 0) {
|
if (this.highlighted === -1) {
|
||||||
this.highlighted = this.suggestions.length - 1
|
this.input.focus()
|
||||||
}
|
} else if (this.highlighted < -1) {
|
||||||
|
this.highlighted = len - 1
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
} else {
|
|
||||||
this.highlighted = 0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cycleForward (e) {
|
cycleForward (e) {
|
||||||
const len = this.suggestions.length || 0
|
const len = this.suggestions.length || 0
|
||||||
if (len > 1) {
|
|
||||||
this.highlighted += 1
|
this.highlighted += 1
|
||||||
if (this.highlighted >= len) {
|
if (this.highlighted >= len) {
|
||||||
this.highlighted = 0
|
this.highlighted = -1
|
||||||
}
|
this.input.focus()
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
} else {
|
|
||||||
this.highlighted = 0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollIntoView () {
|
scrollIntoView () {
|
||||||
|
@ -540,6 +555,13 @@ const EmojiInput = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resize () {
|
resize () {
|
||||||
|
},
|
||||||
|
autoCompleteItemLabel (suggestion) {
|
||||||
|
if (suggestion.user) {
|
||||||
|
return suggestion.displayText + ' ' + suggestion.detailText
|
||||||
|
} else {
|
||||||
|
return this.maybeLocalizedEmojiName(suggestion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,19 @@
|
||||||
class="emoji-input"
|
class="emoji-input"
|
||||||
:class="{ 'with-picker': !hideEmojiButton }"
|
:class="{ 'with-picker': !hideEmojiButton }"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot
|
||||||
|
:id="'textbox-' + randomSeed"
|
||||||
|
:aria-owns="suggestionListId"
|
||||||
|
aria-autocomplete="both"
|
||||||
|
:aria-expanded="showSuggestions"
|
||||||
|
:aria-activedescendant="(!showSuggestions || highlighted === -1) ? '' : suggestionItemId(highlighted)"
|
||||||
|
/>
|
||||||
<!-- TODO: make the 'x' disappear if at the end maybe? -->
|
<!-- TODO: make the 'x' disappear if at the end maybe? -->
|
||||||
<div
|
<div
|
||||||
ref="hiddenOverlay"
|
ref="hiddenOverlay"
|
||||||
class="hidden-overlay"
|
class="hidden-overlay"
|
||||||
:style="overlayStyle"
|
:style="overlayStyle"
|
||||||
|
:aria-hidden="true"
|
||||||
>
|
>
|
||||||
<span>{{ preText }}</span>
|
<span>{{ preText }}</span>
|
||||||
<span
|
<span
|
||||||
|
@ -18,11 +25,16 @@
|
||||||
>x</span>
|
>x</span>
|
||||||
<span>{{ postText }}</span>
|
<span>{{ postText }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<screen-reader-notice
|
||||||
|
ref="screenReaderNotice"
|
||||||
|
aria-live="assertive"
|
||||||
|
/>
|
||||||
<template v-if="enableEmojiPicker">
|
<template v-if="enableEmojiPicker">
|
||||||
<button
|
<button
|
||||||
v-if="!hideEmojiButton"
|
v-if="!hideEmojiButton"
|
||||||
class="button-unstyled emoji-picker-icon"
|
class="button-unstyled emoji-picker-icon"
|
||||||
type="button"
|
type="button"
|
||||||
|
:title="$t('emoji.add_emoji')"
|
||||||
@click.prevent="togglePicker"
|
@click.prevent="togglePicker"
|
||||||
>
|
>
|
||||||
<FAIcon :icon="['far', 'smile-beam']" />
|
<FAIcon :icon="['far', 'smile-beam']" />
|
||||||
|
@ -43,17 +55,24 @@
|
||||||
ref="suggestorPopover"
|
ref="suggestorPopover"
|
||||||
class="autocomplete-panel"
|
class="autocomplete-panel"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
|
:trigger-attrs="{ 'aria-hidden': true }"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div
|
<div
|
||||||
|
:id="suggestionListId"
|
||||||
ref="panel-body"
|
ref="panel-body"
|
||||||
class="autocomplete-panel-body"
|
class="autocomplete-panel-body"
|
||||||
|
role="listbox"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(suggestion, index) in suggestions"
|
v-for="(suggestion, index) in suggestions"
|
||||||
|
:id="suggestionItemId(index)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="autocomplete-item"
|
class="autocomplete-item"
|
||||||
|
role="option"
|
||||||
:class="{ highlighted: index === highlighted }"
|
:class="{ highlighted: index === highlighted }"
|
||||||
|
:aria-label="autoCompleteItemLabel(suggestion)"
|
||||||
|
:aria-selected="index === highlighted"
|
||||||
@click.stop.prevent="onClick($event, suggestion)"
|
@click.stop.prevent="onClick($event, suggestion)"
|
||||||
>
|
>
|
||||||
<span class="image">
|
<span class="image">
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
ref="popover"
|
ref="popover"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
popover-class="emoji-picker popover-default"
|
popover-class="emoji-picker popover-default"
|
||||||
|
:trigger-attrs="{ 'aria-hidden': true }"
|
||||||
@show="onPopoverShown"
|
@show="onPopoverShown"
|
||||||
@close="onPopoverClosed"
|
@close="onPopoverClosed"
|
||||||
>
|
>
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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 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 { reject, map, uniqBy, debounce } from 'lodash'
|
import { reject, map, uniqBy, debounce } from 'lodash'
|
||||||
import suggestor from '../emoji_input/suggestor.js'
|
import suggestor from '../emoji_input/suggestor.js'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
|
@ -629,6 +630,9 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
openProfileTab () {
|
openProfileTab () {
|
||||||
this.$store.dispatch('openSettingsModalTab', 'profile')
|
this.$store.dispatch('openSettingsModalTab', 'profile')
|
||||||
|
},
|
||||||
|
propsToNative (props) {
|
||||||
|
return propsToNative(props)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
<span>{{ $t('post_status.scope_notice.public') }}</span>
|
<span>{{ $t('post_status.scope_notice.public') }}</span>
|
||||||
<a
|
<a
|
||||||
class="fa-scale-110 fa-old-padding dismiss"
|
class="fa-scale-110 fa-old-padding dismiss"
|
||||||
|
:title="$t('post_status.scope_notice_dismiss')"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
@click.prevent="dismissScopeNotice()"
|
@click.prevent="dismissScopeNotice()"
|
||||||
>
|
>
|
||||||
<FAIcon icon="times" />
|
<FAIcon icon="times" />
|
||||||
|
@ -42,6 +45,9 @@
|
||||||
<span>{{ $t('post_status.scope_notice.unlisted') }}</span>
|
<span>{{ $t('post_status.scope_notice.unlisted') }}</span>
|
||||||
<a
|
<a
|
||||||
class="fa-scale-110 fa-old-padding dismiss"
|
class="fa-scale-110 fa-old-padding dismiss"
|
||||||
|
:title="$t('post_status.scope_notice_dismiss')"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
@click.prevent="dismissScopeNotice()"
|
@click.prevent="dismissScopeNotice()"
|
||||||
>
|
>
|
||||||
<FAIcon icon="times" />
|
<FAIcon icon="times" />
|
||||||
|
@ -54,6 +60,9 @@
|
||||||
<span>{{ $t('post_status.scope_notice.private') }}</span>
|
<span>{{ $t('post_status.scope_notice.private') }}</span>
|
||||||
<a
|
<a
|
||||||
class="fa-scale-110 fa-old-padding dismiss"
|
class="fa-scale-110 fa-old-padding dismiss"
|
||||||
|
:title="$t('post_status.scope_notice_dismiss')"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
@click.prevent="dismissScopeNotice()"
|
@click.prevent="dismissScopeNotice()"
|
||||||
>
|
>
|
||||||
<FAIcon icon="times" />
|
<FAIcon icon="times" />
|
||||||
|
@ -124,14 +133,17 @@
|
||||||
:suggest="emojiSuggestor"
|
:suggest="emojiSuggestor"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
>
|
>
|
||||||
<input
|
<template #default="inputProps">
|
||||||
v-model="newStatus.spoilerText"
|
<input
|
||||||
type="text"
|
v-model="newStatus.spoilerText"
|
||||||
:placeholder="$t('post_status.content_warning')"
|
type="text"
|
||||||
:disabled="posting && !optimisticPosting"
|
:placeholder="$t('post_status.content_warning')"
|
||||||
size="1"
|
:disabled="posting && !optimisticPosting"
|
||||||
class="form-post-subject"
|
v-bind="propsToNative(inputProps)"
|
||||||
>
|
size="1"
|
||||||
|
class="form-post-subject"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
ref="emoji-input"
|
ref="emoji-input"
|
||||||
|
@ -148,29 +160,32 @@
|
||||||
@sticker-upload-failed="uploadFailed"
|
@sticker-upload-failed="uploadFailed"
|
||||||
@shown="handleEmojiInputShow"
|
@shown="handleEmojiInputShow"
|
||||||
>
|
>
|
||||||
<textarea
|
<template #default="inputProps">
|
||||||
ref="textarea"
|
<textarea
|
||||||
v-model="newStatus.status"
|
ref="textarea"
|
||||||
:placeholder="placeholder || $t('post_status.default')"
|
v-model="newStatus.status"
|
||||||
rows="1"
|
:placeholder="placeholder || $t('post_status.default')"
|
||||||
cols="1"
|
rows="1"
|
||||||
:disabled="posting && !optimisticPosting"
|
cols="1"
|
||||||
class="form-post-body"
|
:disabled="posting && !optimisticPosting"
|
||||||
:class="{ 'scrollable-form': !!maxHeight }"
|
class="form-post-body"
|
||||||
@keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)"
|
:class="{ 'scrollable-form': !!maxHeight }"
|
||||||
@keydown.meta.enter="postStatus($event, newStatus)"
|
v-bind="propsToNative(inputProps)"
|
||||||
@keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)"
|
@keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)"
|
||||||
@input="resize"
|
@keydown.meta.enter="postStatus($event, newStatus)"
|
||||||
@compositionupdate="resize"
|
@keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)"
|
||||||
@paste="paste"
|
@input="resize"
|
||||||
/>
|
@compositionupdate="resize"
|
||||||
<p
|
@paste="paste"
|
||||||
v-if="hasStatusLengthLimit"
|
/>
|
||||||
class="character-counter faint"
|
<p
|
||||||
:class="{ error: isOverLengthLimit }"
|
v-if="hasStatusLengthLimit"
|
||||||
>
|
class="character-counter faint"
|
||||||
{{ charactersLeft }}
|
:class="{ error: isOverLengthLimit }"
|
||||||
</p>
|
>
|
||||||
|
{{ charactersLeft }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<div
|
<div
|
||||||
v-if="!disableScopeSelector"
|
v-if="!disableScopeSelector"
|
||||||
|
@ -193,6 +208,7 @@
|
||||||
id="post-content-type"
|
id="post-content-type"
|
||||||
v-model="newStatus.contentType"
|
v-model="newStatus.contentType"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:attrs="{ 'aria-label': $t('post_status.content_type_selection') }"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="postFormat in postFormats"
|
v-for="postFormat in postFormats"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
const ScreenReaderNotice = {
|
||||||
|
props: {
|
||||||
|
ariaLive: {
|
||||||
|
type: String,
|
||||||
|
defualt: 'assertive'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
currentText: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
announce (text) {
|
||||||
|
this.currentText = text
|
||||||
|
setTimeout(() => { this.currentText = '' }, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScreenReaderNotice
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="screen-reader-text"
|
||||||
|
:aria-live="ariaLive"
|
||||||
|
>
|
||||||
|
{{ currentText }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./screen_reader_notice.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.screen-reader-text {
|
||||||
|
display: block;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,6 +13,7 @@ export default {
|
||||||
'modelValue',
|
'modelValue',
|
||||||
'disabled',
|
'disabled',
|
||||||
'unstyled',
|
'unstyled',
|
||||||
'kind'
|
'kind',
|
||||||
|
'attrs'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<select
|
<select
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
|
v-bind="attrs"
|
||||||
@change="$emit('update:modelValue', $event.target.value)"
|
@change="$emit('update:modelValue', $event.target.value)"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -12,6 +12,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche
|
||||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
@ -261,6 +262,9 @@ const ProfileTab = {
|
||||||
messageArgs: [error.message],
|
messageArgs: [error.message],
|
||||||
level: 'error'
|
level: 'error'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
propsToNative (props) {
|
||||||
|
return propsToNative(props)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,14 @@
|
||||||
enable-emoji-picker
|
enable-emoji-picker
|
||||||
:suggest="emojiSuggestor"
|
:suggest="emojiSuggestor"
|
||||||
>
|
>
|
||||||
<input
|
<template #default="inputProps">
|
||||||
id="username"
|
<input
|
||||||
v-model="newName"
|
id="username"
|
||||||
class="name-changer"
|
v-model="newName"
|
||||||
>
|
class="name-changer"
|
||||||
|
v-bind="propsToNative(inputProps)"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<p>{{ $t('settings.bio') }}</p>
|
<p>{{ $t('settings.bio') }}</p>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
|
@ -20,10 +23,13 @@
|
||||||
enable-emoji-picker
|
enable-emoji-picker
|
||||||
:suggest="emojiUserSuggestor"
|
:suggest="emojiUserSuggestor"
|
||||||
>
|
>
|
||||||
<textarea
|
<template #default="inputProps">
|
||||||
v-model="newBio"
|
<textarea
|
||||||
class="bio resize-height"
|
v-model="newBio"
|
||||||
/>
|
class="bio resize-height"
|
||||||
|
v-bind="propsToNative(inputProps)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<p v-if="role === 'admin' || role === 'moderator'">
|
<p v-if="role === 'admin' || role === 'moderator'">
|
||||||
<Checkbox v-model="showRole">
|
<Checkbox v-model="showRole">
|
||||||
|
@ -60,10 +66,13 @@
|
||||||
hide-emoji-button
|
hide-emoji-button
|
||||||
:suggest="userSuggestor"
|
:suggest="userSuggestor"
|
||||||
>
|
>
|
||||||
<input
|
<template #default="inputProps">
|
||||||
v-model="newFields[i].name"
|
<input
|
||||||
:placeholder="$t('settings.profile_fields.name')"
|
v-model="newFields[i].name"
|
||||||
>
|
:placeholder="$t('settings.profile_fields.name')"
|
||||||
|
v-bind="propsToNative(inputProps)"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
v-model="newFields[i].value"
|
v-model="newFields[i].value"
|
||||||
|
@ -71,10 +80,13 @@
|
||||||
hide-emoji-button
|
hide-emoji-button
|
||||||
:suggest="userSuggestor"
|
:suggest="userSuggestor"
|
||||||
>
|
>
|
||||||
<input
|
<template #default="inputProps">
|
||||||
v-model="newFields[i].value"
|
<input
|
||||||
:placeholder="$t('settings.profile_fields.value')"
|
v-model="newFields[i].value"
|
||||||
>
|
:placeholder="$t('settings.profile_fields.value')"
|
||||||
|
v-bind="propsToNative(inputProps)"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<button
|
<button
|
||||||
class="delete-field button-unstyled -hover-highlight"
|
class="delete-field button-unstyled -hover-highlight"
|
||||||
|
|
|
@ -271,6 +271,7 @@
|
||||||
"text/markdown": "Markdown",
|
"text/markdown": "Markdown",
|
||||||
"text/bbcode": "BBCode"
|
"text/bbcode": "BBCode"
|
||||||
},
|
},
|
||||||
|
"content_type_selection": "Post format",
|
||||||
"content_warning": "Subject (optional)",
|
"content_warning": "Subject (optional)",
|
||||||
"default": "Just landed in L.A.",
|
"default": "Just landed in L.A.",
|
||||||
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
||||||
|
@ -288,6 +289,7 @@
|
||||||
"private": "This post will be visible to your followers only",
|
"private": "This post will be visible to your followers only",
|
||||||
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
|
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
|
||||||
},
|
},
|
||||||
|
"scope_notice_dismiss": "Close this notice",
|
||||||
"scope": {
|
"scope": {
|
||||||
"direct": "Direct - post to mentioned users only",
|
"direct": "Direct - post to mentioned users only",
|
||||||
"private": "Followers-only - post to followers only",
|
"private": "Followers-only - post to followers only",
|
||||||
|
@ -1056,7 +1058,8 @@
|
||||||
"reject_follow_request": "Reject follow request",
|
"reject_follow_request": "Reject follow request",
|
||||||
"bookmark": "Bookmark",
|
"bookmark": "Bookmark",
|
||||||
"toggle_expand": "Expand or collapse notification to show post in full",
|
"toggle_expand": "Expand or collapse notification to show post in full",
|
||||||
"toggle_mute": "Expand or collapse notification to reveal muted content"
|
"toggle_mute": "Expand or collapse notification to reveal muted content",
|
||||||
|
"autocomplete_available": "{number} result is available. Use up and down keys to navigate through them. | {number} results are available. Use up and down keys to navigate through them."
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"error": {
|
"error": {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { kebabCase } from 'lodash'
|
||||||
|
|
||||||
|
const propsToNative = props => Object.keys(props).reduce((acc, cur) => {
|
||||||
|
acc[kebabCase(cur)] = props[cur]
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
export { propsToNative }
|
|
@ -14,7 +14,8 @@ const generateInput = (value, padEmoji = true) => {
|
||||||
padEmoji
|
padEmoji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
$t: (msg) => msg
|
||||||
},
|
},
|
||||||
stubs: {
|
stubs: {
|
||||||
FAIcon: true
|
FAIcon: true
|
||||||
|
|
Loading…
Reference in New Issue