Add Emoji functional component
This commit is contained in:
parent
ee1ef09201
commit
52982706fe
3 changed files with 61 additions and 8 deletions
|
@ -5,7 +5,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
import emojify from 'soapbox/features/emoji/emoji';
|
||||
import { Emoji } from 'soapbox/components/ui';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
import type { RootState } from 'soapbox/store';
|
||||
|
@ -109,12 +109,9 @@ class EmojiSelector extends ImmutablePureComponent<IEmojiSelector> {
|
|||
const { visible, focused, allowedEmoji } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys
|
||||
handlers={this.handlers}
|
||||
className='emoji-react-selector-container'
|
||||
>
|
||||
<HotKeys handlers={this.handlers}>
|
||||
<div
|
||||
className={classNames('emoji-react-selector w-max', { 'emoji-react-selector--visible': visible, 'emoji-react-selector--focused': focused })}
|
||||
className={classNames('flex absolute bg-white dark:bg-slate-500 px-2 py-3 rounded-full shadow-md opacity-0 pointer-events-none duration-100 w-max', { 'opacity-100 pointer-events-auto z-[999]': visible || focused })}
|
||||
onBlur={this.handleBlur}
|
||||
ref={this.setRef}
|
||||
>
|
||||
|
@ -122,11 +119,12 @@ class EmojiSelector extends ImmutablePureComponent<IEmojiSelector> {
|
|||
<button
|
||||
key={i}
|
||||
className='emoji-react-selector__emoji'
|
||||
dangerouslySetInnerHTML={{ __html: emojify(emoji) }}
|
||||
onClick={this.handleReact(emoji)}
|
||||
onKeyDown={this.handleKeyDown(i)}
|
||||
tabIndex={(visible || focused) ? 0 : -1}
|
||||
/>
|
||||
>
|
||||
<Emoji emoji={emoji} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
|
54
app/soapbox/components/ui/emoji/emoji.tsx
Normal file
54
app/soapbox/components/ui/emoji/emoji.tsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import { joinPublicPath } from 'soapbox/utils/static';
|
||||
|
||||
// Taken from twemoji-parser
|
||||
// https://github.com/twitter/twemoji-parser/blob/a97ef3994e4b88316812926844d51c296e889f76/src/index.js
|
||||
const removeVS16s = (rawEmoji: string): string => {
|
||||
const vs16RegExp = /\uFE0F/g;
|
||||
const zeroWidthJoiner = String.fromCharCode(0x200d);
|
||||
return rawEmoji.indexOf(zeroWidthJoiner) < 0 ? rawEmoji.replace(vs16RegExp, '') : rawEmoji;
|
||||
};
|
||||
|
||||
const toCodePoints = (unicodeSurrogates: string): string[] => {
|
||||
const points = [];
|
||||
let char = 0;
|
||||
let previous = 0;
|
||||
let i = 0;
|
||||
while (i < unicodeSurrogates.length) {
|
||||
char = unicodeSurrogates.charCodeAt(i++);
|
||||
if (previous) {
|
||||
points.push((0x10000 + ((previous - 0xd800) << 10) + (char - 0xdc00)).toString(16));
|
||||
previous = 0;
|
||||
} else if (char > 0xd800 && char <= 0xdbff) {
|
||||
previous = char;
|
||||
} else {
|
||||
points.push(char.toString(16));
|
||||
}
|
||||
}
|
||||
return points;
|
||||
};
|
||||
|
||||
interface IEmoji {
|
||||
className?: string,
|
||||
emoji: string,
|
||||
}
|
||||
|
||||
const Emoji: React.FC<IEmoji> = ({ className, emoji }): JSX.Element | null => {
|
||||
const codepoints = toCodePoints(removeVS16s(emoji));
|
||||
const filename = codepoints.join('-');
|
||||
|
||||
if (!filename) return null;
|
||||
|
||||
return (
|
||||
<img
|
||||
draggable='false'
|
||||
className={classNames('emojione', className)}
|
||||
alt={emoji}
|
||||
src={joinPublicPath(`packs/emoji/${filename}.svg`)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Emoji;
|
|
@ -2,6 +2,7 @@ export { default as Avatar } from './avatar/avatar';
|
|||
export { default as Button } from './button/button';
|
||||
export { Card, CardBody, CardHeader, CardTitle } from './card/card';
|
||||
export { default as Column } from './column/column';
|
||||
export { default as Emoji } from './emoji/emoji';
|
||||
export { default as Form } from './form/form';
|
||||
export { default as FormActions } from './form-actions/form-actions';
|
||||
export { default as FormGroup } from './form-group/form-group';
|
||||
|
|
Loading…
Reference in a new issue