<textarea class="compose-box-input" placeholder="What's on your mind?" ref:textarea bind:value=rawText on:blur="onBlur()" on:focus="onFocus()" on:selectionChange="onSelectionChange(event)" on:keydown="onKeydown(event)" ></textarea> <style> .compose-box-input { grid-area: input; margin: 10px 0 0 5px; padding: 10px; border: 1px solid var(--input-border); min-height: 75px; resize: none; overflow: hidden; word-wrap: break-word; /* Text must be at least 16px or else iOS Safari zooms in */ font-size: 1.2em; /* Hack to make Edge stretch the element all the way to the right. * Also desktop Safari makes the gauge stretch too far to the right without it. */ width: calc(100% - 5px); } </style> <script> import { store } from '../../_store/store' import { autosize } from '../../_utils/autosize' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import debounce from 'lodash/debounce' import { mark, stop } from '../../_utils/marks' import { selectionChange } from '../../_utils/events' import { clickSelectedAutosuggestionUsername } from '../../_actions/compose' import { clickSelectedAutosuggestionEmoji } from '../../_actions/emoji' export default { oncreate() { this.setupSyncFromStore() this.setupSyncToStore() this.setupAutosize() }, ondestroy() { this.teardownAutosize() }, methods: { setupSyncFromStore() { let textarea = this.refs.textarea let firstTime = true this.observe('text', text => { if (this.get('rawText') !== text) { this.set({rawText: text}) // this next autosize is required to resize after // the user clicks the "toot" button mark('autosize.update()') autosize.update(textarea) stop('autosize.update()') } if (firstTime) { firstTime = false if (this.get('autoFocus')) { textarea.focus() } } }) }, setupSyncToStore() { const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000) this.observe('rawText', rawText => { mark('observe rawText') let realm = this.get('realm') this.store.setComposeData(realm, {text: rawText}) saveText() stop('observe rawText') }, {init: false}) }, setupAutosize() { let textarea = this.refs.textarea requestAnimationFrame(() => { mark('autosize()') autosize(textarea) stop('autosize()') }) }, teardownAutosize() { mark('autosize.destroy()') autosize.destroy(this.refs.textarea) stop('autosize.destroy()') }, onBlur() { this.store.set({composeFocused: false}) }, onFocus() { this.store.set({composeFocused: true}) }, onSelectionChange(selectionStart) { this.store.set({composeSelectionStart: selectionStart}) }, onKeydown(e) { let { keyCode } = e switch (keyCode) { case 9: // tab case 13: //enter this.clickSelectedAutosuggestion(e) break case 38: // up this.incrementAutosuggestSelected(-1, e) break case 40: // down this.incrementAutosuggestSelected(1, e) break case 27: // escape this.clearAutosuggestions(e) break default: } }, clickSelectedAutosuggestion(event) { let autosuggestionShown = this.store.get('composeAutosuggestionShown') if (!autosuggestionShown) { return } let type = this.store.get('composeAutosuggestionType') if (type === 'account') { /* no await */ clickSelectedAutosuggestionUsername(this.get('realm')) } else { // emoji /* no await */ clickSelectedAutosuggestionEmoji(this.get('realm')) } event.preventDefault() event.stopPropagation() }, incrementAutosuggestSelected(increment, event) { let autosuggestionShown = this.store.get('composeAutosuggestionShown') if (!autosuggestionShown) { return } let selected = this.store.get('composeAutosuggestionSelected') || 0 let searchResults = this.store.get('composeAutosuggestionSearchResults') || [] selected += increment if (selected >= 0) { selected = selected % searchResults.length } else { selected = searchResults.length + selected } this.store.set({composeAutosuggestionSelected: selected}) event.preventDefault() event.stopPropagation() }, clearAutosuggestions(event) { let autosuggestionShown = this.store.get('composeAutosuggestionShown') if (!autosuggestionShown) { return } this.store.set({ composeAutosuggestionSearchResults: [], composeAutosuggestionSelected: 0 }) event.preventDefault() event.stopPropagation() } }, store: () => store, data: () => ({ rawText: '' }), computed: { postedStatusForRealm: ($postedStatusForRealm) => $postedStatusForRealm }, events: { selectionChange } } </script>