Browse Source

Add emoji-mart

main
GreatBearShark 2 years ago
parent
commit
625fcd54da
  1. 1
      package.json
  2. 31
      public/custom.css
  3. 50
      src/components/markdown-textarea.tsx
  4. 20
      src/custom-emojis.ts
  5. 1
      vite.config.ts
  6. 24
      yarn.lock

1
package.json

@ -30,6 +30,7 @@
"bootswatch": "^4.3.1",
"choices.js": "^9.0.1",
"classcat": "^4.0.2",
"emoji-mart": "^3.0.0",
"emoji-short-name": "^1.0.0",
"husky": "^4.2.5",
"i18next": "^19.4.1",

31
public/custom.css

@ -156,6 +156,7 @@ a.text-body {
background-color: transparent;
border: 0;
box-shadow: 0;
width: 100%;
}
.welcome-container {
@ -627,4 +628,34 @@ a.text-body {
.user-online-badge {
display: flex;
align-items: center;
}
.emoji-picker-container {
position: absolute;
top: 30px;
z-index: 1000;
transform: translateX(-50%);
}
@media only screen and (max-width: 992px) {
.emoji-picker-container {
width: 100vw;
transform: translateX(0%);
position: fixed;
left: 0;
}
.emoji-picker-container > section {
width: 100% !important;
}
}
.click-away-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .3);
z-index: 999;
}

50
src/components/markdown-textarea.tsx

@ -17,6 +17,9 @@ import { i18n } from '../i18next';
import emojiShortName from 'emoji-short-name';
import { Icon } from './icon';
import { linkEvent } from '../linkEvent';
import 'emoji-mart/css/emoji-mart.css'
import { Picker } from 'emoji-mart'
import { customEmojis } from '../custom-emojis';
interface MarkdownTextAreaProps {
initialContent: string;
@ -35,6 +38,7 @@ interface MarkdownTextAreaState {
previewMode: boolean;
loading: boolean;
imageLoading: boolean;
showEmojiPicker: boolean;
}
export class MarkdownTextArea extends Component<
@ -45,17 +49,18 @@ export class MarkdownTextArea extends Component<
private formId = `comment-form-${randomStr()}`;
private tribute: Tribute;
private emptyState: MarkdownTextAreaState = {
content: this.props.initialContent,
content: this.props.initialContent || '',
previewMode: false,
loading: false,
imageLoading: false,
showEmojiPicker: false,
};
constructor(props: any, context: any) {
super(props, context);
this.tribute = setupTribute();
this.setupEmojiPicker();
// this.setupEmojiPicker();
this.state = this.emptyState;
}
@ -230,15 +235,27 @@ export class MarkdownTextArea extends Component<
// onChange={linkEvent(this, this.handleImageUpload)}
/>
</form> */}
<button
onClick={linkEvent(this, this.handleEmojiPickerClick)}
className="btn btn-sm text-muted"
data-tippy-content={i18n.t('emoji_picker')}
>
<svg className="icon icon-inline">
<use xlinkHref="#icon-smile" />
</svg>
</button>
<span style={{ position: 'relative' }}>
<button
onClick={this.toggleEmojiPicker}
className="btn btn-sm text-muted"
data-tippy-content={i18n.t('emoji_picker')}
type="button"
>
<svg className="icon icon-inline">
<use xlinkHref="#icon-smile" />
</svg>
</button>
{this.state.showEmojiPicker && (
<>
<div className="emoji-picker-container">
<Picker custom={customEmojis} onSelect={this.handleInsertEmoji} theme="auto" />
</div>
<div
onClick={this.toggleEmojiPicker} className="click-away-container" />
</>
)}
</span>
<button
className="btn btn-sm text-muted"
data-tippy-content={i18n.t('header')}
@ -391,6 +408,10 @@ export class MarkdownTextArea extends Component<
emojiPicker.togglePicker(event.target);
}
toggleEmojiPicker = () => {
this.setState({ showEmojiPicker: !this.state.showEmojiPicker });
}
handleContentChange(i: MarkdownTextArea, event: any) {
i.state.content = event.target.value;
i.setState(i.state);
@ -429,6 +450,13 @@ export class MarkdownTextArea extends Component<
i.props.onReplyCancel();
}
handleInsertEmoji = ({ colons: shortcode }: { colons: string}) => {
const { content } = this.state;
// pad the emoji with spaces
this.setState({ content: `${content} ${shortcode} ` });
this.toggleEmojiPicker();
}
handleInsertLink(i: MarkdownTextArea, event: any) {
event.preventDefault();
if (!i.state.content) {

20
src/custom-emojis.ts

@ -133,16 +133,30 @@ export const emojiPaths = [
const EMOJI_DIR_PATH = `${BASE_PATH}emojis/`;
export const customEmojis = emojiPaths.map(path => ({
export const customEmojis = emojiPaths.map(path => {
const name = path.split('.')[0];
return {
name: name,
short_names: [name],
text: '',
emoticons: [],
keywords: [name],
imageUrl: EMOJI_DIR_PATH + path,
customCategory: 'Custom'
}
})
export const emojiReplacements = emojiPaths.map(path => ({
key: path.split('.')[0],
val: `<img className="icon icon-navbar" src="${EMOJI_DIR_PATH + path}" alt="${
val: `<img class="icon icon-navbar" src="${EMOJI_DIR_PATH + path}" alt="${
path.split('.')[0]
}" />`,
}));
export function replaceEmojis(html) {
let newHtml = html;
customEmojis.forEach(emoji => {
emojiReplacements.forEach(emoji => {
if (html.includes(`:${emoji.key}:`)) {
const regex = new RegExp(`:${emoji.key}:`, 'g');
newHtml = newHtml.replace(regex, emoji.val);

1
vite.config.ts

@ -6,6 +6,7 @@ const config: UserConfig = {
plugins: [reactPlugin],
optimizeDeps: {
include: [
'emoji-mart',
'markdown-it-container',
'moment',
'markdown-it-emoji',

24
yarn.lock

@ -218,6 +218,13 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/[email protected]^7.0.0", "@babel/[email protected]^7.5.5":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.0.tgz#f10245877042a815e07f7e693faff0ae9d3a2aac"
integrity sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/[email protected]^7.1.2", "@babel/[email protected]^7.3.1", "@babel/[email protected]^7.4.5", "@babel/[email protected]^7.6.3", "@babel/[email protected]^7.7.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
@ -225,13 +232,6 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/[email protected]^7.5.5":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.0.tgz#f10245877042a815e07f7e693faff0ae9d3a2aac"
integrity sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/[email protected]^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
@ -1919,6 +1919,14 @@ [email protected]^1.0.1:
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
[email protected]^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-3.0.0.tgz#eca24a04881e27752a6921e09f65a86ce8539a50"
integrity sha512-r5DXyzOLJttdwRYfJmPq/XL3W5tiAE/VsRnS0Hqyn27SqPA/GOYwVUSx50px/dXdJyDSnvmoPbuJ/zzhwSaU4A==
dependencies:
"@babel/runtime" "^7.0.0"
prop-types "^15.6.0"
[email protected]^7.0.1, [email protected]^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@ -4641,7 +4649,7 @@ [email protected]^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
[email protected]^15.6.2, [email protected]^15.7.2:
[email protected]^15.6.0, [email protected]^15.6.2, pr[email protected]^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==

Loading…
Cancel
Save