EmojiSelector.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <div class='emoji_selector'>
  3. <div
  4. class='emoji_selector__overlay'
  5. :class='{ "emoji_selector__overlay--show" : value }'
  6. @click='$emit("input", false)'
  7. ></div>
  8. <div
  9. class='emoji_selector__tooltip'
  10. @scroll='setStickyIndex'
  11. ref='tooltip'
  12. :class='{
  13. "emoji_selector__tooltip--show" : value,
  14. "emoji_selector__tooltip--right" : rightAlign
  15. }'
  16. >
  17. <template v-for='(row, $index) in emojis'>
  18. <div
  19. class='emoji_selector__title'
  20. :class='{ "emoji_selector__title--sticky" : stickyIndex === $index }'
  21. ref='title'
  22. >{{row.title}}</div>
  23. <div class='emoji_selector__row' ref='emoji_row'>
  24. <span
  25. class='emoji_selector__emoji'
  26. v-for='emoji in row.emojis'
  27. @click='emitEmoji(emoji)'
  28. >{{emoji}}</span>
  29. </div>
  30. </template>
  31. </div>
  32. </div>
  33. </template>
  34. <script>
  35. import throttle from 'lodash.throttle'
  36. export default {
  37. name: 'EmojiSelector',
  38. props: ['value', 'right-align'],
  39. data () {
  40. return {
  41. stickyIndex: 0,
  42. emojis: [
  43. { title: 'smileys', emojis: [
  44. '😀' , '😃' , '😄' , '😁' , '😆' , '😅' , '😂' , '🤣' , '😊' , '😇' , '🙂' , '🙃' , '😉' , '😌' , '😍' , '😘' , '😗' , '😙' , '😚' , '😋' , '😜' , '😝' , '😛' , '🤑' , '🤗' , '🤓' , '😎' , '🤡' , '🤠' , '😏' , '😒' , '😞' , '😔' , '😟' , '😕' , '🙁' , '😣' , '😖' , '😫' , '😩' , '😤' , '😠' , '😡' , '😶' , '😐' , '😑' , '😯' , '😦' , '😧' , '😮' , '😲' , '😵' , '😳' , '😱' , '😨' , '😰' , '😢' , '😥' , '🤤' , '😭' , '😓' , '😪' , '😴' , '🙄' , '🤔' , '🤥' , '😬' , '🤐'
  45. ]},
  46. { title: 'people', emojis: [
  47. '👶' , '👦' , '👧' , '👨' , '👩' , '👱‍♀️' , '👱' , '👴' , '👵' , '👲' , '👳‍♀️' , '👳' , '👮‍♀️' , '👮', '💁', '💁‍♂️', '🙅', '🙅‍♂️', '🙆', '🙆‍♂️', '🙋', '🙋‍♂️', '💃', '🕺', '👯', '👯‍♂️', '🚶‍♀️', '🚶', '🏃‍♀️'
  48. ]},
  49. { title: 'animals', emojis: [
  50. '🐶' , '🐱' , '🐭' , '🐹' , '🐰' , '🦊' , '🐻' , '🐼' , '🐨' , '🐯' , '🦁' , '🐮' , '🐷' , '🐽' , '🐸' , '🐵' , '🙊' , '🙉' , '🙊' , '🐒' , '🐔' , '🐧' , '🐦' , '🐤' , '🐣' , '🐥' , '🦆' , '🦅' , '🦉' , '🦇' , '🐺' , '🐗' , '🐴' , '🦄' , '🐝' , '🐛' , '🦋' , '🐌' , '🐞' , '🐜' , '🕷' , '🐢' , '🐍'
  51. ]},
  52. ]
  53. }
  54. },
  55. methods: {
  56. emitEmoji (emoji) {
  57. this.$emit('input', false)
  58. this.$emit('emoji', emoji)
  59. },
  60. setStickyIndex: throttle(function (e) {
  61. let tooltipRect = this.$refs.tooltip.getBoundingClientRect();
  62. let sortedRows = this.$refs.emoji_row.sort((a, b) => {
  63. a.rect = a.getBoundingClientRect()
  64. b.rect = b.getBoundingClientRect()
  65. return a.rect - b.rect
  66. })
  67. let emojiRowsInView = sortedRows.filter(row => {
  68. let rowRect = row.rect
  69. return rowRect.top < tooltipRect.bottom && rowRect.bottom > tooltipRect.top;
  70. })
  71. let topRowInView = emojiRowsInView[0]
  72. this.stickyIndex = sortedRows.indexOf(topRowInView)
  73. }, 100)
  74. },
  75. mounted () {
  76. this.setStickyIndex()
  77. }
  78. }
  79. </script>
  80. <style lang='scss' scoped>
  81. @import '../assets/scss/variables.scss';
  82. .emoji_selector {
  83. display: inline-block;
  84. position: absolute;
  85. transform: translateZ(0);
  86. @at-root #{&}__overlay {
  87. pointer-events: none;
  88. position: fixed;
  89. top: 0;
  90. left: 0;
  91. width: 100%;
  92. height: 100%;
  93. z-index: 3;
  94. @at-root #{&}--show {
  95. pointer-events: all;
  96. }
  97. }
  98. @at-root #{&}__tooltip {
  99. pointer-events: none;
  100. opacity: 0;
  101. bottom: calc(100% + 3rem);
  102. transition: all 0.2s;
  103. position: absolute;
  104. width: 14rem;
  105. height: 7rem;
  106. border-radius: 0.25rem;
  107. border: 0.125rem solid $color__gray--primary;
  108. background-color: #fff;
  109. left: 0.25rem;
  110. box-shadow: 0 10px 10px rgba(0, 0, 0, 0.22);
  111. cursor: default;
  112. overflow-y: auto;
  113. padding: 0 0.375rem;
  114. padding-top: 1.15rem;
  115. z-index: 4;
  116. @at-root #{&}--show {
  117. pointer-events: all;
  118. opacity: 1;
  119. bottom: calc(100% + 2rem);
  120. }
  121. @at-root #{&}--right {
  122. left: 22.5rem;
  123. }
  124. }
  125. @at-root #{&}__row {
  126. display: block;
  127. text-align: left;
  128. line-height: 1.6rem;
  129. }
  130. @at-root #{&}__title {
  131. font-weight: bold;
  132. font-variant: small-caps;
  133. font-size: 0.9rem;
  134. text-align: left;
  135. color: $color__text--primary;
  136. padding-left: 0.375rem;
  137. @at-root #{&}--sticky {
  138. margin-top: -1.125rem;
  139. width: 13.25rem;
  140. background: rgba(255, 255, 255, 0.97);
  141. position: fixed;
  142. padding-bottom: 0.125rem;
  143. top: -7.75rem;
  144. pointer-events: none;
  145. }
  146. }
  147. @at-root #{&}__emoji {
  148. padding: 0.25rem;
  149. border-radius: 0.25rem;
  150. transition: all 0.2s;
  151. &:hover {
  152. background-color: $color__gray--primary;
  153. }
  154. &:active {
  155. background-color: $color__gray--darker;
  156. }
  157. }
  158. }
  159. @media (max-width: 420px) {
  160. .emoji_selector {
  161. display: none;
  162. }
  163. }
  164. </style>