Quantcast
Channel: Ionic Framework - Ionic Forum
Viewing all articles
Browse latest Browse all 48980

Set focus and position end to textarea

$
0
0

I would like to set position on a textarea and set focus programmatically but it seems that there is a problem on iOS when the text is more long.

It’s a mention component which you can tag a people searched by the name.

<template>

  <div class="mentionable-textarea-wrapper">
    <textarea @ionFocus="onFocus"
                  @ionBlur="onBlur"
                  @keydown.right="isOpened = false"
                  @keydown.left="isOpened = false"
                  @ionChange="onChange"
                  v-model="text" rows="3" cols="20"
                  :placeholder="passedPlaceholder" ref="textarea">

    </textarea>

    <ion-content class="tooltip" v-show="isOpened && filteredItems.length">
      <ion-list class="user-list" mode="ios">
        <ion-item v-for="item in filteredItems" @click="onMentionSelect(item.id)" :key="item.id">
          <div class="tooltip-item">
            <span>{{ item.first_name }} {{ item.last_name }}</span>
          </div>
        </ion-item>
      </ion-list>
    </ion-content>
  </div>

</template>

<script lang="ts">
import {nextTick, defineComponent} from 'vue';
import {IonTextarea, IonList, IonItem, IonContent} from '@ionic/vue';

export default defineComponent({
  name: "MentionableTextarea",
  emits: ["onFocus", "onBlur", "update:modelValue", "update:selectedIds"],
  components: {
    IonTextarea,
    IonList,
    IonItem,
    IonContent
  },
  props: {
    modelValue: {
      type: String,
      default: ''
    },
    selectedIds: {
      type: Array,
      default: () => [] as any
    },
    placeholder: {
      type: String,
      default: ''
    },
    items: {
      type: Array as any,
      required: true,
      default: () => [] as any,
    }
  },
  data() {
    return {
      isOpened: false,
      passedPlaceholder: this.placeholder,
      passedItems: this.items,
      text: '',
      textToFilter: '',
      selectedMentions: [],
      previousTextByAt: [],
      selectionStart: 0,
    };
  },
  computed: {
    /**
     * @desc Filter items by text
     */
    filteredItems: function (this: any) {
      if (!this.textToFilter) {
        return this.items
      }
      const searchText = this.textToFilter.toLowerCase().trim().replace('@', '')
      return this.items.filter(item => {
        return item.first_name.toLowerCase().includes(searchText) || item.last_name.toLowerCase().includes(searchText) || item.username.toLowerCase().includes(searchText)
      })
    }
  },
  methods: {
    /**
     * @desc On focus callback
     */
    onFocus() {
      this.$emit('onFocus')
    },
    /**
     * @desc On blur callback
     */
    onBlur() {
      this.$emit('onBlur')
    },
    /**
     * @desc On input change event
     */
    onChange(e) {
      e.currentTarget.getInputElement().then(el => this.selectionStart = el.selectionStart)
      nextTick(() => {

        const splittedTextByAtWords = this._getTagsFromText()

        // Update list of results
        if (this.previousTextByAt && splittedTextByAtWords) {
          const difference = splittedTextByAtWords.filter(x => !this.previousTextByAt.includes(x));
          if (difference && difference.length) {
            this.textToFilter = difference[0]
            this.isOpened = true
          } else {
            this.textToFilter = ''
            this.isOpened = false
          }
        } else {
          this.isOpened = false;
        }

        this._updateSelectedUsers()

        this.previousTextByAt = splittedTextByAtWords

        this.$emit('update:modelValue', this._parseTextWithHtml())

      })
    },

    /**
     * @desc On mention select
     * @param id
     */
    onMentionSelect(id) {

      // eslint-disable-next-line @typescript-eslint/no-this-alias
      const $vm = this
      // Search item in array
      const foundItem = this.items.filter(x => x.id == id);
      const concatName = '@' + foundItem[0].username + ' ';
      const index = this.selectionStart - this.textToFilter.length;
      this._replaceTextAtIndex(index, concatName)


      setTimeout(function () {
        $vm.isOpened = false;
        // Find element and set focus to the right position
        const textarea = document.getElementsByClassName("textarea")[0] as HTMLInputElement
        textarea.focus()
        textarea.setSelectionRange($vm.text.length, $vm.text.length)
        console.log($vm.text.length)
      }, 500);

    },
    /**
     * @desc Get tags from ext
     */
    _getTagsFromText() {
      const splittedText = this.text.split(' ')
      const splittedTextByAtWords = []

      // Get all the words with @
      splittedText.forEach(function (item) {
        if (item.trim().startsWith('@')) {
          splittedTextByAtWords.push(item)
        }
      })

      return splittedTextByAtWords;
    },

    /**
     * @desc Update selected users array
     */
    _updateSelectedUsers() {
      this.selectedMentions = [];

      const splittedTextByAtWords = this._getTagsFromText()
      // eslint-disable-next-line @typescript-eslint/no-this-alias
      const $vm = this;

      splittedTextByAtWords.forEach(function (item) {
        // Update list of id if a username match
        const foundUserByUsername = $vm.items.filter(x => x.username === item.replace('@', ''));
        if (foundUserByUsername.length) {
          $vm.selectedMentions.push({'id': foundUserByUsername[0].id, 'username': foundUserByUsername[0].username})
        }
      });

      this.$emit('update:selectedIds', this.selectedMentions)
    },

    /**
     * @desc Add html for tags
     */
    _parseTextWithHtml() {
      // eslint-disable-next-line @typescript-eslint/no-this-alias
      const $vm = this;
      if (this.selectedMentions.length) {
        // Split text with space
        const newTextSplitted = this.text.split(" ");
        const newTextParsed = []
        newTextSplitted.forEach(function (piece) {
          // Search piece of string between mentions, if correspond attach html
          const foundMention = $vm.selectedMentions.filter(x => '@' + x.username === piece) as any
          if (foundMention.length) {
            piece = '<strong data-id="' + foundMention[0].id + '">@' + foundMention[0].username + '</strong>'
            newTextParsed.push(piece)
          } else {
            newTextParsed.push(piece)
          }
        })
        return newTextParsed.join(" ")
      } else {
        return this.text;
      }

    },

    _replaceTextAtIndex(index, replacement) {
      this.text = this.text.substr(0, index) + replacement + this.text.substr(index + replacement.length);
    },
  }
});
</script>

1 post - 1 participant

Read full topic


Viewing all articles
Browse latest Browse all 48980

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>