OverviewFilesCommitsIssuesDocsReleasesPeers

holepunchto-deps / @holepunchto / emojis / index.js

188 lines · javascript ref:
import toArray from 'lodash.toarray'
import rawEmojisBase from './data/raw-emojis-base.js'
import rawEmojisCustom from './data/raw-emojis-custom.js'
import rawEmojisOverwrite from './data/raw-emojis-overwrite.js'
import messages from './data/messages.js'
import skinTones from './data/skin-tones.js'
export { default as emoji_categories } from './emoji_categories.json' with { type: 'json' }

const CATEGORY_RECENTS = 'recents'
const CATEGORY_CUSTOM = 'custom'

const dataByShortCode = new Map()
const dataByEmoji = new Map()
const dataByHex = new Map()
const customEmojisData = []
const allEmojisData = []
const allEmojisDataByTone = {}
const categories = []

const normalizeEmoji = (raw, { emojisPath }) => {
  const { path, pathRiv, ...rest } = raw
  const result = { ...rest }
  if (path) {
    result.url = `${emojisPath}${path}`
  }
  if (pathRiv) {
    result.urlRiv = `${emojisPath}${pathRiv}`
  }
  return result
}

function fill(emojiData) {
  for (const shortCode of emojiData.shortCodes) {
    dataByShortCode.set(shortCode, emojiData)
  }
  dataByEmoji.set(emojiData.emoji, emojiData)
  dataByHex.set(emojiData.hexcode, emojiData)
  emojiData.skins?.forEach(fill)
}

function flatSlice(hits, maxResults) {
  const result = []
  for (const hit of hits) {
    for (const emoji of hit) {
      result.push(emoji)
      if (result.length >= maxResults) {
        break
      }
    }
    if (result.length >= maxResults) {
      break
    }
  }
  return result
}

export function initEmojis({ emojisPath = '/assets/emojis/' }) {
  rawEmojisCustom.forEach((raw) => {
    const customEmojiData = normalizeEmoji(raw, { emojisPath })
    const data = { ...customEmojiData, custom: true }
    fill(data)
    customEmojisData.push(data)
    allEmojisData.push(data)
  })

  rawEmojisBase.forEach((emojiData) => {
    if (rawEmojisOverwrite[emojiData.hexcode]) {
      emojiData = { ...emojiData, ...rawEmojisOverwrite[emojiData.hexcode] }
    }
    fill(emojiData)
    allEmojisData.push(emojiData)
  })

  categories.push(CATEGORY_RECENTS)
  categories.push(CATEGORY_CUSTOM)
  messages.groups.forEach(({ key }) => categories.push(key))
}

export function getEmojiData(any) {
  return dataByHex.get(any) || dataByShortCode.get(any) || dataByEmoji.get(any)
}

export function getDataOfSkin(tone) {
  if (!tone) return allEmojisData

  if (!allEmojisDataByTone[tone]) {
    allEmojisDataByTone[tone] = allEmojisData.map((emoji) => {
      return emoji.skins?.find(skin => skin.tone === tone) || emoji
    })
  }

  return allEmojisDataByTone[tone]
}

export function emojify(text, format) {
  return text
    .split(/:([a-zA-Z0-9_\-+]+):/g)
    .map((string, index) => {
      if (index % 2 === 0) {
        return string
      }

      if (!dataByShortCode.has(string)) {
        return `:${string}:`
      }
      const emojiData = dataByShortCode.get(string)
      const { emoji, alt } = emojiData

      if (format) {
        return format(emoji, string, emojiData)
      }

      return alt || emoji
    })
    .join('')
}

export function unemojify(text) {
  return toArray(text)
    .map((char) => {
      if (dataByEmoji.has(char)) {
        return `:${dataByEmoji.get(char).shortCodes[0]}:`
      }

      const hex = char.codePointAt(0).toString(16).toUpperCase()
      if (dataByHex.has(hex)) {
        return `:${dataByHex.get(hex).shortCodes[0]}:`
      }

      return char
    })
    .join('')
}

export function getAllEmojisData() {
  return allEmojisData
}

export function getCustomEmojisData() {
  return customEmojisData
}

export function getEmojiMessages() {
  return messages
}

export function getCategories() {
  return categories
}

export function getSkinTones() {
  return skinTones
}

export const emojiSearch = (data, termSearch, maxResults = 20) => {
  if (!termSearch) return data.slice(0, maxResults)

  const term = termSearch.toLowerCase()
  const termWithPrefix = `:${term}`

  const hits = [[], [], [], [], []]

  for (const emoji of data) {
    const { shortCodes = [], emoticon = [], tags = [] } = emoji
    const emoticons = Array.isArray(emoticon) ? emoticon : [emoticon]

    let prio = -1

    if (shortCodes.some((code) => code === term) || emoticons.some((code) => code === termWithPrefix)) {
      prio = 0
    } else if (shortCodes.some((code) => code.startsWith(term))) {
      prio = 1
    } else if (shortCodes.some((code) => code.includes(term))) {
      prio = 2
    } else if (tags.some((tag) => tag.startsWith(term))) {
      prio = 3
    } else if (tags.some((tag) => tag.includes(term))) {
      prio = 4
    }

    if (prio !== -1) {
      hits[prio].push(emoji)
    }
  }

  return flatSlice(hits, maxResults)
}