Skip to content

Commit

Permalink
feat: add (emojiSelect) output, fire on enter key
Browse files Browse the repository at this point in the history
  • Loading branch information
scttcper committed Mar 28, 2018
1 parent a93cae3 commit 5cf3af0
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 58 deletions.
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ __DEMO__: https://typectrl.github.io/ngx-emoji-mart/

This project is a port of [emoji-mart](https://github.com/missive/emoji-mart) by missive

- [Installation](#installation)
- [Components](#components)
- [Picker](#picker)
- [Emoji](#emoji)
- [Custom emojis](#custom-emojis)
- [Headless search](#headless-search)
- [Storage](#storage)
- [Features](#features)
- [Powerful search](#powerful-search)
- [Fully customizable](#fully-customizable)
- [Installation](#installation)
- [Components](#components)
- [Picker](#picker)
- [Emoji](#emoji)
- [Custom emojis](#custom-emojis)
- [Headless search](#headless-search)
- [Storage](#storage)
- [Features](#features)
- [Powerful search](#powerful-search)
- [Fully customizable](#fully-customizable)

## Installation

Expand Down Expand Up @@ -74,7 +74,8 @@ use component
| **custom** | | `[]` | [Custom emojis](#custom-emojis) |
| **recent** | | | Pass your own frequently used emojis as array of string IDs |
| **emojiSize** | | `24` | The emoji width and height |
| **(emojiClick)** | | | Params: `{ emoji, $event }` |
| **(emojiClick)** | | | not triggered on return key in search bar. Params: `{ emoji, $event }` |
| **(emojiSelect)** | | | whenever an emoji is selected. returns `{ emoji, $event }` |
| **perLine** | | `9` | Number of emojis per line. While there’s no minimum or maximum, this will affect the picker’s width. This will set _Frequently Used_ length as well (`perLine * 4`) |
| **i18n** | | [`{…}`](#i18n) | [An object](#i18n) containing localized strings |
| **native** | | `false` | Renders the native unicode emoji |
Expand Down
2 changes: 1 addition & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h2>Angular Emoji Mart ✨</h2>
[native]="native"
[hideRecent]="false"
[custom]="CUSTOM_EMOJIS"
(emojiClick)="handleClick($event)"
(emojiSelect)="handleClick($event)"
title="Pick your emoji…"
emoji='point_up'>
</emoji-mart>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/emoji/data/data.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Emoji } from '../emoji.component';
export interface EmojiCategory {
id: string;
name: string;
emojis: string[];
emojis: any[] | null;
anchor?: boolean;
first?: boolean;
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/picker/category.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export class CategoryComponent implements OnInit, AfterViewInit {
}
updateDisplay(display: 'none' | 'inherit') {
this.containerStyles.display = display;
this.getEmojis();
this.ref.detectChanges();
}
trackById(index: number, item: any) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/picker/emoji-search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class EmojiSearch {
return;
}

category.emojis.forEach(emojiId =>
category.emojis!.forEach(emojiId =>
pool[emojiId] = this.emojiService.names[emojiId],
);
});
Expand Down
1 change: 1 addition & 0 deletions src/lib/picker/picker.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<emoji-search
[i18n]="i18n"
(search)="handleSearch($event)"
(enterKey)="handleEnterKey($event)"
[include]="include"
[exclude]="exclude"
[custom]="custom"
Expand Down
112 changes: 68 additions & 44 deletions src/lib/picker/picker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@ import {
ViewChildren,
} from '@angular/core';

import { categories, Emoji, EmojiCategory, EmojiEvent } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { categories, Emoji, EmojiCategory, EmojiEvent, EmojiData } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { CategoryComponent } from './category.component';
import { EmojiFrequentlyService } from './emoji-frequently.service';
import { PreviewComponent } from './preview.component';
import { measureScrollbar } from './utils';


const RECENT_CATEGORY = {
const RECENT_CATEGORY: EmojiCategory = {
id: 'recent',
name: 'Recent',
emojis: null,
};
const SEARCH_CATEGORY = {
const SEARCH_CATEGORY: EmojiCategory = {
id: 'search',
name: 'Search',
emojis: null,
anchor: false,
};
const CUSTOM_CATEGORY = {
const CUSTOM_CATEGORY: EmojiCategory = {
id: 'custom',
name: 'Custom',
emojis: (<any[]>[]),
emojis: [],
};
const I18N = {
search: 'Search',
Expand Down Expand Up @@ -82,9 +82,11 @@ export class PickerComponent implements OnInit, AfterViewInit {
@Input() include: string[] = [];
@Input() exclude: string[] = [];
@Output() emojiClick = new EventEmitter<any>();
@Output() emojiSelect = new EventEmitter<any>();
@ViewChild('scrollRef') private scrollRef?: ElementRef;
@ViewChild('previewRef') private previewRef?: PreviewComponent;
@ViewChildren('categoryRef') private categoryRefs?: QueryList<CategoryComponent>;
@ViewChildren('categoryRef')
private categoryRefs?: QueryList<CategoryComponent>;
scrollHeight = 0;
clientHeight = 0;
selected?: string;
Expand All @@ -97,22 +99,27 @@ export class PickerComponent implements OnInit, AfterViewInit {
leaveTimeout: any;
NAMESPACE = 'emoji-mart';
measureScrollbar = 0;
@Input() backgroundImageFn: Emoji['backgroundImageFn'] = (set: string, sheetSize: number) =>

@Input()
backgroundImageFn: Emoji['backgroundImageFn'] = (
set: string,
sheetSize: number,
) =>
`https://unpkg.com/emoji-datasource-${this.set}@4.0.3/img/${
this.set
}/sheets-256/${this.sheetSize}.png`

constructor(
private frequently: EmojiFrequentlyService,
) { }
constructor(private frequently: EmojiFrequentlyService) {}

ngOnInit() {
// measure scroll
this.measureScrollbar = measureScrollbar();

this.i18n = { ...I18N, ...this.i18n };
this.i18n.categories = { ...I18N.categories, ...this.i18n.categories };
this.skin = JSON.parse(localStorage.getItem(`${this.NAMESPACE}.skin`) || 'null') || this.skin;
this.skin =
JSON.parse(localStorage.getItem(`${this.NAMESPACE}.skin`) || 'null') ||
this.skin;

const allCategories = [...categories];

Expand Down Expand Up @@ -160,8 +167,8 @@ export class PickerComponent implements OnInit, AfterViewInit {
const newEmojis = [];

const { emojis } = category;
for (let emojiIndex = 0; emojiIndex < emojis.length; emojiIndex++) {
const emoji = emojis[emojiIndex];
for (let emojiIndex = 0; emojiIndex < emojis!.length; emojiIndex++) {
const emoji = emojis![emojiIndex];
if (this.emojisToShowFilter(emoji)) {
newEmojis.push(emoji);
}
Expand Down Expand Up @@ -207,7 +214,7 @@ export class PickerComponent implements OnInit, AfterViewInit {
}

updateCategoriesSize() {
this.categoryRefs!.forEach((component) => component.memoizeSize());
this.categoryRefs!.forEach(component => component.memoizeSize());

if (this.scrollRef) {
const target = this.scrollRef.nativeElement;
Expand All @@ -216,8 +223,8 @@ export class PickerComponent implements OnInit, AfterViewInit {
}
}

handleAnchorClick($event: { category: EmojiCategory, index: number }) {
const component = this.categoryRefs!.find((n) => n.id === $event.category.id);
handleAnchorClick($event: { category: EmojiCategory; index: number }) {
const component = this.categoryRefs!.find(n => n.id === $event.category.id);
let scrollToComponent = null;

scrollToComponent = () => {
Expand Down Expand Up @@ -264,7 +271,7 @@ export class PickerComponent implements OnInit, AfterViewInit {
for (let i = 0, l = this.categories.length; i < l; i++) {
const ii = scrollingDown ? this.categories.length - 1 - i : i;
const category = this.categories[ii];
const component = this.categoryRefs!.find((n) => n.id === category.id);
const component = this.categoryRefs!.find(n => n.id === category.id);

if (component) {
const active = component.handleScroll(scrollTop);
Expand All @@ -282,7 +289,9 @@ export class PickerComponent implements OnInit, AfterViewInit {
}

if (scrollTop < minTop) {
activeCategory = this.categories.filter(category => !(category.anchor === false))[0];
activeCategory = this.categories.filter(
category => !(category.anchor === false),
)[0];
} else if (scrollTop + this.clientHeight >= this.scrollHeight) {
activeCategory = this.categories[this.categories.length - 1];
}
Expand Down Expand Up @@ -314,35 +323,20 @@ export class PickerComponent implements OnInit, AfterViewInit {
this.handleScroll();
}

handleEmojiOver($event: EmojiEvent) {
if (!this.showPreview || !this.previewRef) {
return;
}

const emojiData = CUSTOM_CATEGORY.emojis.find(customEmoji => customEmoji.id === $event.emoji.id);
if (emojiData) {
$event.emoji = { ...emojiData };
}

this.previewEmoji = $event.emoji;
clearTimeout(this.leaveTimeout);
}

handleEmojiLeave($event: EmojiEvent) {
if (!this.showPreview || !this.previewRef) {
return;
handleEnterKey($event: Event, emoji?: EmojiData) {
if (!emoji) {
if (SEARCH_CATEGORY.emojis !== null && SEARCH_CATEGORY.emojis.length) {
emoji = SEARCH_CATEGORY.emojis[0];
if (emoji) {
this.emojiSelect.emit({ $event, emoji });
} else {
return;
}
}
}

this.leaveTimeout = setTimeout(() => {
this.previewEmoji = null;
this.previewRef!.ref.markForCheck();
}, 16);
}

handleEmojiClick($event: EmojiEvent) {
this.emojiClick.emit($event);
if (!this.hideRecent && !this.recent) {
this.frequently.add($event.emoji);
this.frequently.add((<EmojiData>emoji));
}

const component = this.categoryRefs!.toArray()[1];
Expand All @@ -369,6 +363,36 @@ export class PickerComponent implements OnInit, AfterViewInit {
});
}
}
handleEmojiOver($event: EmojiEvent) {
if (!this.showPreview || !this.previewRef) {
return;
}

const emojiData = CUSTOM_CATEGORY.emojis!.find(
customEmoji => customEmoji.id === $event.emoji.id,
);
if (emojiData) {
$event.emoji = { ...emojiData };
}

this.previewEmoji = $event.emoji;
clearTimeout(this.leaveTimeout);
}
handleEmojiLeave($event: EmojiEvent) {
if (!this.showPreview || !this.previewRef) {
return;
}

this.leaveTimeout = setTimeout(() => {
this.previewEmoji = null;
this.previewRef!.ref.markForCheck();
}, 16);
}
handleEmojiClick($event: EmojiEvent) {
this.emojiClick.emit($event);
this.emojiSelect.emit($event);
this.handleEnterKey($event.$event, $event.emoji);
}
handleSkinChange(skin: Emoji['skin']) {
this.skin = skin;
localStorage.setItem(`${this.NAMESPACE}.skin`, String(skin));
Expand Down
6 changes: 6 additions & 0 deletions src/lib/picker/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EmojiSearch } from './emoji-search.service';
template: `
<div class="emoji-mart-search">
<input #inputRef type="text"
(keyup.enter)="handleEnterKey($event)"
[placeholder]="i18n.search" [autofocus]="autoFocus"
[(ngModel)]="query" (ngModelChange)="handleChange()"
/>
Expand All @@ -33,6 +34,7 @@ export class SearchComponent implements AfterViewInit {
@Input() custom: any[] = [];
@Input() emojisToShowFilter?: (x: any) => boolean;
@Output() search = new EventEmitter<any>();
@Output() enterKey = new EventEmitter<any>();
@ViewChild('inputRef') private inputRef?: ElementRef;
query = '';

Expand All @@ -43,6 +45,10 @@ export class SearchComponent implements AfterViewInit {
this.inputRef.nativeElement.focus();
}
}
handleEnterKey($event: Event) {
this.enterKey.emit($event);
$event.preventDefault();
}
handleChange() {
this.search.emit(
this.emojiSearch.search(
Expand Down

0 comments on commit 5cf3af0

Please sign in to comment.