packages feed

kurita-0: client/src/Emoji.tsx

import React, {createContext, CSSProperties, useContext} from 'react'
import {useInView} from 'react-intersection-observer'

// from https://github.com/twitter/twemoji/blob/7914d152d65a5b3d8749e1768e9b25437ec9fd64/2/twemoji.js#L548-L566
function toCodePoint(unicodeSurrogates: string, sep: string = '-') {
  var
    r = [],
    c = 0,
    p = 0,
    i = 0;
  while (i < unicodeSurrogates.length) {
    c = unicodeSurrogates.charCodeAt(i++);
    if (p) {
      r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
      p = 0;
    } else if (0xD800 <= c && c <= 0xDBFF) {
      p = c;
    } else {
      r.push(c.toString(16));
    }
  }
  return r.join(sep);
}

export const EmojiPath = createContext('')

const Emoji: React.FC<{
  className?: string,
  emoji: string,
  size: number,
  style?: CSSProperties,
}> = ({className, emoji, size, style}) => {
  const [viewRef, inView] = useInView({
    rootMargin: '100px 0px 100px 0px',
    triggerOnce: true,
  })

  const basePath = useContext(EmojiPath)
  const codePoint = toCodePoint(emoji)

  const placeholder = (
    <div
      ref={viewRef}
      className={className}
      style={{display: 'inline-block', width: size, height: size, ...style}}
    />
  )

  const emojiImg = (
    <img
      ref={viewRef}
      className={className}
      src={`${basePath}${codePoint}.svg`}
      alt={emoji}
      width={size}
      height={size}
      style={style}
    />
  )

  return inView ? emojiImg : placeholder
}

export default Emoji