Skip to content

Instantly share code, notes, and snippets.

@BrianHung
Created July 8, 2020 05:31
Show Gist options
  • Select an option

  • Save BrianHung/543ddff995d5c60b522f99091bfca814 to your computer and use it in GitHub Desktop.

Select an option

Save BrianHung/543ddff995d5c60b522f99091bfca814 to your computer and use it in GitHub Desktop.
import { VNode, CreateElement } from "vue"
import { Component, Vue, Prop } from 'vue-property-decorator';
import { Emoji } from "./utils"
import "./EmojiMart.css"
import * as Fuse from 'fuse.js';
@Component({})
export default class EmojiMart extends Vue {
@Prop({ required: true }) emojiData!: Record<string, Emoji[]>;
@Prop({ required: true }) handleClick: any;
@Prop({ default: true }) showNavbar!: boolean;
@Prop({ default: true }) showFooter!: boolean;
@Prop({ default: true }) showScroll!: boolean;
@Prop({ default: "auto" }) colorTheme!: string;
previewEmoji: Emoji | null = null;
searchEmojis: Record<string, Emoji[]> = {};
skinTone!: string | null;
index: any; // search index
query: string = "";
created () {
this.index = new Fuse.default(Object.values(this.emojiData).flat(), {
threshold: 0.30,
keys: [{name: "name", weight: 0.70}, {name: "keywords", weight: 0.30}]
});
}
public search(query: string): void {
this.query = query
if (query === "") {
this.searchEmojis = Object.keys(this.emojiData).reduce((sum, category) => ({...sum, [category as string]: []}), {})
} else {
const filter = this.index.search(query).map((result: Fuse.default.FuseResult<any>) => result.item);
this.searchEmojis = Object.entries(this.emojiData)
.map(([category, list]) => ([category, list.filter(e => filter.includes(e))]))
.reduce((sum, [category, list]) => ({...sum, [category as string]: list}), {})
}
}
private scrollToCategory(category: string): void {
const categoryElement = Array.from(document.querySelectorAll('div.emoji-mart-category'))
.find(element => (element as HTMLElement).dataset.category === category);
categoryElement && categoryElement.scrollIntoView({behavior: "smooth"});
}
private handleClickInScroll(event: Event) {
const element = event.target as HTMLElement,
emojiSpan = element.closest("span.emoji-mart-emoji") as HTMLElement;
if (emojiSpan === null) {
return
} else {
const emoji = Object.values(this.emojiData).flat().find((e: Emoji) => e.unicode === emojiSpan.dataset.unicode)
this.handleClick(emoji)
}
}
private handleMouseInScroll(event: Event) {
const element = event.target as HTMLElement,
emojiSpan = element.closest("span.emoji-mart-emoji") as HTMLElement;
if (emojiSpan === null) {
this.previewEmoji = null;
} else {
const emoji = Object.values(this.emojiData).flat().find((e: Emoji) => e.unicode === emojiSpan.dataset.unicode)
this.previewEmoji = emoji || null;
}
}
private createEmojiSpan (emoji: Emoji): VNode {
const h: CreateElement = this.$createElement
return (
<span class="emoji-mart-emoji" data-unicode={emoji.unicode}>
<svg class="emoji-mart-emoji-svg">
<use href={`./twemoji.svg#${emoji.unicode}`}></use>
</svg>
</span>
)
}
private get colorThemeClass(): string {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return "emoji-mart emoji-mart-dark"
} else {
return "emoji-mart"
}
}
render (h: CreateElement): VNode {
return (
<div class={this.colorThemeClass}>
{ this.showNavbar &&
<div class="emoji-mart-navbar">
{ Object.entries(this.emojiData).map(([category, list]) =>
<span class="emoji-mart-navbar-category" onClick={() => this.scrollToCategory(category)}>
{this.createEmojiSpan(list[0])}
</span>
)
}
</div>
}
<div class="emoji-mart-scroll" onClick={this.handleClickInScroll} onMousemove={this.showFooter ? this.handleMouseInScroll : () => undefined}>
{ this.query !== ""
? Object.values(this.searchEmojis).flat().length !== 0
? Object.entries(this.searchEmojis).map(([category, list]) =>
list.length !== 0 &&
<div class="emoji-mart-category" data-category={category}>
<div class="emoji-mart-category-title">{category}</div>
<div class="emoji-mart-category-emoji">
{ list.map((emoji: Emoji) => this.createEmojiSpan(emoji)) }
</div>
</div>
)
: <div class="emoji-mart-category">
<div class="emoji-mart-category-title">No results</div>
</div>
: this.showScroll &&
Object.entries(this.emojiData).map(([category, list]) =>
list.length !== 0 &&
<div class="emoji-mart-category" data-category={category}>
<div class="emoji-mart-category-title">{category}</div>
<div class="emoji-mart-category-emoji">
{ list.map((emoji: Emoji) => this.createEmojiSpan(emoji)) }
</div>
</div>
)
}
</div>
{ this.showFooter &&
<div class="emoji-mart-footer">
{ this.createEmojiSpan(this.previewEmoji !== null ? this.previewEmoji : {name: "wave", unicode: "1f44b", keywords: []}) }
<div class="emoji-mart-name">
{ this.previewEmoji !== null ? this.previewEmoji.name : <span style="font-size: 1.2em; font-weight: 500;">Emoji Mart</span> }
</div>
</div>
}
</div>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment