|
40 | 40 | <li v-if="admin"><i @click.stop.prevent="openResolution" class="fas fa-desktop"></i></li>
|
41 | 41 | <li v-if="!controlLocked && !implicitHosting" :class="extraControls || 'extra-control'">
|
42 | 42 | <i
|
43 |
| - :class="[hosted && !hosting ? 'disabled' : '', !hosted && !hosting ? 'faded' : '', 'fas', 'fa-keyboard']" |
| 43 | + :class="[ |
| 44 | + hosted && !hosting ? 'disabled' : '', |
| 45 | + !hosted && !hosting ? 'faded' : '', |
| 46 | + 'fas', |
| 47 | + 'fa-computer-mouse', |
| 48 | + ]" |
44 | 49 | @click.stop.prevent="toggleControl"
|
45 | 50 | />
|
46 | 51 | </li>
|
|
57 | 62 | class="fas fa-external-link-alt"
|
58 | 63 | />
|
59 | 64 | </li>
|
| 65 | + <li |
| 66 | + v-if="hosting && is_touch_device" |
| 67 | + :class="extraControls || 'extra-control'" |
| 68 | + @click.stop.prevent="toggleMobileKeyboard" |
| 69 | + > |
| 70 | + <i class="fas fa-keyboard" /> |
| 71 | + </li> |
60 | 72 | </ul>
|
61 | 73 | <neko-resolution ref="resolution" v-if="admin" />
|
62 | 74 | <neko-clipboard ref="clipboard" v-if="hosting && (!clipboard_read_available || !clipboard_write_available)" />
|
|
117 | 129 | }
|
118 | 130 | @media (max-width: 768px) {
|
119 | 131 | &.extra-control {
|
120 |
| - display: inline-block; |
| 132 | + display: block; |
121 | 133 | }
|
122 | 134 | }
|
123 | 135 |
|
|
353 | 365 | return this.$accessor.video.horizontal
|
354 | 366 | }
|
355 | 367 |
|
| 368 | + get is_touch_device() { |
| 369 | + return ( |
| 370 | + // check if the device has a touch screen |
| 371 | + ('ontouchstart' in window || navigator.maxTouchPoints > 0) && |
| 372 | + // we also check if the device has a pointer |
| 373 | + !window.matchMedia('(pointer:fine)').matches && |
| 374 | + // and is capable of hover, then it probably has a mouse |
| 375 | + !window.matchMedia('(hover:hover)').matches |
| 376 | + ) |
| 377 | + } |
| 378 | +
|
356 | 379 | @Watch('width')
|
357 | 380 | onWidthChanged() {
|
358 | 381 | this.onResize()
|
|
804 | 827 | this._overlay.focus()
|
805 | 828 | }
|
806 | 829 | }
|
| 830 | +
|
| 831 | + // |
| 832 | + // mobile keyboard |
| 833 | + // |
| 834 | +
|
| 835 | + kbdShow = false |
| 836 | + kbdOpen = false |
| 837 | +
|
| 838 | + showMobileKeyboard() { |
| 839 | + // skip if not a touch device |
| 840 | + if (!this.is_touch_device) return |
| 841 | +
|
| 842 | + this.kbdShow = true |
| 843 | + this.kbdOpen = false |
| 844 | +
|
| 845 | + const overlay = this.$refs.overlay as HTMLTextAreaElement |
| 846 | + overlay.focus() |
| 847 | + window.visualViewport?.addEventListener('resize', this.onVisualViewportResize) |
| 848 | + } |
| 849 | +
|
| 850 | + hideMobileKeyboard() { |
| 851 | + // skip if not a touch device |
| 852 | + if (!this.is_touch_device) return |
| 853 | +
|
| 854 | + this.kbdShow = false |
| 855 | + this.kbdOpen = false |
| 856 | +
|
| 857 | + const overlay = this.$refs.overlay as HTMLTextAreaElement |
| 858 | + window.visualViewport?.removeEventListener('resize', this.onVisualViewportResize) |
| 859 | + overlay.blur() |
| 860 | + } |
| 861 | +
|
| 862 | + toggleMobileKeyboard() { |
| 863 | + // skip if not a touch device |
| 864 | + if (!this.is_touch_device) return |
| 865 | +
|
| 866 | + if (this.kbdShow) { |
| 867 | + this.hideMobileKeyboard() |
| 868 | + } else { |
| 869 | + this.showMobileKeyboard() |
| 870 | + } |
| 871 | + } |
| 872 | +
|
| 873 | + // visual viewport resize event is fired when keyboard is opened or closed |
| 874 | + // android does not blur textarea when keyboard is closed, so we need to do it manually |
| 875 | + onVisualViewportResize() { |
| 876 | + if (!this.kbdShow) return |
| 877 | +
|
| 878 | + if (!this.kbdOpen) { |
| 879 | + this.kbdOpen = true |
| 880 | + } else { |
| 881 | + this.hideMobileKeyboard() |
| 882 | + } |
| 883 | + } |
807 | 884 | }
|
808 | 885 | </script>
|
0 commit comments