Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. 'use client'
  2. import type { ChangeEvent, FC } from 'react'
  3. import React, { useState } from 'react'
  4. import data from '@emoji-mart/data'
  5. import type { EmojiMartData } from '@emoji-mart/data'
  6. import { init } from 'emoji-mart'
  7. import {
  8. ChevronDownIcon,
  9. ChevronUpIcon,
  10. MagnifyingGlassIcon,
  11. } from '@heroicons/react/24/outline'
  12. import Input from '@/app/components/base/input'
  13. import Divider from '@/app/components/base/divider'
  14. import { searchEmoji } from '@/utils/emoji'
  15. import cn from '@/utils/classnames'
  16. declare global {
  17. // eslint-disable-next-line ts/no-namespace
  18. namespace JSX {
  19. // eslint-disable-next-line ts/consistent-type-definitions
  20. interface IntrinsicElements {
  21. 'em-emoji': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
  22. }
  23. }
  24. }
  25. init({ data })
  26. const backgroundColors = [
  27. '#FFEAD5',
  28. '#E4FBCC',
  29. '#D3F8DF',
  30. '#E0F2FE',
  31. '#E0EAFF',
  32. '#EFF1F5',
  33. '#FBE8FF',
  34. '#FCE7F6',
  35. '#FEF7C3',
  36. '#E6F4D7',
  37. '#D5F5F6',
  38. '#D1E9FF',
  39. '#D1E0FF',
  40. '#D5D9EB',
  41. '#ECE9FE',
  42. '#FFE4E8',
  43. ]
  44. type IEmojiPickerInnerProps = {
  45. emoji?: string
  46. background?: string
  47. onSelect?: (emoji: string, background: string) => void
  48. className?: string
  49. }
  50. const EmojiPickerInner: FC<IEmojiPickerInnerProps> = ({
  51. onSelect,
  52. className,
  53. }) => {
  54. const { categories } = data as EmojiMartData
  55. const [selectedEmoji, setSelectedEmoji] = useState('')
  56. const [selectedBackground, setSelectedBackground] = useState(backgroundColors[0])
  57. const [showStyleColors, setShowStyleColors] = useState(false)
  58. const [searchedEmojis, setSearchedEmojis] = useState<string[]>([])
  59. const [isSearching, setIsSearching] = useState(false)
  60. React.useEffect(() => {
  61. if (selectedEmoji) {
  62. setShowStyleColors(true)
  63. if (selectedBackground)
  64. onSelect?.(selectedEmoji, selectedBackground)
  65. }
  66. }, [onSelect, selectedEmoji, selectedBackground])
  67. return <div className={cn(className, 'flex flex-col')}>
  68. <div className='flex w-full flex-col items-center px-3 pb-2'>
  69. <div className="relative w-full">
  70. <div className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center pl-3">
  71. <MagnifyingGlassIcon className="h-5 w-5 text-text-quaternary" aria-hidden="true" />
  72. </div>
  73. <Input
  74. className="pl-10"
  75. type="search"
  76. id="search"
  77. placeholder="Search emojis..."
  78. onChange={async (e: ChangeEvent<HTMLInputElement>) => {
  79. if (e.target.value === '') {
  80. setIsSearching(false)
  81. }
  82. else {
  83. setIsSearching(true)
  84. const emojis = await searchEmoji(e.target.value)
  85. setSearchedEmojis(emojis)
  86. }
  87. }}
  88. />
  89. </div>
  90. </div>
  91. <Divider className='my-3' />
  92. <div className="w-full flex-1 overflow-y-auto overflow-x-hidden px-3">
  93. {isSearching && <>
  94. <div key={'category-search'} className='flex flex-col'>
  95. <p className='system-xs-medium-uppercase mb-1 text-text-primary'>Search</p>
  96. <div className='grid h-full w-full grid-cols-8 gap-1'>
  97. {searchedEmojis.map((emoji: string, index: number) => {
  98. return <div
  99. key={`emoji-search-${index}`}
  100. className='inline-flex h-10 w-10 items-center justify-center rounded-lg'
  101. onClick={() => {
  102. setSelectedEmoji(emoji)
  103. }}
  104. >
  105. <div className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg p-1 ring-components-input-border-hover ring-offset-1 hover:ring-1'>
  106. <em-emoji id={emoji} />
  107. </div>
  108. </div>
  109. })}
  110. </div>
  111. </div>
  112. </>}
  113. {categories.map((category, index: number) => {
  114. return <div key={`category-${index}`} className='flex flex-col'>
  115. <p className='system-xs-medium-uppercase mb-1 text-text-primary'>{category.id}</p>
  116. <div className='grid h-full w-full grid-cols-8 gap-1'>
  117. {category.emojis.map((emoji, index: number) => {
  118. return <div
  119. key={`emoji-${index}`}
  120. className='inline-flex h-10 w-10 items-center justify-center rounded-lg'
  121. onClick={() => {
  122. setSelectedEmoji(emoji)
  123. }}
  124. >
  125. <div className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg p-1 ring-components-input-border-hover ring-offset-1 hover:ring-1'>
  126. <em-emoji id={emoji} />
  127. </div>
  128. </div>
  129. })}
  130. </div>
  131. </div>
  132. })}
  133. </div>
  134. {/* Color Select */}
  135. <div className={cn('flex items-center justify-between p-3 pb-0')}>
  136. <p className='system-xs-medium-uppercase mb-2 text-text-primary'>Choose Style</p>
  137. {showStyleColors ? <ChevronDownIcon className='h-4 w-4' onClick={() => setShowStyleColors(!showStyleColors)} /> : <ChevronUpIcon className='h-4 w-4' onClick={() => setShowStyleColors(!showStyleColors)} />}
  138. </div>
  139. {showStyleColors && <div className='grid w-full grid-cols-8 gap-1 px-3'>
  140. {backgroundColors.map((color) => {
  141. return <div
  142. key={color}
  143. className={
  144. cn(
  145. 'cursor-pointer',
  146. 'ring-offset-1 hover:ring-1',
  147. 'inline-flex h-10 w-10 items-center justify-center rounded-lg',
  148. color === selectedBackground ? 'ring-1 ring-components-input-border-hover' : '',
  149. )}
  150. onClick={() => {
  151. setSelectedBackground(color)
  152. }}
  153. >
  154. <div className={cn(
  155. 'flex h-8 w-8 items-center justify-center rounded-lg p-1',
  156. )
  157. } style={{ background: color }}>
  158. {selectedEmoji !== '' && <em-emoji id={selectedEmoji} />}
  159. </div>
  160. </div>
  161. })}
  162. </div>}
  163. </div>
  164. }
  165. export default EmojiPickerInner