commit: 8625a612cdf99c9e1378073d6526d49cd5266767
parent: 98c5e4a9d050123e7837454992cfcc0aa11d7e3b
Author: HJ <spam@hjkos.com>
Date: Thu, 14 Feb 2019 19:19:58 +0000
Merge branch 'revert-2bc1cc9f' into 'develop'
Revert "Merge branch 'fix/no-autocomplete-in-non-post-forms' into 'develop'"
See merge request pleroma/pleroma-fe!582
Diffstat:
6 files changed, 199 insertions(+), 276 deletions(-)
diff --git a/src/components/autocomplete_input/autocomplete_input.js b/src/components/autocomplete_input/autocomplete_input.js
@@ -1,150 +0,0 @@
-import Completion from '../../services/completion/completion.js'
-import { take, filter, map } from 'lodash'
-
-const AutoCompleteInput = {
- props: [
- 'id',
- 'classObj',
- 'value',
- 'placeholder',
- 'autoResize',
- 'multiline',
- 'drop',
- 'dragoverPrevent',
- 'paste',
- 'keydownMetaEnter',
- 'keyupCtrlEnter'
- ],
- components: {},
- mounted () {
- this.autoResize && this.resize(this.$refs.textarea)
- const textLength = this.$refs.textarea.value.length
- this.$refs.textarea.setSelectionRange(textLength, textLength)
- },
- data () {
- return {
- caret: 0,
- highlighted: 0,
- text: this.value
- }
- },
- computed: {
- users () {
- return this.$store.state.users.users
- },
- emoji () {
- return this.$store.state.instance.emoji || []
- },
- customEmoji () {
- return this.$store.state.instance.customEmoji || []
- },
- textAtCaret () {
- return (this.wordAtCaret || {}).word || ''
- },
- wordAtCaret () {
- const word = Completion.wordAtPosition(this.text, this.caret - 1) || {}
- return word
- },
- candidates () {
- const firstchar = this.textAtCaret.charAt(0)
- if (firstchar === '@') {
- const query = this.textAtCaret.slice(1).toUpperCase()
- const matchedUsers = filter(this.users, (user) => {
- return user.screen_name.toUpperCase().startsWith(query) ||
- user.name && user.name.toUpperCase().startsWith(query)
- })
- if (matchedUsers.length <= 0) {
- return false
- }
- // eslint-disable-next-line camelcase
- return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
- // eslint-disable-next-line camelcase
- screen_name: `@${screen_name}`,
- name: name,
- img: profile_image_url_original,
- highlighted: index === this.highlighted
- }))
- } else if (firstchar === ':') {
- if (this.textAtCaret === ':') { return }
- const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1)))
- if (matchedEmoji.length <= 0) {
- return false
- }
- return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
- screen_name: `:${shortcode}:`,
- name: '',
- utf: utf || '',
- // eslint-disable-next-line camelcase
- img: utf ? '' : this.$store.state.instance.server + image_url,
- highlighted: index === this.highlighted
- }))
- } else {
- return false
- }
- }
- },
- methods: {
- setCaret ({target: {selectionStart}}) {
- this.caret = selectionStart
- },
- cycleBackward (e) {
- const len = this.candidates.length || 0
- if (len > 0) {
- e.preventDefault()
- this.highlighted -= 1
- if (this.highlighted < 0) {
- this.highlighted = this.candidates.length - 1
- }
- } else {
- this.highlighted = 0
- }
- },
- cycleForward (e) {
- const len = this.candidates.length || 0
- if (len > 0) {
- if (e.shiftKey) { return }
- e.preventDefault()
- this.highlighted += 1
- if (this.highlighted >= len) {
- this.highlighted = 0
- }
- } else {
- this.highlighted = 0
- }
- },
- replace (replacement) {
- this.text = Completion.replaceWord(this.text, this.wordAtCaret, replacement)
- const el = this.$el.querySelector('textarea')
- el.focus()
- this.caret = 0
- },
- replaceCandidate (e) {
- const len = this.candidates.length || 0
- if (this.textAtCaret === ':' || e.ctrlKey) { return }
- if (len > 0) {
- e.preventDefault()
- const candidate = this.candidates[this.highlighted]
- const replacement = candidate.utf || (candidate.screen_name + ' ')
- this.text = Completion.replaceWord(this.text, this.wordAtCaret, replacement)
- const el = this.$el.querySelector('textarea') || this.$el.querySelector('input')
- el.focus()
- this.caret = 0
- this.highlighted = 0
- }
- },
- resize (e) {
- const target = e.target || e
- if (!(target instanceof window.Element)) { return }
- const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) +
- Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1))
- // Auto is needed to make textbox shrink when removing lines
- target.style.height = 'auto'
- target.style.height = `${target.scrollHeight - vertPadding}px`
- if (target.value === '') {
- target.style.height = null
- }
- }
- }
-}
-
-export default AutoCompleteInput
diff --git a/src/components/autocomplete_input/autocomplete_input.vue b/src/components/autocomplete_input/autocomplete_input.vue
@@ -1,104 +0,0 @@
-<template>
- <div style="display: flex; flex-direction: column;">
- <textarea
- v-if="multiline"
- ref="textarea"
- rows="1"
- :value="text" :class="classObj" :id="id" :placeholder="placeholder"
- @input="text = $event.target.value, $emit('input', $event.target.value), autoResize && resize($event)"
- @click="setCaret"
- @keyup="setCaret"
- @keydown.down="cycleForward"
- @keydown.up="cycleBackward"
- @keydown.shift.tab="cycleBackward"
- @keydown.tab="cycleForward"
- @keydown.enter="replaceCandidate"
- @drop="drop && drop()"
- @dragover.prevent="dragoverPrevent && dragoverPrevent()"
- @paste="paste && paste()"
- @keydown.meta.enter="keydownMetaEnter && keydownMetaEnter()"
- @keyup.ctrl.enter="keyupCtrlEnter && keyupCtrlEnter()">
- </textarea>
- <input
- v-else
- ref="textarea"
- :value="text" :class="classObj" :id="id" :placeholder="placeholder"
- @input="text = $event.target.value, $emit('input', $event.target.value), autoResize && resize($event)"
- @click="setCaret"
- @keyup="setCaret"
- @keydown.down="cycleForward"
- @keydown.up="cycleBackward"
- @keydown.shift.tab="cycleBackward"
- @keydown.tab="cycleForward"
- @keydown.enter="replaceCandidate"
- @drop="drop && drop()"
- @dragover.prevent="dragoverPrevent && dragoverPrevent()"
- @paste="paste && paste()"
- @keydown.meta.enter="keydownMetaEnter && keydownMetaEnter()"
- @keyup.ctrl.enter="keyupCtrlEnter && keyupCtrlEnter()"/>
- <div style="position:relative;" v-if="candidates">
- <div class="autocomplete-panel">
- <div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
- <div class="autocomplete" :class="{ highlighted: candidate.highlighted }">
- <span v-if="candidate.img"><img :src="candidate.img"></img></span>
- <span v-else>{{candidate.utf}}</span>
- <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script src="./autocomplete_input.js"></script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.autocomplete-panel {
- margin: 0 0.5em 0 0.5em;
- border-radius: $fallback--tooltipRadius;
- border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
- position: absolute;
- z-index: 1;
- box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
- // this doesn't match original but i don't care, making it uniform.
- box-shadow: var(--popupShadow);
- min-width: 75%;
- background: $fallback--bg;
- background: var(--bg, $fallback--bg);
- color: $fallback--lightText;
- color: var(--lightText, $fallback--lightText);
-}
-
-.autocomplete {
- cursor: pointer;
- padding: 0.2em 0.4em 0.2em 0.4em;
- border-bottom: 1px solid rgba(0, 0, 0, 0.4);
- display: flex;
-
- img {
- width: 24px;
- height: 24px;
- border-radius: $fallback--avatarRadius;
- border-radius: var(--avatarRadius, $fallback--avatarRadius);
- object-fit: contain;
- }
-
- span {
- line-height: 24px;
- margin: 0 0.1em 0 0.2em;
- }
-
- small {
- margin-left: .5em;
- color: $fallback--faint;
- color: var(--faint, $fallback--faint);
- }
-
- &.highlighted {
- background-color: $fallback--fg;
- background-color: var(--lightBg, $fallback--fg);
- }
-}
-</style>
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
@@ -1,8 +1,8 @@
import statusPoster from '../../services/status_poster/status_poster.service.js'
import MediaUpload from '../media_upload/media_upload.vue'
-import AutoCompleteInput from '../autocomplete_input/autocomplete_input.vue'
import fileTypeService from '../../services/file_type/file_type.service.js'
-import { reject, map, uniqBy } from 'lodash'
+import Completion from '../../services/completion/completion.js'
+import { take, filter, reject, map, uniqBy } from 'lodash'
const buildMentionsString = ({user, attentions}, currentUser) => {
let allAttentions = [...attentions]
@@ -28,10 +28,13 @@ const PostStatusForm = {
'subject'
],
components: {
- MediaUpload,
- AutoCompleteInput
+ MediaUpload
},
mounted () {
+ this.resize(this.$refs.textarea)
+ const textLength = this.$refs.textarea.value.length
+ this.$refs.textarea.setSelectionRange(textLength, textLength)
+
if (this.replyTo) {
this.$refs.textarea.focus()
}
@@ -58,13 +61,15 @@ const PostStatusForm = {
submitDisabled: false,
error: null,
posting: false,
+ highlighted: 0,
newStatus: {
spoilerText: this.subject || '',
status: statusText,
nsfw: false,
files: [],
visibility: scope
- }
+ },
+ caret: 0
}
},
computed: {
@@ -76,6 +81,59 @@ const PostStatusForm = {
direct: { selected: this.newStatus.visibility === 'direct' }
}
},
+ candidates () {
+ const firstchar = this.textAtCaret.charAt(0)
+ if (firstchar === '@') {
+ const query = this.textAtCaret.slice(1).toUpperCase()
+ const matchedUsers = filter(this.users, (user) => {
+ return user.screen_name.toUpperCase().startsWith(query) ||
+ user.name && user.name.toUpperCase().startsWith(query)
+ })
+ if (matchedUsers.length <= 0) {
+ return false
+ }
+ // eslint-disable-next-line camelcase
+ return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
+ // eslint-disable-next-line camelcase
+ screen_name: `@${screen_name}`,
+ name: name,
+ img: profile_image_url_original,
+ highlighted: index === this.highlighted
+ }))
+ } else if (firstchar === ':') {
+ if (this.textAtCaret === ':') { return }
+ const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1)))
+ if (matchedEmoji.length <= 0) {
+ return false
+ }
+ return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
+ screen_name: `:${shortcode}:`,
+ name: '',
+ utf: utf || '',
+ // eslint-disable-next-line camelcase
+ img: utf ? '' : this.$store.state.instance.server + image_url,
+ highlighted: index === this.highlighted
+ }))
+ } else {
+ return false
+ }
+ },
+ textAtCaret () {
+ return (this.wordAtCaret || {}).word || ''
+ },
+ wordAtCaret () {
+ const word = Completion.wordAtPosition(this.newStatus.status, this.caret - 1) || {}
+ return word
+ },
+ users () {
+ return this.$store.state.users.users
+ },
+ emoji () {
+ return this.$store.state.instance.emoji || []
+ },
+ customEmoji () {
+ return this.$store.state.instance.customEmoji || []
+ },
statusLength () {
return this.newStatus.status.length
},
@@ -116,8 +174,53 @@ const PostStatusForm = {
}
},
methods: {
- postStatusCopy () {
- this.postStatus(this.newStatus)
+ replace (replacement) {
+ this.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
+ const el = this.$el.querySelector('textarea')
+ el.focus()
+ this.caret = 0
+ },
+ replaceCandidate (e) {
+ const len = this.candidates.length || 0
+ if (this.textAtCaret === ':' || e.ctrlKey) { return }
+ if (len > 0) {
+ e.preventDefault()
+ const candidate = this.candidates[this.highlighted]
+ const replacement = candidate.utf || (candidate.screen_name + ' ')
+ this.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
+ const el = this.$el.querySelector('textarea')
+ el.focus()
+ this.caret = 0
+ this.highlighted = 0
+ }
+ },
+ cycleBackward (e) {
+ const len = this.candidates.length || 0
+ if (len > 0) {
+ e.preventDefault()
+ this.highlighted -= 1
+ if (this.highlighted < 0) {
+ this.highlighted = this.candidates.length - 1
+ }
+ } else {
+ this.highlighted = 0
+ }
+ },
+ cycleForward (e) {
+ const len = this.candidates.length || 0
+ if (len > 0) {
+ if (e.shiftKey) { return }
+ e.preventDefault()
+ this.highlighted += 1
+ if (this.highlighted >= len) {
+ this.highlighted = 0
+ }
+ } else {
+ this.highlighted = 0
+ }
+ },
+ setCaret ({target: {selectionStart}}) {
+ this.caret = selectionStart
},
postStatus (newStatus) {
if (this.posting) { return }
@@ -202,6 +305,18 @@ const PostStatusForm = {
fileDrag (e) {
e.dataTransfer.dropEffect = 'copy'
},
+ resize (e) {
+ const target = e.target || e
+ if (!(target instanceof window.Element)) { return }
+ const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) +
+ Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1))
+ // Auto is needed to make textbox shrink when removing lines
+ target.style.height = 'auto'
+ target.style.height = `${target.scrollHeight - vertPadding}px`
+ if (target.value === '') {
+ target.style.height = null
+ }
+ },
clearError () {
this.error = null
},
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
@@ -16,16 +16,22 @@
:placeholder="$t('post_status.content_warning')"
v-model="newStatus.spoilerText"
class="form-cw">
- <auto-complete-input v-model="newStatus.status"
- :classObj="{ 'form-control': true }"
- :placeholder="$t('post_status.default')"
- :autoResize="true"
- :multiline="true"
- :drop="fileDrop"
- :dragoverPrevent="fileDrag"
- :paste="paste"
- :keydownMetaEnter="postStatusCopy"
- :keyupCtrlEnter="postStatusCopy"/>
+ <textarea
+ ref="textarea"
+ @click="setCaret"
+ @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
+ @keydown.down="cycleForward"
+ @keydown.up="cycleBackward"
+ @keydown.shift.tab="cycleBackward"
+ @keydown.tab="cycleForward"
+ @keydown.enter="replaceCandidate"
+ @keydown.meta.enter="postStatus(newStatus)"
+ @keyup.ctrl.enter="postStatus(newStatus)"
+ @drop="fileDrop"
+ @dragover.prevent="fileDrag"
+ @input="resize"
+ @paste="paste">
+ </textarea>
<div class="visibility-tray">
<span class="text-format" v-if="formattingOptionsEnabled">
<label for="post-content-type" class="select">
@@ -46,6 +52,17 @@
</div>
</div>
</div>
+ <div style="position:relative;" v-if="candidates">
+ <div class="autocomplete-panel">
+ <div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
+ <div class="autocomplete" :class="{ highlighted: candidate.highlighted }">
+ <span v-if="candidate.img"><img :src="candidate.img"></img></span>
+ <span v-else>{{candidate.utf}}</span>
+ <span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
+ </div>
+ </div>
+ </div>
+ </div>
<div class='form-bottom'>
<media-upload ref="mediaUpload" @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="uploadFailed" :drop-files="dropFiles"></media-upload>
@@ -233,5 +250,52 @@
cursor: pointer;
z-index: 4;
}
+
+ .autocomplete-panel {
+ margin: 0 0.5em 0 0.5em;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
+ position: absolute;
+ z-index: 1;
+ box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
+ // this doesn't match original but i don't care, making it uniform.
+ box-shadow: var(--popupShadow);
+ min-width: 75%;
+ background: $fallback--bg;
+ background: var(--bg, $fallback--bg);
+ color: $fallback--lightText;
+ color: var(--lightText, $fallback--lightText);
+ }
+
+ .autocomplete {
+ cursor: pointer;
+ padding: 0.2em 0.4em 0.2em 0.4em;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.4);
+ display: flex;
+
+ img {
+ width: 24px;
+ height: 24px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ object-fit: contain;
+ }
+
+ span {
+ line-height: 24px;
+ margin: 0 0.1em 0 0.2em;
+ }
+
+ small {
+ margin-left: .5em;
+ color: $fallback--faint;
+ color: var(--faint, $fallback--faint);
+ }
+
+ &.highlighted {
+ background-color: $fallback--fg;
+ background-color: var(--lightBg, $fallback--fg);
+ }
+ }
}
</style>
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
@@ -2,7 +2,6 @@ import { unescape } from 'lodash'
import TabSwitcher from '../tab_switcher/tab_switcher.js'
import StyleSwitcher from '../style_switcher/style_switcher.vue'
-import AutoCompleteInput from '../autocomplete_input/autocomplete_input.vue'
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
const UserSettings = {
@@ -42,8 +41,7 @@ const UserSettings = {
},
components: {
StyleSwitcher,
- TabSwitcher,
- AutoCompleteInput
+ TabSwitcher
},
computed: {
user () {
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
@@ -9,9 +9,9 @@
<div class="setting-item" >
<h2>{{$t('settings.name_bio')}}</h2>
<p>{{$t('settings.name')}}</p>
- <auto-complete-input :classObj="{ 'name-changer': true }" :id="'username'" v-model="newName"/>
+ <input class='name-changer' id='username' v-model="newName"></input>
<p>{{$t('settings.bio')}}</p>
- <auto-complete-input :classObj="{ bio: true }" v-model="newBio" :multiline="true"/>
+ <textarea class="bio" v-model="newBio"></textarea>
<p>
<input type="checkbox" v-model="newLocked" id="account-locked">
<label for="account-locked">{{$t('settings.lock_account_description')}}</label>