├── src ├── assets │ └── .gitkeep ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ └── app.component.spec.ts ├── favicon.ico ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── styles.css ├── tsconfig.app.json ├── tsconfig.spec.json ├── index.html ├── tslint.json ├── main.ts ├── test.ts ├── karma.conf.js └── polyfills.ts ├── renovate.json ├── projects └── ngx-emoji-picker │ ├── src │ ├── lib │ │ ├── misc │ │ │ ├── picker-directions.ts │ │ │ ├── emoji-event.ts │ │ │ ├── caret-event.ts │ │ │ └── emojis.data.ts │ │ ├── styles │ │ │ ├── emoji-categories.scss │ │ │ ├── emoji-header.scss │ │ │ ├── emoji-list.scss │ │ │ ├── emoji-category.scss │ │ │ ├── emoji-footer.scss │ │ │ ├── emoji-search.scss │ │ │ ├── emoji-content.scss │ │ │ ├── emoji-button.scss │ │ │ └── _constants.scss │ │ ├── components │ │ │ ├── emoji-header.component.d.ts │ │ │ ├── emoji-footer.component.ts │ │ │ ├── emoji-category.component.ts │ │ │ ├── emoji-header.component.ts │ │ │ ├── emoji-button.component.ts │ │ │ ├── emoji-categories.component.ts │ │ │ ├── emoji-search.component.ts │ │ │ ├── emoji-list.component.ts │ │ │ ├── emoji-content.component.ts │ │ │ └── emoji-picker.component.ts │ │ ├── pipes │ │ │ ├── emoji-empty-category.pipe.ts │ │ │ └── emoji-fallback.pipe.ts │ │ ├── ngx-emoji-picker.module.ts │ │ └── directives │ │ │ ├── emoji-picker-caret.directive.ts │ │ │ └── emoji-picker-api.directive.ts │ ├── test.ts │ └── public_api.ts │ ├── tsconfig.lib.prod.json │ ├── ng-package.json │ ├── tslint.json │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ ├── package.json │ ├── karma.conf.js │ ├── package-lock.json │ └── README.md ├── e2e ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts ├── tsconfig.e2e.json └── protractor.conf.js ├── .editorconfig ├── browserslist ├── .github └── FUNDING.yml ├── tsconfig.json ├── .gitignore ├── CONTRIBUTING.md ├── .all-contributorsrc ├── package.json ├── tslint.json ├── README.md └── angular.json /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahkohd/ngx-emoji-picker/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/misc/picker-directions.ts: -------------------------------------------------------------------------------- 1 | export enum DIRECTIONS { 2 | top, 3 | bottom, 4 | left, 5 | right 6 | } 7 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "angularCompilerOptions": { 4 | "enableIvy": false 5 | } 6 | } -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-categories.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | :host { 3 | display: flex; 4 | flex-wrap: wrap; 5 | justify-content: space-between; 6 | margin: 0 0 10px; 7 | } 8 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | html,body { 3 | height: 100%; 4 | width: 100%; 5 | } 6 | 7 | body { 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | } -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-emoji-picker", 4 | "lib": { 5 | "entryFile": "src/public_api.ts", 6 | "umdModuleIds": { 7 | "twemoji": "twemoji" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-header.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | :host { 3 | display: block; 4 | border-bottom: 1px solid $ep-border-header; 5 | border-radius: $ep-border-radius $ep-border-radius 0 0; 6 | padding: $ep-padding-results; 7 | background: $ep-background-header; 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/misc/emoji-event.ts: -------------------------------------------------------------------------------- 1 | export class EmojiEvent { 2 | char: string; 3 | label: string; 4 | 5 | constructor(data) { 6 | Object.assign(this, data); 7 | } 8 | 9 | static fromArray(emojiArray) { 10 | return new EmojiEvent({ char : emojiArray[0], label : emojiArray[1]}) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-header.component.d.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from '@angular/core'; 2 | export declare class EmojiHeaderComponent { 3 | emojisCategories: any; 4 | displaySearchBar: any; 5 | categorySelection: EventEmitter; 6 | searchEmitter: EventEmitter; 7 | constructor(); 8 | } 9 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{message}} 3 |
4 | 5 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-list.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | :host { 3 | overflow-y: auto; 4 | } 5 | 6 | .emoji-list { 7 | padding: 0 $ep-padding-results $ep-padding-results; 8 | } 9 | 10 | .emoji-buttons { 11 | display: flex; 12 | justify-content: center; 13 | flex-wrap: wrap; 14 | margin: $ep-margin/2 0; 15 | } 16 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-category.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | .emoji-category { 3 | margin: 0; 4 | font-size: $ep-font-size; 5 | padding: $ep-padding/2 0 $ep-padding/2 $ep-padding/2; 6 | border-bottom: $ep-border-size solid $ep-border-input; 7 | color: $ep-category-title; 8 | font-family: $ep-font-family; 9 | } 10 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'emoji-footer', 5 | styleUrls: ['../styles/emoji-footer.scss'], 6 | template: ` 7 |
8 | ` 9 | }) 10 | 11 | export class EmojiFooterComponent { 12 | constructor() { } 13 | } 14 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxEmojiMart 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getTitleText()).toEqual('Welcome to ngx-emoji-mart!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/pipes/emoji-empty-category.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'notEmptyEmojiCategory' 5 | }) 6 | 7 | export class EmojiEmptyCategoryPipe implements PipeTransform { 8 | transform(categories: any[]): any[] { 9 | return categories.filter(category => category.emojis.length !== 0); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-footer.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | .emoji-footer { 3 | display: flex; 4 | align-items: center; 5 | justify-content: space-between; 6 | border-top: $ep-border-size solid $ep-border-header; 7 | border-radius: 0 0 $ep-border-radius $ep-border-radius; 8 | padding: $ep-padding-results; 9 | background: $ep-background-header; 10 | } 11 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-search.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | input { 3 | width: 100%; 4 | padding: $ep-padding/2 $ep-padding; 5 | border: $ep-border-size solid $ep-border-input; 6 | outline: none; 7 | font-size: $ep-input-font-size; 8 | font-weight: inherit; 9 | box-sizing: border-box; 10 | 11 | &:focus { 12 | border-color: darken($ep-border-input, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-root", 5 | templateUrl: "./app.component.html", 6 | styleUrls: ["./app.component.css"], 7 | }) 8 | export class AppComponent { 9 | title = "ngx-emoji-mart"; 10 | toggled: boolean; 11 | message: String = ""; 12 | constructor() { 13 | this.toggled = false; 14 | } 15 | handleSelection(event) { 16 | this.message = this.message + "" + event.char; 17 | this.toggled = false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { NgxEmojiPickerModule } from '../../projects/ngx-emoji-picker/src/public_api'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | NgxEmojiPickerModule.forRoot() 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-content.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | :host { 3 | display: flex; 4 | flex-direction: column; 5 | width: 100vw; 6 | height: $ep-height; 7 | border-radius: $ep-border-radius; 8 | background: $ep-background-panel; 9 | text-align: left; 10 | box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14), 0 9px 46px 8px rgba(0,0,0,.12); 11 | 12 | @media(min-width: $ep-width) { 13 | width: $ep-width; 14 | } 15 | } 16 | 17 | emoji-list { 18 | flex-grow: 1; 19 | } 20 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-category.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ElementRef } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'emoji-category', 5 | styleUrls: ['../styles/emoji-category.scss'], 6 | template: ` 7 |

{{category.name}}

8 | ` 9 | }) 10 | 11 | export class EmojiCategoryComponent { 12 | @Input('category') category; 13 | 14 | constructor(private _element: ElementRef) { } 15 | 16 | public scrollIntoView() { 17 | this._element.nativeElement.scrollIntoView(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/emoji-button.scss: -------------------------------------------------------------------------------- 1 | @import '_constants.scss'; 2 | :host { 3 | display: inline-block; 4 | } 5 | 6 | .emoji-button { 7 | padding: 0; 8 | border: none; 9 | outline: none; 10 | background: none; 11 | cursor: pointer; 12 | width: $ep-emoji-width; 13 | height: $ep-emoji-width; 14 | border-radius: $ep-border-radius; 15 | font-size: ($ep-emoji-width - 10px); 16 | line-height: 1.1; 17 | cursor: pointer; 18 | transition: all 0.2s; 19 | 20 | &:hover, &:focus { 21 | background: #F1F1F1; 22 | border-color: #F1F1F1; 23 | } 24 | 25 | ::ng-deep img { 26 | width: 20px; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: ahkohd 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Output, Input } from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'emoji-header', 5 | styleUrls: ['../styles/emoji-header.scss'], 6 | template: ` 7 | 8 | 9 | ` 10 | }) 11 | export class EmojiHeaderComponent { 12 | @Input('emojisCategories') emojisCategories 13 | 14 | @Output('categorySelection') categorySelection = new EventEmitter() 15 | @Output('search') searchEmitter = new EventEmitter() 16 | 17 | constructor() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "downlevelIteration": true, 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "target": "es2015", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ], 22 | "paths": { 23 | "ngx-emoji-picker": [ 24 | "dist/ngx-emoji-picker" 25 | ], 26 | "ngx-emoji-picker/*": [ 27 | "dist/ngx-emoji-picker/*" 28 | ] 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | 31 | # misc 32 | /.sass-cache 33 | /connect.lock 34 | /coverage 35 | /libpeerconnection.log 36 | npm-debug.log 37 | yarn-error.log 38 | testem.log 39 | /typings 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, EventEmitter, Output } from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'emoji-button', 5 | styleUrls: ['../styles/emoji-button.scss'], 6 | template: ` 7 | 11 | ` 12 | }) 13 | export class EmojiButtonComponent { 14 | @Input('emoji') emoji 15 | @Input('dataToEmit') dataToEmit 16 | @Input('options') options 17 | @Input('fitzpatrick') fitzpatrick 18 | 19 | @Output('selection') selectionEmitter: EventEmitter = new EventEmitter() 20 | 21 | constructor() {} 22 | 23 | ngOnChanges() {} 24 | } 25 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-categories.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, EventEmitter, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'emoji-categories', 5 | styleUrls: ['../styles/emoji-categories.scss'], 6 | template: ` 7 | 8 | 12 | 13 | ` 14 | }) 15 | 16 | export class EmojiCategoriesComponent { 17 | @Input('emojisCategories') emojisCategories; 18 | @Output('categorySelection') categorySelection = new EventEmitter(); 19 | 20 | constructor() {} 21 | 22 | handleCategorySelection(event) { 23 | this.categorySelection.emit(event); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "skipTemplateCodegen": true, 22 | "strictMetadataEmit": true, 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true, 25 | "enableResourceInlining": true 26 | }, 27 | "exclude": [ 28 | "src/test.ts", 29 | "**/*.spec.ts" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/pipes/emoji-fallback.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import twemoji from 'twemoji'; 3 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; 4 | 5 | @Pipe({ 6 | name: 'emojiFallback' 7 | }) 8 | export class EmojiFallback implements PipeTransform { 9 | constructor(private domSanitizer: DomSanitizer) { 10 | 11 | } 12 | 13 | transform(emoji: string): string|SafeHtml { 14 | return !emoji || this.supportsEmoji() ? emoji : this.domSanitizer.bypassSecurityTrustHtml(twemoji.parse(emoji.trim())); 15 | } 16 | 17 | // Solution: https://stackoverflow.com/questions/45576748/how-can-i-detect-rendering-support-for-emoji-in-javascript 18 | supportsEmoji() { 19 | var ctx = document.createElement("canvas").getContext("2d"); 20 | ctx.fillText("🙃", -2, 4); 21 | return ctx.getImageData(0, 0, 1, 1).data[3] > 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/styles/_constants.scss: -------------------------------------------------------------------------------- 1 | $ep-font-family: 'Arial', 'Arial Black', 'Tahoma', 'Trebuchet MS', 'Verdana'; 2 | 3 | // Colors 4 | $ep-background-header: #FCFCFC !default; 5 | $ep-border-header: #F9F9F9 !default; 6 | $ep-background-panel: #FFF !default; 7 | $ep-border-input: #F0F0F0 !default; 8 | $ep-category-title: #777 !default; 9 | 10 | // Padding and Borders 11 | $ep-padding: 10px; 12 | $ep-margin: 10px; 13 | $ep-padding-results: $ep-padding !default; 14 | $ep-border-radius: 3px !default; 15 | 16 | // Dimensions 17 | $ep-emoji-width: 34px !default; 18 | $ep-font-size: 16px; 19 | $ep-input-font-size: 14px; 20 | $ep-border-size: 1px; 21 | 22 | $ep-width: $ep-emoji-width * 7 + ($ep-padding-results * 2); 23 | 24 | $ep-height: 25 | $ep-emoji-width * (5 + 1) + 26 | $ep-padding * 5 + 27 | $ep-border-size * 4 + 28 | $ep-input-font-size + 3px + 29 | $ep-font-size + 3px + 30 | $ep-padding * 2; 31 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-emoji-picker", 3 | "author": { 4 | "name": "Victor Aremu", 5 | "email": "victor.olorunbunmi@gmail.com", 6 | "url": "https://github.com/ahkohd/ngx-emoji-picker" 7 | }, 8 | "version": "0.1.6", 9 | "description": "A simple emoji picker for Angular 2+ / Ionic 3 with AOT build support", 10 | "keywords": [ 11 | "emoji", 12 | "picker", 13 | "angular", 14 | "angular2", 15 | "angular7", 16 | "angular8", 17 | "angular9", 18 | "ionic3", 19 | "ionic", 20 | "ngx", 21 | "dropdown", 22 | "aot" 23 | ], 24 | "homepage": "https://github.com/ahkohd/ngx-emoji-picker", 25 | "license": "MIT", 26 | "peerDependencies": { 27 | "@angular/common": "^9.1.9", 28 | "@angular/core": "^9.1.9", 29 | "twemoji": "^13.0.1" 30 | }, 31 | "whitelistedNonPeerDependencies": [ 32 | "twemoji" 33 | ], 34 | "dependencies": {} 35 | } 36 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.1.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'ngx-emoji-mart'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('ngx-emoji-mart'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ngx-emoji-mart!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "ahkohd", 10 | "name": "Victor Aremu", 11 | "avatar_url": "https://avatars1.githubusercontent.com/u/13041443?v=4", 12 | "profile": "https://victor-aremu.web.app", 13 | "contributions": [ 14 | "maintenance", 15 | "projectManagement" 16 | ] 17 | }, 18 | { 19 | "login": "GNURub", 20 | "name": "Rubén", 21 | "avatar_url": "https://avatars3.githubusercontent.com/u/1318648?v=4", 22 | "profile": "https://github.com/GNURub", 23 | "contributions": [ 24 | "code" 25 | ] 26 | }, 27 | { 28 | "login": "cheygo", 29 | "name": "cheygo", 30 | "avatar_url": "https://avatars3.githubusercontent.com/u/69327675?v=4", 31 | "profile": "https://github.com/cheygo", 32 | "contributions": [ 33 | "a11y" 34 | ] 35 | } 36 | ], 37 | "contributorsPerLine": 7, 38 | "projectName": "ngx-emoji-picker", 39 | "projectOwner": "ahkohd", 40 | "repoType": "github", 41 | "repoHost": "https://github.com", 42 | "skipCi": true 43 | } 44 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-search.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Output, 5 | ViewChild, 6 | ElementRef, 7 | OnDestroy, 8 | } from "@angular/core"; 9 | import { Subject } from "rxjs"; 10 | import { takeUntil } from "rxjs/operators"; 11 | 12 | @Component({ 13 | selector: "emoji-search", 14 | styleUrls: ["../styles/emoji-search.scss"], 15 | template: ` 16 | 24 | `, 25 | }) 26 | export class EmojiSearchComponent implements OnDestroy { 27 | @Output("search") searchEmitter: EventEmitter = new EventEmitter(); 28 | @ViewChild("input", { static: true }) input: ElementRef; 29 | 30 | private _searchValue: Subject = new Subject(); 31 | private _destroyed = new Subject(); 32 | 33 | constructor() { 34 | this._searchValue.pipe(takeUntil(this._destroyed)).subscribe((value) => { 35 | this.searchEmitter.emit(value); 36 | }); 37 | } 38 | 39 | handleInputChange(event) { 40 | this._searchValue.next(event); 41 | } 42 | 43 | ngOnDestroy() { 44 | this._destroyed.next(true); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ViewChildren, QueryList, forwardRef, Output, EventEmitter } from '@angular/core'; 2 | import { EmojiCategoryComponent } from './emoji-category.component'; 3 | 4 | @Component({ 5 | selector: 'emoji-list', 6 | styleUrls: ['../styles/emoji-list.scss'], 7 | template: ` 8 |
9 | 10 | 11 |
12 | 16 |
17 |
18 |
19 | ` 20 | }) 21 | 22 | export class EmojiListComponent { 23 | @ViewChildren(forwardRef(() => EmojiCategoryComponent)) emojiCategoryComponents: QueryList; 24 | @Input('emojis') emojis; 25 | @Output('emoji-selection') emojiSelectionEmitter = new EventEmitter(); 26 | 27 | constructor() { } 28 | 29 | public selectCategory(event) { 30 | this.emojiCategoryComponents.forEach((categoryCmp:EmojiCategoryComponent) => { 31 | if (categoryCmp['category'].name === event.name) { 32 | categoryCmp.scrollIntoView(); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-emoji-picker 3 | */ 4 | export * from "./lib/ngx-emoji-picker.module"; 5 | export * from "./lib/misc/caret-event"; 6 | export * from "./lib/misc/emoji-event"; 7 | export * from "./lib/directives/emoji-picker-api.directive"; 8 | export * from "./lib/components/emoji-button.component"; 9 | export * from "./lib/components/emoji-content.component"; 10 | export * from "./lib/components/emoji-picker.component"; 11 | export * from "./lib/components/emoji-list.component"; 12 | export * from "./lib/components/emoji-header.component"; 13 | export * from "./lib/components/emoji-search.component"; 14 | export * from "./lib/components/emoji-categories.component"; 15 | export * from "./lib/components/emoji-category.component"; 16 | export * from "./lib/components/emoji-footer.component"; 17 | export * from "./lib/components/emoji-footer.component"; 18 | export * from "./lib/directives/emoji-picker-api.directive"; 19 | export * from "./lib/directives/emoji-picker-caret.directive"; 20 | export * from "./lib/components/emoji-button.component"; 21 | export * from "./lib/components/emoji-content.component"; 22 | export * from "./lib/components/emoji-picker.component"; 23 | export * from "./lib/components/emoji-list.component"; 24 | export * from "./lib/components/emoji-header.component"; 25 | export * from "./lib/components/emoji-search.component"; 26 | export * from "./lib/components/emoji-categories.component"; 27 | export * from "./lib/components/emoji-category.component"; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-emoji-mart", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build --prod", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "9.1.9", 15 | "@angular/common": "9.1.9", 16 | "@angular/compiler": "9.1.9", 17 | "@angular/core": "9.1.9", 18 | "@angular/forms": "9.1.9", 19 | "@angular/platform-browser": "9.1.9", 20 | "@angular/platform-browser-dynamic": "9.1.9", 21 | "@angular/router": "9.1.9", 22 | "core-js": "2.6.1", 23 | "rxjs": "6.5.5", 24 | "tslib": "1.13.0", 25 | "twemoji": "^13.0.1", 26 | "zone.js": "0.10.3" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "0.901.7", 30 | "@angular-devkit/build-ng-packagr": "0.901.7", 31 | "@angular/cli": "9.1.7", 32 | "@angular/compiler-cli": "9.1.9", 33 | "@angular/language-service": "9.1.9", 34 | "@types/node": "12.12.45", 35 | "@types/jasmine": "2.8.12", 36 | "@types/jasminewd2": "2.0.6", 37 | "codelyzer": "5.2.2", 38 | "jasmine-core": "2.99.1", 39 | "jasmine-spec-reporter": "4.2.1", 40 | "karma": "3.1.4", 41 | "karma-chrome-launcher": "2.2.0", 42 | "karma-coverage-istanbul-reporter": "2.0.4", 43 | "karma-jasmine": "1.1.2", 44 | "karma-jasmine-html-reporter": "0.2.2", 45 | "ng-packagr": "9.1.5", 46 | "protractor": "5.4.1", 47 | "ts-node": "7.0.1", 48 | "tslib": "1.9.3", 49 | "tslint": "5.11.0", 50 | "typescript": "3.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, forwardRef, Output, EventEmitter } from '@angular/core'; 2 | import { EMOJIS } from '../misc/emojis.data'; 3 | import { EmojiListComponent } from './emoji-list.component'; 4 | 5 | @Component({ 6 | selector: 'emoji-content', 7 | styleUrls: ['../styles/emoji-content.scss'], 8 | template: ` 9 | 13 | 14 | 15 | ` 16 | }) 17 | 18 | export class EmojiContentComponent { 19 | @ViewChild(forwardRef(() => EmojiListComponent), { static: true }) emojiListComponent: EmojiListComponent; 20 | @Output('emoji-selection') emojiSelectionEmitter = new EventEmitter(); 21 | 22 | private _emojis = EMOJIS; 23 | emojis = this._emojis.slice(); 24 | emojisCategories = this._emojis.map(category => Object.assign({}, category, { emojis : [] })); 25 | 26 | constructor() {} 27 | 28 | searchHandler(value) { 29 | let filteredEmojis = this.emojisCategories.map(category => Object.assign({}, category, { emojis : [] })); 30 | 31 | value = value.replace(/-/g, '').toLowerCase(); 32 | 33 | Object.keys(this._emojis).forEach(i => { 34 | const category = this._emojis[i]; 35 | 36 | category.emojis.forEach(emoji => { 37 | if (emoji[1].indexOf(value) !== -1) { 38 | filteredEmojis[i].emojis.push(emoji); 39 | } 40 | }); 41 | }); 42 | 43 | this.emojis = filteredEmojis; 44 | } 45 | 46 | categorySelectionHandler(event) { 47 | this.emojiListComponent.selectCategory(event); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/ngx-emoji-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { EmojiButtonComponent } from './components/emoji-button.component'; 5 | import { EmojiCategoriesComponent } from './components/emoji-categories.component'; 6 | import { EmojiCategoryComponent } from './components/emoji-category.component'; 7 | import { EmojiContentComponent } from './components/emoji-content.component'; 8 | import { EmojiFooterComponent } from './components/emoji-footer.component'; 9 | import { EmojiHeaderComponent } from './components/emoji-header.component'; 10 | import { EmojiListComponent } from './components/emoji-list.component'; 11 | import { EmojiSearchComponent } from './components/emoji-search.component'; 12 | 13 | import { EmojiPickerApiDirective } from './directives/emoji-picker-api.directive'; 14 | import { EmojiPickerCaretDirective } from './directives/emoji-picker-caret.directive'; 15 | import { EmojiEmptyCategoryPipe } from './pipes/emoji-empty-category.pipe'; 16 | import { EmojiPickerComponent } from './components/emoji-picker.component'; 17 | import { EmojiFallback } from './pipes/emoji-fallback.pipe'; 18 | 19 | 20 | @NgModule({ 21 | declarations: [ 22 | EmojiPickerApiDirective, 23 | EmojiPickerCaretDirective, 24 | EmojiButtonComponent, 25 | EmojiContentComponent, 26 | EmojiPickerComponent, 27 | EmojiListComponent, 28 | EmojiHeaderComponent, 29 | EmojiSearchComponent, 30 | EmojiCategoriesComponent, 31 | EmojiCategoryComponent, 32 | EmojiFooterComponent, 33 | EmojiEmptyCategoryPipe, 34 | EmojiFallback 35 | ], 36 | imports: [ 37 | CommonModule 38 | ], 39 | exports: [ 40 | EmojiPickerApiDirective, 41 | EmojiPickerCaretDirective, 42 | EmojiButtonComponent, 43 | EmojiContentComponent, 44 | EmojiPickerComponent, 45 | EmojiListComponent, 46 | EmojiHeaderComponent, 47 | EmojiSearchComponent, 48 | EmojiCategoriesComponent, 49 | EmojiCategoryComponent, 50 | EmojiFooterComponent 51 | ], 52 | providers: [], 53 | entryComponents: [EmojiPickerComponent] 54 | }) 55 | export class NgxEmojiPickerModule { 56 | static forRoot(): ModuleWithProviders { 57 | return { 58 | ngModule: NgxEmojiPickerModule, 59 | providers: [] 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-emoji-picker", 3 | "version": "0.1.6", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "fs-extra": { 8 | "version": "8.1.0", 9 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 10 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 11 | "requires": { 12 | "graceful-fs": "^4.2.0", 13 | "jsonfile": "^4.0.0", 14 | "universalify": "^0.1.0" 15 | }, 16 | "dependencies": { 17 | "jsonfile": { 18 | "version": "4.0.0", 19 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 20 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 21 | "requires": { 22 | "graceful-fs": "^4.1.6" 23 | } 24 | } 25 | } 26 | }, 27 | "graceful-fs": { 28 | "version": "4.2.4", 29 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 30 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 31 | }, 32 | "jsonfile": { 33 | "version": "5.0.0", 34 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", 35 | "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", 36 | "requires": { 37 | "graceful-fs": "^4.1.6", 38 | "universalify": "^0.1.2" 39 | } 40 | }, 41 | "twemoji": { 42 | "version": "13.0.1", 43 | "resolved": "https://registry.npmjs.org/twemoji/-/twemoji-13.0.1.tgz", 44 | "integrity": "sha512-mrTBq+XpCLM4zm76NJOjLHoQNV9mHdBt3Cba/T5lS1rxn8ArwpqE47mqTocupNlkvcLxoeZJjYSUW0DU5ZwqZg==", 45 | "requires": { 46 | "fs-extra": "^8.0.1", 47 | "jsonfile": "^5.0.0", 48 | "twemoji-parser": "13.0.0", 49 | "universalify": "^0.1.2" 50 | } 51 | }, 52 | "twemoji-parser": { 53 | "version": "13.0.0", 54 | "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-13.0.0.tgz", 55 | "integrity": "sha512-zMaGdskpH8yKjT2RSE/HwE340R4Fm+fbie4AaqjDa4H/l07YUmAvxkSfNl6awVWNRRQ0zdzLQ8SAJZuY5MgstQ==" 56 | }, 57 | "universalify": { 58 | "version": "0.1.2", 59 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 60 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/misc/caret-event.ts: -------------------------------------------------------------------------------- 1 | export class CaretEvent { 2 | caretOffset: number; 3 | caretRange: Range; 4 | textContent: string; 5 | 6 | constructor(data) { 7 | Object.assign(this, data); 8 | } 9 | 10 | clone(): CaretEvent { 11 | return new CaretEvent(Object.assign({}, this, { 12 | caretRange: this.caretRange && this.caretRange.cloneRange ? this.caretRange.cloneRange() : this.caretRange 13 | })); 14 | } 15 | 16 | static generateNullEvent() { 17 | return new CaretEvent({ 18 | caretOffset: 0, 19 | textContent: '' 20 | }); 21 | } 22 | 23 | static comparePropsOfObject(r1, r2) { 24 | for (let k in r1) { 25 | if (r1[k] !== r2[k]) { 26 | return false 27 | } 28 | } 29 | return true; 30 | } 31 | 32 | static compare(e1: CaretEvent, e2: CaretEvent): boolean { 33 | const changed = 34 | /** different when either caretRange is omitted while other exists */ 35 | (!e1.caretRange && e2.caretRange) || 36 | (e1.caretRange && !e2.caretRange) || 37 | /** different when offset has changed */ 38 | (e1.caretOffset !== e2.caretOffset) || 39 | /** different when textContent has changed */ 40 | (e1.textContent !== e2.textContent) || 41 | /** different when range object properties changed */ 42 | !this.comparePropsOfObject(e1.caretRange, e2.caretRange) 43 | ; 44 | 45 | return !changed; 46 | } 47 | 48 | static generateCaretEvent(win, doc, element: HTMLElement & HTMLInputElement): CaretEvent { 49 | let caretOffset = 0, sel, caretRange, textContent = element.textContent; 50 | 51 | if (element.tagName.toLowerCase() === 'input') { 52 | return new CaretEvent({ 53 | caretOffset: element.selectionEnd, 54 | textContent: element.value 55 | }) 56 | } 57 | 58 | if (typeof win.getSelection != "undefined") { 59 | sel = win.getSelection(); 60 | if (sel.rangeCount > 0) { 61 | const range = win.getSelection().getRangeAt(0); 62 | const preCaretRange = range.cloneRange(); 63 | preCaretRange.selectNodeContents(element); 64 | preCaretRange.setEnd(range.endContainer, range.endOffset); 65 | caretOffset = preCaretRange.toString().length; 66 | 67 | /** Keeping a reference of the range to emit */ 68 | caretRange = range.cloneRange(); 69 | } 70 | } else if ((sel = doc.selection) && sel.type != "Control") { 71 | const textRange = sel.createRange(); 72 | const preCaretTextRange = doc.body.createTextRange(); 73 | preCaretTextRange.moveToElementText(element); 74 | preCaretTextRange.setEndPoint("EndToEnd", textRange); 75 | caretOffset = preCaretTextRange.text.length; 76 | 77 | /** Keeping a reference of the range to emit and making it compatible */ 78 | caretRange = textRange.duplicate(); 79 | caretRange.insertNode = (e) => { 80 | const container = document.createElement("div"); 81 | container.appendChild(e); 82 | caretRange.pasteHTML(container.innerHTML); 83 | }; 84 | } 85 | 86 | return new CaretEvent({ 87 | caretOffset, 88 | caretRange, 89 | textContent 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/directives/emoji-picker-caret.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Output, EventEmitter, ElementRef, OnInit, OnDestroy} from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { takeUntil, distinctUntilChanged } from 'rxjs/operators'; 4 | 5 | 6 | 7 | 8 | import { CaretEvent } from '../misc/caret-event'; 9 | 10 | @Directive({ 11 | selector: '[emojiPickerCaretEmitter]', 12 | host: { 13 | '(keyup)': 'updateCaretPosition()', 14 | '(mouseup)': 'updateCaretPosition()', 15 | '(selectstart)': 'updateCaretPosition()', 16 | '(focus)': 'updateCaretPosition()', 17 | '(DOMSubtreeModified)': 'updateCaretDueMutation($event)' 18 | } 19 | }) 20 | export class EmojiPickerCaretDirective implements OnInit, OnDestroy { 21 | @Output('emojiPickerCaretEmitter') caretEmitter = new EventEmitter(); 22 | 23 | private _caretEvent$ = new Subject(); 24 | private _destroyed$ = new Subject(); 25 | 26 | private _lastCaretEvent: CaretEvent = CaretEvent.generateNullEvent(); 27 | 28 | private _win; 29 | private _doc; 30 | 31 | get doc() { 32 | if (!this._doc) { 33 | this._doc = this._el.nativeElement.ownerDocument || this._el.nativeElement.document || document; 34 | } 35 | 36 | return this._doc; 37 | } 38 | 39 | get win() { 40 | if (!this._win) { 41 | this._win = this.doc.defaultView || this.doc.parentWindow || window 42 | } 43 | 44 | return this._win 45 | } 46 | 47 | constructor( 48 | private _el: ElementRef 49 | ) { 50 | this._caretEvent$ 51 | .pipe(takeUntil(this._destroyed$), distinctUntilChanged((event1, event2) => { 52 | return CaretEvent.compare(event1, event2); 53 | })) 54 | .subscribe((event: CaretEvent) => { 55 | this.caretEmitter.emit(event); 56 | this._lastCaretEvent = event.clone() 57 | }) 58 | ; 59 | } 60 | 61 | ngOnInit() { 62 | if (!this._el.nativeElement.getAttribute('contenteditable') && this._el.nativeElement.tagName.toLowerCase() !== 'input') { 63 | throw new Error('(emojiPickerPositionEmitter) should only work on contenteditable enabled or input elements'); 64 | } 65 | } 66 | 67 | ngOnDestroy() { 68 | this._destroyed$.next(true); 69 | } 70 | 71 | updateCaretPosition() { 72 | const cEvent = CaretEvent.generateCaretEvent(this.win, this.doc, this._el.nativeElement); 73 | this._caretEvent$.next(cEvent); 74 | } 75 | 76 | updateCaretDueMutation() { 77 | const cEvent = CaretEvent.generateCaretEvent(this.win, this.doc, this._el.nativeElement); 78 | let textMovement = cEvent.textContent.length - this._lastCaretEvent.textContent.length; 79 | cEvent.caretOffset = this._lastCaretEvent.caretOffset + textMovement; 80 | 81 | /** change detection after DOMSubtreeModified event is weird 82 | * ChangeDetectorRef.detectChanges(), ChangeDetectorRef.markForCheck(), ApplicationRef.tick(), NgZone.run() 83 | * all of those methods did not work as expected. 84 | * As a temporary hack I am emitting an event after a short timeout, which is fine due to the _caretEvent$ smart stream 85 | */ 86 | 87 | setTimeout(() => { 88 | this._caretEvent$.next(cEvent); 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "no-inputs-metadata-property": true, 122 | "no-outputs-metadata-property": true, 123 | "no-host-metadata-property": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-lifecycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/directives/emoji-picker-api.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | Input, 4 | ComponentFactoryResolver, 5 | ViewContainerRef, 6 | ComponentFactory, 7 | ComponentRef, 8 | ElementRef, 9 | EventEmitter, 10 | Output, 11 | OnInit 12 | } from '@angular/core'; 13 | import { Subject , Subscription } from 'rxjs'; 14 | import { takeUntil, distinctUntilChanged} from 'rxjs/operators'; 15 | 16 | 17 | import { EmojiPickerComponent } from '../components/emoji-picker.component'; 18 | import { DIRECTIONS } from '../misc/picker-directions'; 19 | import { EmojiEvent } from '../misc/emoji-event'; 20 | 21 | @Directive({ 22 | selector: '[emojiPickerIf]', 23 | host: { 24 | '(click)': '$event.emojiPickerExempt = true' // marking off event listening on anchor 25 | } 26 | }) 27 | export class EmojiPickerApiDirective { 28 | private _directionCode: DIRECTIONS = DIRECTIONS.bottom; 29 | private _searchBar: Boolean = false; 30 | 31 | @Input('emojiPickerDirection') set emojiPickerDirection(direction: string) { 32 | if (DIRECTIONS[direction] === undefined) { 33 | console.error(`Emoji-Picker: direction '${direction}' passed as input does not exist in DIRECTIONS table, defaulting to 'bottom'`); 34 | this._directionCode = DIRECTIONS.bottom; 35 | } else { 36 | this._directionCode = DIRECTIONS[direction]; 37 | } 38 | } 39 | 40 | 41 | @Input('emojiPickerIf') set emojiPickerIf(condition: boolean) { 42 | this._emojiPickerOpenState.next(condition); 43 | } 44 | @Output('emojiPickerIfChange') emojiPickerIfEmitter = new EventEmitter(); 45 | 46 | @Output('emojiPickerSelect') selectEmitter = new EventEmitter(); 47 | 48 | private _emojiPickerOpenState = new Subject(); 49 | private _destroyed = new Subject(); 50 | 51 | private _emojiPickerFactory: ComponentFactory; 52 | private _emojiPickerRef: ComponentRef; 53 | private _emojiSubs: Subscription[] = []; 54 | 55 | constructor( 56 | private _cfr: ComponentFactoryResolver, 57 | private _vcr: ViewContainerRef, 58 | private _el: ElementRef 59 | ) { 60 | this.initPicker(); 61 | 62 | this._emojiPickerOpenState 63 | .pipe(takeUntil(this._destroyed), 64 | distinctUntilChanged() 65 | ) 66 | .subscribe(value => { 67 | 68 | if (value) { 69 | this._emojiPickerRef?.instance?.show?.(); 70 | } else { 71 | this._emojiPickerRef?.instance?.hide?.(); 72 | } 73 | }); 74 | } 75 | 76 | initPicker() { 77 | this._emojiPickerFactory = this._emojiPickerFactory || this._cfr.resolveComponentFactory(EmojiPickerComponent); 78 | this._emojiPickerRef = this._emojiPickerRef || this._vcr.createComponent(this._emojiPickerFactory); 79 | 80 | this._emojiPickerRef.instance.setPosition(this._el, this._directionCode); 81 | this._emojiSubs.push( 82 | this._emojiPickerRef.instance.pickerCloseEmitter.subscribe(event => this.emojiPickerIfEmitter.emit(false)), 83 | this._emojiPickerRef.instance.selectionEmitter.subscribe(event => this.selectEmitter.emit(EmojiEvent.fromArray(event))) 84 | ); 85 | } 86 | 87 | closePicker() { 88 | if (!this._emojiPickerRef || !this._emojiPickerRef.destroy) { 89 | return; 90 | } 91 | 92 | this._emojiSubs.forEach((subscription: Subscription) => subscription.unsubscribe()); 93 | this._emojiPickerRef.destroy(); 94 | 95 | this._emojiSubs = []; 96 | delete this._emojiPickerRef; 97 | } 98 | 99 | ngOnDestroy() { 100 | this._destroyed.next(true); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/components/emoji-picker.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Output, 5 | ElementRef, 6 | Renderer2, 7 | } from "@angular/core"; 8 | import { DIRECTIONS } from "../misc/picker-directions"; 9 | import { Subject } from "rxjs"; 10 | import { takeUntil, debounceTime } from "rxjs/operators"; 11 | 12 | @Component({ 13 | selector: "emoji-picker", 14 | styles: [":host { position: absolute; z-index: 9999; display: none; }"], 15 | template: ` 16 | 19 | `, 20 | host: { 21 | "(document:click)": "onBackground($event)", 22 | "(click)": "_lastHostMousedownEvent = $event", 23 | "(window:resize)": "_windowResize.next($event)", 24 | }, 25 | }) 26 | export class EmojiPickerComponent { 27 | @Output("emoji-select") selectionEmitter = new EventEmitter(); 28 | @Output("picker-close") pickerCloseEmitter = new EventEmitter(); 29 | 30 | public _lastHostMousedownEvent; 31 | public _currentTarget: ElementRef; 32 | public _currentDirection: DIRECTIONS; 33 | 34 | public _windowResize = new Subject(); 35 | public _destroyed = new Subject(); 36 | 37 | constructor(private _renderer: Renderer2, private _el: ElementRef) { 38 | this._windowResize 39 | .pipe(takeUntil(this._destroyed), debounceTime(100)) 40 | .subscribe((event) => { 41 | this.setPosition(this._currentTarget, this._currentDirection); 42 | }); 43 | } 44 | 45 | hide() { 46 | this._renderer.setStyle(this._el.nativeElement, "display", "none"); 47 | } 48 | 49 | show() { 50 | this._renderer.setStyle(this._el.nativeElement, "display", "block"); 51 | this.setPosition(this._currentTarget, this._currentDirection); 52 | } 53 | 54 | setPosition( 55 | target: ElementRef, 56 | directionCode: DIRECTIONS = DIRECTIONS.bottom 57 | ) { 58 | if (!target) { 59 | return console.error( 60 | "Emoji-Picker: positioning failed due to missing target element or direction code" 61 | ); 62 | } 63 | 64 | this._renderer.setStyle(this._el.nativeElement, "transform", ""); 65 | 66 | /** Store anchor and direction */ 67 | this._currentTarget = target; 68 | this._currentDirection = directionCode; 69 | 70 | const targetBorders = target.nativeElement.getBoundingClientRect(), 71 | thisBorders = this._el.nativeElement.getBoundingClientRect(); 72 | 73 | let heightCorrection = 0, 74 | widthCorrection = 0; 75 | 76 | /** Setting up centering of picker for all cases */ 77 | switch (this._currentDirection) { 78 | case DIRECTIONS.top: 79 | case DIRECTIONS.bottom: 80 | widthCorrection = 81 | targetBorders.left - 82 | thisBorders.left + 83 | (targetBorders.width - thisBorders.width) / 2; 84 | break; 85 | case DIRECTIONS.left: 86 | case DIRECTIONS.right: 87 | heightCorrection = 88 | targetBorders.top - 89 | thisBorders.top + 90 | (targetBorders.height - thisBorders.height) / 2; 91 | break; 92 | } 93 | 94 | /** Setting up relative positioning for all cases */ 95 | switch (this._currentDirection) { 96 | case DIRECTIONS.top: 97 | heightCorrection = targetBorders.top - thisBorders.bottom; 98 | break; 99 | case DIRECTIONS.left: 100 | widthCorrection = targetBorders.left - thisBorders.right; 101 | break; 102 | case DIRECTIONS.right: 103 | widthCorrection = targetBorders.right - thisBorders.left; 104 | break; 105 | case DIRECTIONS.bottom: 106 | heightCorrection = targetBorders.bottom - thisBorders.top; 107 | break; 108 | } 109 | 110 | /** Correcting positioning due to overflow problems */ 111 | if (thisBorders.bottom + heightCorrection > window.innerHeight) { 112 | heightCorrection += 113 | window.innerHeight - (thisBorders.bottom + heightCorrection); 114 | } 115 | 116 | if (thisBorders.top + heightCorrection < 0) { 117 | heightCorrection -= thisBorders.top + heightCorrection; 118 | } 119 | 120 | if (thisBorders.right + widthCorrection > window.innerWidth) { 121 | widthCorrection += 122 | window.innerWidth - (thisBorders.right + widthCorrection); 123 | } 124 | 125 | if (thisBorders.left + widthCorrection < 0) { 126 | widthCorrection -= thisBorders.left + widthCorrection; 127 | } 128 | 129 | /** set the position adjustments to the emoji picker element */ 130 | this._renderer.setStyle( 131 | this._el.nativeElement, 132 | "transform", 133 | `translate(${widthCorrection}px,${heightCorrection}px)` 134 | ); 135 | } 136 | 137 | onBackground(event) { 138 | /** internal mousedowns are ignored */ 139 | if (event === this._lastHostMousedownEvent || event.emojiPickerExempt) { 140 | return; 141 | } 142 | 143 | this.pickerCloseEmitter.emit(event); 144 | } 145 | 146 | ngOnDestroy() { 147 | this._destroyed.next(true); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ngx-emoji-picker.svg)](https://badge.fury.io/js/ngx-emoji-picker) 2 | 3 | 4 | 5 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) 6 | 7 | 8 | 9 | # 😎 Ngx-emoji-picker 10 | 11 | ## 🆕 What's New 12 | 13 | - 🐣 Fallback emojis using twemoji. 14 | - 🛠 Fixed the [AOT (Ahead Of Time) Compile error](https://github.com/danielehrhardt/ionic3-emoji-picker/issues/8) or simply put the **---prod** build compile error. 15 | - ⏫ Upgraded the library from a **Ng2** project to a **Ng9** project. 16 | - ⏫ Upgraded the library **RxJs** from **v5** to **v6** to make it support new angular versions. 17 | - ✅ Now compatible with **Ng2** - **Ng9+** projects and as well as **Ionic 3/4**. 18 | 19 | _A fork of [ionic3-emoji-picker](https://github.com/danielehrhardt/ionic3-emoji-picker) project created by [danielehrhardt](https://github.com/danielehrhardt)_ 20 | 21 | # Installation 22 | 23 | ## Install the module via NPM 24 | 25 | ```shell 26 | # Angular 8 downwards... 27 | npm i ngx-emoji-picker@0.0.2 28 | 29 | # Angular 9 upwards... 30 | npm i ngx-emoji-picker twemoji 31 | ``` 32 | 33 | ## Import it in your app's module(s) 34 | 35 | Import `EmojiPickerModule.forRoot()` in your app's main module 36 | 37 | app.module.ts 38 | 39 | ```ts 40 | import { NgxEmojiPickerModule } from 'ngx-emoji-picker'; 41 | 42 | @NgModule({ 43 | ... 44 | imports: [ 45 | ... 46 | NgxEmojiPickerModule.forRoot() 47 | ], 48 | ... 49 | }) 50 | export class AppModule {} 51 | ``` 52 | 53 | If your app uses lazy loading, you need to import `EmojiPickerModule` in your shared module or child modules: 54 | 55 | ```ts 56 | import { NgxEmojiPickerModule } from 'ngx-emoji-picker'; 57 | 58 | @NgModule({ 59 | ... 60 | imports: [ 61 | ... 62 | NgxEmojiPickerModule 63 | ], 64 | ... 65 | }) 66 | export class SharedModule {} 67 | ``` 68 | 69 | ## Sample 70 | 71 | ### Angular Example 72 | 73 | ```html 74 | 😄 81 | ``` 82 | 83 | ```ts 84 | toggled: boolean = false; 85 | handleSelection(event) { 86 | console.log(event.char); 87 | } 88 | ``` 89 | 90 | ### Ionic 3 Example 91 | 92 | ```html 93 | 94 | 95 | 106 | ``` 107 | 108 | ```ts 109 | toggled: boolean = false; 110 | message: string; 111 | 112 | handleSelection(event) { 113 | this.message += event.char; 114 | } 115 | ``` 116 | 117 | ### Directive API: 118 | 119 | ```html 120 | 129 | ``` 130 | 131 | ### Emitter `(emojiPickerSelect)="handleSelection($event)"` 132 | 133 | ``` 134 | $event = EmojiEvent{ char : "😌", label : "relieved" } 135 | ``` 136 | 137 | ## EmojiPickerCaretEmitter 138 | 139 | added for your convenience, emits information regarding a content editable enabled element 140 | 141 | ### Emitter `(emojiPickerCaretEmitter)="handleCaretChange($event)"` 142 | 143 | ``` 144 | $event = CaretEvent{ caretOffset: 13, caretRange: Range{...}, textContent: 'content of div or input' } 145 | ``` 146 | 147 | Emoji Picker will get placed relative the element chosen via the directive api, centered and within window borders 148 | 149 | # Related 150 | 151 | - [ngx-emoj](https://github.com/ahkohd/ngx-emoj) - 💅 A simple, theme-able emoji mart/picker for angular 4+ 152 | 153 | # Contributing 154 | 155 | See [CONTRIBUTING.md](./CONTRIBUTING.md) 156 | 157 | ## Contributors ✨ 158 | 159 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |

Victor Aremu

🚧 📆

Rubén

💻

cheygo

️️️️♿️
171 | 172 | 173 | 174 | 175 | 176 | 177 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 178 | -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ngx-emoji-picker.svg)](https://badge.fury.io/js/ngx-emoji-picker) 2 | 3 | 4 | 5 | [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) 6 | 7 | 8 | 9 | # 😎 Ngx-emoji-picker 10 | 11 | ## 🆕 What's New 12 | 13 | - 🐣 Fallback emojis using twemoji. 14 | - 🛠 Fixed the [AOT (Ahead Of Time) Compile error](https://github.com/danielehrhardt/ionic3-emoji-picker/issues/8) or simply put the **---prod** build compile error. 15 | - ⏫ Upgraded the library from a **Ng2** project to a **Ng9** project. 16 | - ⏫ Upgraded the library **RxJs** from **v5** to **v6** to make it support new angular versions. 17 | - ✅ Now compatible with **Ng2** - **Ng9+** projects and as well as **Ionic 3/4**. 18 | 19 | _A fork of [ionic3-emoji-picker](https://github.com/danielehrhardt/ionic3-emoji-picker) project created by [danielehrhardt](https://github.com/danielehrhardt)_ 20 | 21 | # Installation 22 | 23 | ## Install the module via NPM 24 | 25 | ```shell 26 | # Angular 8 downwards... 27 | npm i ngx-emoji-picker@0.0.2 28 | 29 | # Angular 9 upwards... 30 | npm i ngx-emoji-picker twemoji 31 | ``` 32 | 33 | ## Import it in your app's module(s) 34 | 35 | Import `EmojiPickerModule.forRoot()` in your app's main module 36 | 37 | app.module.ts 38 | 39 | ```ts 40 | import { NgxEmojiPickerModule } from 'ngx-emoji-picker'; 41 | 42 | @NgModule({ 43 | ... 44 | imports: [ 45 | ... 46 | NgxEmojiPickerModule.forRoot() 47 | ], 48 | ... 49 | }) 50 | export class AppModule {} 51 | ``` 52 | 53 | If your app uses lazy loading, you need to import `EmojiPickerModule` in your shared module or child modules: 54 | 55 | ```ts 56 | import { NgxEmojiPickerModule } from 'ngx-emoji-picker'; 57 | 58 | @NgModule({ 59 | ... 60 | imports: [ 61 | ... 62 | NgxEmojiPickerModule 63 | ], 64 | ... 65 | }) 66 | export class SharedModule {} 67 | ``` 68 | 69 | ## Sample 70 | 71 | ### Angular Example 72 | 73 | ```html 74 | 😄 81 | ``` 82 | 83 | ```ts 84 | toggled: boolean = false; 85 | handleSelection(event) { 86 | console.log(event.char); 87 | } 88 | ``` 89 | 90 | ### Ionic 3 Example 91 | 92 | ```html 93 | 94 | 95 | 106 | ``` 107 | 108 | ```ts 109 | toggled: boolean = false; 110 | message: string; 111 | 112 | handleSelection(event) { 113 | this.message += event.char; 114 | } 115 | ``` 116 | 117 | ### Directive API: 118 | 119 | ```html 120 | 129 | ``` 130 | 131 | ### Emitter `(emojiPickerSelect)="handleSelection($event)"` 132 | 133 | ``` 134 | $event = EmojiEvent{ char : "😌", label : "relieved" } 135 | ``` 136 | 137 | ## EmojiPickerCaretEmitter 138 | 139 | added for your convenience, emits information regarding a content editable enabled element 140 | 141 | ### Emitter `(emojiPickerCaretEmitter)="handleCaretChange($event)"` 142 | 143 | ``` 144 | $event = CaretEvent{ caretOffset: 13, caretRange: Range{...}, textContent: 'content of div or input' } 145 | ``` 146 | 147 | Emoji Picker will get placed relative the element chosen via the directive api, centered and within window borders 148 | 149 | # Related 150 | 151 | - [ngx-emoj](https://github.com/ahkohd/ngx-emoj) - 💅 A simple, theme-able emoji mart/picker for angular 4+ 152 | 153 | # Contributing 154 | 155 | See [CONTRIBUTING.md](./CONTRIBUTING.md) 156 | 157 | ## Contributors ✨ 158 | 159 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |

Victor Aremu

🚧 📆

Rubén

💻

cheygo

️️️️♿️
171 | 172 | 173 | 174 | 175 | 176 | 177 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 178 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-emoji-mart": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "aot": true, 17 | "outputPath": "dist/ngx-emoji-mart", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "polyfills": "src/polyfills.ts", 21 | "tsConfig": "src/tsconfig.app.json", 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | }, 54 | { 55 | "type": "anyComponentStyle", 56 | "maximumWarning": "6kb" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "options": { 65 | "browserTarget": "ngx-emoji-mart:build" 66 | }, 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "ngx-emoji-mart:build:production" 70 | } 71 | } 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "ngx-emoji-mart:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "main": "src/test.ts", 83 | "polyfills": "src/polyfills.ts", 84 | "tsConfig": "src/tsconfig.spec.json", 85 | "karmaConfig": "src/karma.conf.js", 86 | "styles": [ 87 | "src/styles.css" 88 | ], 89 | "scripts": [], 90 | "assets": [ 91 | "src/favicon.ico", 92 | "src/assets" 93 | ] 94 | } 95 | }, 96 | "lint": { 97 | "builder": "@angular-devkit/build-angular:tslint", 98 | "options": { 99 | "tsConfig": [ 100 | "src/tsconfig.app.json", 101 | "src/tsconfig.spec.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | } 108 | } 109 | }, 110 | "ngx-emoji-mart-e2e": { 111 | "root": "e2e/", 112 | "projectType": "application", 113 | "prefix": "", 114 | "architect": { 115 | "e2e": { 116 | "builder": "@angular-devkit/build-angular:protractor", 117 | "options": { 118 | "protractorConfig": "e2e/protractor.conf.js", 119 | "devServerTarget": "ngx-emoji-mart:serve" 120 | }, 121 | "configurations": { 122 | "production": { 123 | "devServerTarget": "ngx-emoji-mart:serve:production" 124 | } 125 | } 126 | }, 127 | "lint": { 128 | "builder": "@angular-devkit/build-angular:tslint", 129 | "options": { 130 | "tsConfig": "e2e/tsconfig.e2e.json", 131 | "exclude": [ 132 | "**/node_modules/**" 133 | ] 134 | } 135 | } 136 | } 137 | }, 138 | "ngx-emoji-picker": { 139 | "root": "projects/ngx-emoji-picker", 140 | "sourceRoot": "projects/ngx-emoji-picker/src", 141 | "projectType": "library", 142 | "prefix": "lib", 143 | "architect": { 144 | "build": { 145 | "builder": "@angular-devkit/build-ng-packagr:build", 146 | "options": { 147 | "tsConfig": "projects/ngx-emoji-picker/tsconfig.lib.json", 148 | "project": "projects/ngx-emoji-picker/ng-package.json" 149 | }, 150 | "configurations": { 151 | "production": { 152 | "tsConfig": "projects/ngx-emoji-picker/tsconfig.lib.prod.json" 153 | } 154 | } 155 | }, 156 | "test": { 157 | "builder": "@angular-devkit/build-angular:karma", 158 | "options": { 159 | "main": "projects/ngx-emoji-picker/src/test.ts", 160 | "tsConfig": "projects/ngx-emoji-picker/tsconfig.spec.json", 161 | "karmaConfig": "projects/ngx-emoji-picker/karma.conf.js" 162 | } 163 | }, 164 | "lint": { 165 | "builder": "@angular-devkit/build-angular:tslint", 166 | "options": { 167 | "tsConfig": [ 168 | "projects/ngx-emoji-picker/tsconfig.lib.json", 169 | "projects/ngx-emoji-picker/tsconfig.spec.json" 170 | ], 171 | "exclude": [ 172 | "**/node_modules/**" 173 | ] 174 | } 175 | } 176 | } 177 | } 178 | }, 179 | "defaultProject": "ngx-emoji-picker", 180 | "cli": { 181 | "analytics": "b8c12113-e22f-40b8-8447-17ed27592e12" 182 | } 183 | } -------------------------------------------------------------------------------- /projects/ngx-emoji-picker/src/lib/misc/emojis.data.ts: -------------------------------------------------------------------------------- 1 | export const EMOJIS = [ 2 | { 3 | emojis: [ 4 | ['😀', 'grinning'], 5 | ['😃', 'smiley'], 6 | ['😄', 'smile'], 7 | ['😁', 'grin'], 8 | ['😆', 'laughing'], 9 | ['😅', 'sweat_smile'], 10 | ['😂', 'joy'], 11 | ['🤣', 'rofl'], 12 | ['😊', 'blush'], 13 | ['😇', 'innocent'], 14 | ['🙂', 'slightly_smiling_face'], 15 | ['🙃', 'upside_down_face'], 16 | ['😉', 'wink'], 17 | ['😌', 'relieved'], 18 | ['😍', 'heart_eyes'], 19 | ['😘', 'kissing_heart'], 20 | ['😗', 'kissing'], 21 | ['😙', 'kissing_smiling_eyes'], 22 | ['😚', 'kissing_closed_eyes'], 23 | ['😋', 'yum'], 24 | ['😜', 'stuck_out_tongue_winking_eye'], 25 | ['😝', 'stuck_out_tongue_closed_eyes'], 26 | ['😛', 'stuck_out_tongue'], 27 | ['🤑', 'money_mouth_face'], 28 | ['🤗', 'hugs'], 29 | ['🤓', 'nerd_face'], 30 | ['😎', 'sunglasses'], 31 | ['🤡', 'clown_face'], 32 | ['🤠', 'cowboy_hat_face'], 33 | ['😏', 'smirk'], 34 | ['😒', 'unamused'], 35 | ['😞', 'disappointed'], 36 | ['😔', 'pensive'], 37 | ['😟', 'worried'], 38 | ['😕', 'confused'], 39 | ['🙁', 'slightly_frowning_face'], 40 | ['☹️', 'frowning_face'], 41 | ['😣', 'persevere'], 42 | ['😖', 'confounded'], 43 | ['😫', 'tired_face'], 44 | ['😩', 'weary'], 45 | ['😤', 'triumph'], 46 | ['😠', 'angry'], 47 | ['😡', 'rage'], 48 | ['😶', 'no_mouth'], 49 | ['😐', 'neutral_face'], 50 | ['😑', 'expressionless'], 51 | ['😯', 'hushed'], 52 | ['😦', 'frowning'], 53 | ['😧', 'anguished'], 54 | ['😮', 'open_mouth'], 55 | ['😲', 'astonished'], 56 | ['😵', 'dizzy_face'], 57 | ['😳', 'flushed'], 58 | ['😱', 'scream'], 59 | ['😨', 'fearful'], 60 | ['😰', 'cold_sweat'], 61 | ['😢', 'cry'], 62 | ['😥', 'disappointed_relieved'], 63 | ['🤤', 'drooling_face'], 64 | ['😭', 'sob'], 65 | ['😓', 'sweat'], 66 | ['😪', 'sleepy'], 67 | ['😴', 'sleeping'], 68 | ['🙄', 'roll_eyes'], 69 | ['🤔', 'thinking'], 70 | ['🤥', 'lying_face'], 71 | ['😬', 'grimacing'], 72 | ['🤐', 'zipper_mouth_face'], 73 | ['🤢', 'nauseated_face'], 74 | ['🤧', 'sneezing_face'], 75 | ['😷', 'mask'], 76 | ['🤒', 'face_with_thermometer'], 77 | ['🤕', 'face_with_head_bandage'], 78 | ['😈', 'smiling_imp'], 79 | ['👿', 'imp'], 80 | ['👹', 'japanese_ogre'], 81 | ['👺', 'japanese_goblin'], 82 | ['💩', 'hankey'], 83 | ['👻', 'ghost'], 84 | ['💀', 'skull'], 85 | ['☠️', 'skull_and_crossbones'], 86 | ['👽', 'alien'], 87 | ['👾', 'space_invader'], 88 | ['🤖', 'robot'], 89 | ['🎃', 'jack_o_lantern'], 90 | ['😺', 'smiley_cat'], 91 | ['😸', 'smile_cat'], 92 | ['😹', 'joy_cat'], 93 | ['😻', 'heart_eyes_cat'], 94 | ['😼', 'smirk_cat'], 95 | ['😽', 'kissing_cat'], 96 | ['🙀', 'scream_cat'], 97 | ['😿', 'crying_cat_face'], 98 | ['😾', 'pouting_cat'], 99 | ['👐', 'open_hands'], 100 | ['🙌', 'raised_hands'], 101 | ['👏', 'clap'], 102 | ['🙏', 'pray'], 103 | ['🤝', 'handshake'], 104 | ['👍', '+1'], 105 | ['👎', '-1'], 106 | ['👊', 'fist_oncoming'], 107 | ['✊', 'fist_raised'], 108 | ['🤛', 'fist_left'], 109 | ['🤜', 'fist_right'], 110 | ['🤞', 'crossed_fingers'], 111 | ['✌️', 'v'], 112 | ['🤘', 'metal'], 113 | ['👌', 'ok_hand'], 114 | ['👈', 'point_left'], 115 | ['👉', 'point_right'], 116 | ['👆', 'point_up_2'], 117 | ['👇', 'point_down'], 118 | ['☝️', 'point_up'], 119 | ['✋', 'hand'], 120 | ['🤚', 'raised_back_of_hand'], 121 | ['🖐', 'raised_hand_with_fingers_splayed'], 122 | ['🖖', 'vulcan_salute'], 123 | ['👋', 'wave'], 124 | ['🤙', 'call_me_hand'], 125 | ['💪', 'muscle'], 126 | ['🖕', 'middle_finger'], 127 | ['✍️', 'writing_hand'], 128 | ['🤳', 'selfie'], 129 | ['💅', 'nail_care'], 130 | ['💍', 'ring'], 131 | ['💄', 'lipstick'], 132 | ['💋', 'kiss'], 133 | ['👄', 'lips'], 134 | ['👅', 'tongue'], 135 | ['👂', 'ear'], 136 | ['👃', 'nose'], 137 | ['👣', 'footprints'], 138 | ['👁', 'eye'], 139 | ['👀', 'eyes'], 140 | ['🗣', 'speaking_head'], 141 | ['👤', 'bust_in_silhouette'], 142 | ['👥', 'busts_in_silhouette'], 143 | ['👶', 'baby'], 144 | ['👦', 'boy'], 145 | ['👧', 'girl'], 146 | ['👨', 'man'], 147 | ['👩', 'woman'], 148 | ['👱‍♀', 'blonde_woman'], 149 | ['👱', 'blonde_man'], 150 | ['👴', 'older_man'], 151 | ['👵', 'older_woman'], 152 | ['👲', 'man_with_gua_pi_mao'], 153 | ['👳‍♀', 'woman_with_turban'], 154 | ['👳', 'man_with_turban'], 155 | ['👮‍♀', 'policewoman'], 156 | ['👮', 'policeman'], 157 | ['👷‍♀', 'construction_worker_woman'], 158 | ['👷', 'construction_worker_man'], 159 | ['💂‍♀', 'guardswoman'], 160 | ['💂', 'guardsman'], 161 | ['🕵️‍♀️', 'female_detective'], 162 | ['🕵', 'male_detective'], 163 | ['👩‍⚕', 'woman_health_worker'], 164 | ['👨‍⚕', 'man_health_worker'], 165 | ['👩‍🌾', 'woman_farmer'], 166 | ['👨‍🌾', 'man_farmer'], 167 | ['👩‍🍳', 'woman_cook'], 168 | ['👨‍🍳', 'man_cook'], 169 | ['👩‍🎓', 'woman_student'], 170 | ['👨‍🎓', 'man_student'], 171 | ['👩‍🎤', 'woman_singer'], 172 | ['👨‍🎤', 'man_singer'], 173 | ['👩‍🏫', 'woman_teacher'], 174 | ['👨‍🏫', 'man_teacher'], 175 | ['👩‍🏭', 'woman_factory_worker'], 176 | ['👨‍🏭', 'man_factory_worker'], 177 | ['👩‍💻', 'woman_technologist'], 178 | ['👨‍💻', 'man_technologist'], 179 | ['👩‍💼', 'woman_office_worker'], 180 | ['👨‍💼', 'man_office_worker'], 181 | ['👩‍🔧', 'woman_mechanic'], 182 | ['👨‍🔧', 'man_mechanic'], 183 | ['👩‍🔬', 'woman_scientist'], 184 | ['👨‍🔬', 'man_scientist'], 185 | ['👩‍🎨', 'woman_artist'], 186 | ['👨‍🎨', 'man_artist'], 187 | ['👩‍🚒', 'woman_firefighter'], 188 | ['👨‍🚒', 'man_firefighter'], 189 | ['👩‍✈', 'woman_pilot'], 190 | ['👨‍✈', 'man_pilot'], 191 | ['👩‍🚀', 'woman_astronaut'], 192 | ['👨‍🚀', 'man_astronaut'], 193 | ['👩‍⚖', 'woman_judge'], 194 | ['👨‍⚖', 'man_judge'], 195 | ['🤶', 'mrs_claus'], 196 | ['🎅', 'santa'], 197 | ['👸', 'princess'], 198 | ['🤴', 'prince'], 199 | ['👰', 'bride_with_veil'], 200 | ['🤵', 'man_in_tuxedo'], 201 | ['👼', 'angel'], 202 | ['🤰', 'pregnant_woman'], 203 | ['🙇‍♀', 'bowing_woman'], 204 | ['🙇', 'bowing_man'], 205 | ['💁', 'tipping_hand_woman'], 206 | ['💁‍♂', 'tipping_hand_man'], 207 | ['🙅', 'no_good_woman'], 208 | ['🙅‍♂', 'no_good_man'], 209 | ['🙆', 'ok_woman'], 210 | ['🙆‍♂', 'ok_man'], 211 | ['🙋', 'raising_hand_woman'], 212 | ['🙋‍♂', 'raising_hand_man'], 213 | ['🤦‍♀', 'woman_facepalming'], 214 | ['🤦‍♂', 'man_facepalming'], 215 | ['🤷‍♀', 'woman_shrugging'], 216 | ['🤷‍♂', 'man_shrugging'], 217 | ['🙎', 'pouting_woman'], 218 | ['🙎‍♂', 'pouting_man'], 219 | ['🙍', 'frowning_woman'], 220 | ['🙍‍♂', 'frowning_man'], 221 | ['💇', 'haircut_woman'], 222 | ['💇‍♂', 'haircut_man'], 223 | ['💆', 'massage_woman'], 224 | ['💆‍♂', 'massage_man'], 225 | ['🕴', 'business_suit_levitating'], 226 | ['💃', 'dancer'], 227 | ['🕺', 'man_dancing'], 228 | ['👯', 'dancing_women'], 229 | ['👯‍♂', 'dancing_men'], 230 | ['🚶‍♀', 'walking_woman'], 231 | ['🚶', 'walking_man'], 232 | ['🏃‍♀', 'running_woman'], 233 | ['🏃', 'running_man'], 234 | ['👫', 'couple'], 235 | ['👭', 'two_women_holding_hands'], 236 | ['👬', 'two_men_holding_hands'], 237 | ['💑', 'couple_with_heart_woman_man'], 238 | ['👩‍❤️‍👩', 'couple_with_heart_woman_woman'], 239 | ['👨‍❤️‍👨', 'couple_with_heart_man_man'], 240 | ['💏', 'couplekiss_man_woman'], 241 | ['👩‍❤️‍💋‍👩', 'couplekiss_woman_woman'], 242 | ['👨‍❤️‍💋‍👨', 'couplekiss_man_man'], 243 | ['👪', 'family_man_woman_boy'], 244 | ['👨‍👩‍👧', 'family_man_woman_girl'], 245 | ['👨‍👩‍👧‍👦', 'family_man_woman_girl_boy'], 246 | ['👨‍👩‍👦‍👦', 'family_man_woman_boy_boy'], 247 | ['👨‍👩‍👧‍👧', 'family_man_woman_girl_girl'], 248 | ['👩‍👩‍👦', 'family_woman_woman_boy'], 249 | ['👩‍👩‍👧', 'family_woman_woman_girl'], 250 | ['👩‍👩‍👧‍👦', 'family_woman_woman_girl_boy'], 251 | ['👩‍👩‍👦‍👦', 'family_woman_woman_boy_boy'], 252 | ['👩‍👩‍👧‍👧', 'family_woman_woman_girl_girl'], 253 | ['👨‍👨‍👦', 'family_man_man_boy'], 254 | ['👨‍👨‍👧', 'family_man_man_girl'], 255 | ['👨‍👨‍👧‍👦', 'family_man_man_girl_boy'], 256 | ['👨‍👨‍👦‍👦', 'family_man_man_boy_boy'], 257 | ['👨‍👨‍👧‍👧', 'family_man_man_girl_girl'], 258 | ['👩‍👦', 'family_woman_boy'], 259 | ['👩‍👧', 'family_woman_girl'], 260 | ['👩‍👧‍👦', 'family_woman_girl_boy'], 261 | ['👩‍👦‍👦', 'family_woman_boy_boy'], 262 | ['👩‍👧‍👧', 'family_woman_girl_girl'], 263 | ['👨‍👦', 'family_man_boy'], 264 | ['👨‍👧', 'family_man_girl'], 265 | ['👨‍👧‍👦', 'family_man_girl_boy'], 266 | ['👨‍👦‍👦', 'family_man_boy_boy'], 267 | ['👨‍👧‍👧', 'family_man_girl_girl'], 268 | ['👚', 'womans_clothes'], 269 | ['👕', 'shirt'], 270 | ['👖', 'jeans'], 271 | ['👔', 'necktie'], 272 | ['👗', 'dress'], 273 | ['👙', 'bikini'], 274 | ['👘', 'kimono'], 275 | ['👠', 'high_heel'], 276 | ['👡', 'sandal'], 277 | ['👢', 'boot'], 278 | ['👞', 'mans_shoe'], 279 | ['👟', 'athletic_shoe'], 280 | ['👒', 'womans_hat'], 281 | ['🎩', 'tophat'], 282 | ['🎓', 'mortar_board'], 283 | ['👑', 'crown'], 284 | ['⛑', 'rescue_worker_helmet'], 285 | ['🎒', 'school_satchel'], 286 | ['👝', 'pouch'], 287 | ['👛', 'purse'], 288 | ['👜', 'handbag'], 289 | ['💼', 'briefcase'], 290 | ['👓', 'eyeglasses'], 291 | ['🕶', 'dark_sunglasses'], 292 | ['🌂', 'closed_umbrella'], 293 | ['☂️', 'open_umbrella'] 294 | ], 295 | name: 'People', 296 | icon: ['😄', 'smile'] 297 | }, 298 | { 299 | emojis: [ 300 | ['🐶', 'dog'], 301 | ['🐱', 'cat'], 302 | ['🐭', 'mouse'], 303 | ['🐹', 'hamster'], 304 | ['🐰', 'rabbit'], 305 | ['🦊', 'fox_face'], 306 | ['🐻', 'bear'], 307 | ['🐼', 'panda_face'], 308 | ['🐨', 'koala'], 309 | ['🐯', 'tiger'], 310 | ['🦁', 'lion'], 311 | ['🐮', 'cow'], 312 | ['🐷', 'pig'], 313 | ['🐽', 'pig_nose'], 314 | ['🐸', 'frog'], 315 | ['🐵', 'monkey_face'], 316 | ['🙈', 'see_no_evil'], 317 | ['🙉', 'hear_no_evil'], 318 | ['🙊', 'speak_no_evil'], 319 | ['🐒', 'monkey'], 320 | ['🐔', 'chicken'], 321 | ['🐧', 'penguin'], 322 | ['🐦', 'bird'], 323 | ['🐤', 'baby_chick'], 324 | ['🐣', 'hatching_chick'], 325 | ['🐥', 'hatched_chick'], 326 | ['🦆', 'duck'], 327 | ['🦅', 'eagle'], 328 | ['🦉', 'owl'], 329 | ['🦇', 'bat'], 330 | ['🐺', 'wolf'], 331 | ['🐗', 'boar'], 332 | ['🐴', 'horse'], 333 | ['🦄', 'unicorn'], 334 | ['🐝', 'bee'], 335 | ['🐛', 'bug'], 336 | ['🦋', 'butterfly'], 337 | ['🐌', 'snail'], 338 | ['🐚', 'shell'], 339 | ['🐞', 'beetle'], 340 | ['🐜', 'ant'], 341 | ['🕷', 'spider'], 342 | ['🕸', 'spider_web'], 343 | ['🐢', 'turtle'], 344 | ['🐍', 'snake'], 345 | ['🦎', 'lizard'], 346 | ['🦂', 'scorpion'], 347 | ['🦀', 'crab'], 348 | ['🦑', 'squid'], 349 | ['🐙', 'octopus'], 350 | ['🦐', 'shrimp'], 351 | ['🐠', 'tropical_fish'], 352 | ['🐟', 'fish'], 353 | ['🐡', 'blowfish'], 354 | ['🐬', 'dolphin'], 355 | ['🦈', 'shark'], 356 | ['🐳', 'whale'], 357 | ['🐋', 'whale2'], 358 | ['🐊', 'crocodile'], 359 | ['🐆', 'leopard'], 360 | ['🐅', 'tiger2'], 361 | ['🐃', 'water_buffalo'], 362 | ['🐂', 'ox'], 363 | ['🐄', 'cow2'], 364 | ['🦌', 'deer'], 365 | ['🐪', 'dromedary_camel'], 366 | ['🐫', 'camel'], 367 | ['🐘', 'elephant'], 368 | ['🦏', 'rhinoceros'], 369 | ['🦍', 'gorilla'], 370 | ['🐎', 'racehorse'], 371 | ['🐖', 'pig2'], 372 | ['🐐', 'goat'], 373 | ['🐏', 'ram'], 374 | ['🐑', 'sheep'], 375 | ['🐕', 'dog2'], 376 | ['🐩', 'poodle'], 377 | ['🐈', 'cat2'], 378 | ['🐓', 'rooster'], 379 | ['🦃', 'turkey'], 380 | ['🕊', 'dove'], 381 | ['🐇', 'rabbit2'], 382 | ['🐁', 'mouse2'], 383 | ['🐀', 'rat'], 384 | ['🐿', 'chipmunk'], 385 | ['🐾', 'feet'], 386 | ['🐉', 'dragon'], 387 | ['🐲', 'dragon_face'], 388 | ['🌵', 'cactus'], 389 | ['🎄', 'christmas_tree'], 390 | ['🌲', 'evergreen_tree'], 391 | ['🌳', 'deciduous_tree'], 392 | ['🌴', 'palm_tree'], 393 | ['🌱', 'seedling'], 394 | ['🌿', 'herb'], 395 | ['☘️', 'shamrock'], 396 | ['🍀', 'four_leaf_clover'], 397 | ['🎍', 'bamboo'], 398 | ['🎋', 'tanabata_tree'], 399 | ['🍃', 'leaves'], 400 | ['🍂', 'fallen_leaf'], 401 | ['🍁', 'maple_leaf'], 402 | ['🍄', 'mushroom'], 403 | ['🌾', 'ear_of_rice'], 404 | ['💐', 'bouquet'], 405 | ['🌷', 'tulip'], 406 | ['🌹', 'rose'], 407 | ['🥀', 'wilted_flower'], 408 | ['🌻', 'sunflower'], 409 | ['🌼', 'blossom'], 410 | ['🌸', 'cherry_blossom'], 411 | ['🌺', 'hibiscus'], 412 | ['🌎', 'earth_americas'], 413 | ['🌍', 'earth_africa'], 414 | ['🌏', 'earth_asia'], 415 | ['🌕', 'full_moon'], 416 | ['🌖', 'waning_gibbous_moon'], 417 | ['🌗', 'last_quarter_moon'], 418 | ['🌘', 'waning_crescent_moon'], 419 | ['🌑', 'new_moon'], 420 | ['🌒', 'waxing_crescent_moon'], 421 | ['🌓', 'first_quarter_moon'], 422 | ['🌔', 'moon'], 423 | ['🌚', 'new_moon_with_face'], 424 | ['🌝', 'full_moon_with_face'], 425 | ['🌞', 'sun_with_face'], 426 | ['🌛', 'first_quarter_moon_with_face'], 427 | ['🌜', 'last_quarter_moon_with_face'], 428 | ['🌙', 'crescent_moon'], 429 | ['💫', 'dizzy'], 430 | ['⭐️', 'star'], 431 | ['🌟', 'star2'], 432 | ['✨', 'sparkles'], 433 | ['⚡️', 'zap'], 434 | ['🔥', 'fire'], 435 | ['💥', 'boom'], 436 | ['☄', 'comet'], 437 | ['☀️', 'sunny'], 438 | ['🌤', 'sun_behind_small_cloud'], 439 | ['⛅️', 'partly_sunny'], 440 | ['🌥', 'sun_behind_large_cloud'], 441 | ['🌦', 'sun_behind_rain_cloud'], 442 | ['🌈', 'rainbow'], 443 | ['☁️', 'cloud'], 444 | ['🌧', 'cloud_with_rain'], 445 | ['⛈', 'cloud_with_lightning_and_rain'], 446 | ['🌩', 'cloud_with_lightning'], 447 | ['🌨', 'cloud_with_snow'], 448 | ['☃️', 'snowman_with_snow'], 449 | ['⛄️', 'snowman'], 450 | ['❄️', 'snowflake'], 451 | ['🌬', 'wind_face'], 452 | ['💨', 'dash'], 453 | ['🌪', 'tornado'], 454 | ['🌫', 'fog'], 455 | ['🌊', 'ocean'], 456 | ['💧', 'droplet'], 457 | ['💦', 'sweat_drops'], 458 | ['☔️', 'umbrella'] 459 | ], 460 | name: 'Nature', 461 | icon: ['🌸', 'cherry_blossom'] 462 | }, 463 | { 464 | emojis: [ 465 | ['🍏', 'green_apple'], 466 | ['🍎', 'apple'], 467 | ['🍐', 'pear'], 468 | ['🍊', 'tangerine'], 469 | ['🍋', 'lemon'], 470 | ['🍌', 'banana'], 471 | ['🍉', 'watermelon'], 472 | ['🍇', 'grapes'], 473 | ['🍓', 'strawberry'], 474 | ['🍈', 'melon'], 475 | ['🍒', 'cherries'], 476 | ['🍑', 'peach'], 477 | ['🍍', 'pineapple'], 478 | ['🥝', 'kiwi_fruit'], 479 | ['🥑', 'avocado'], 480 | ['🍅', 'tomato'], 481 | ['🍆', 'eggplant'], 482 | ['🥒', 'cucumber'], 483 | ['🥕', 'carrot'], 484 | ['🌽', 'corn'], 485 | ['🌶', 'hot_pepper'], 486 | ['🥔', 'potato'], 487 | ['🍠', 'sweet_potato'], 488 | ['🌰', 'chestnut'], 489 | ['🥜', 'peanuts'], 490 | ['🍯', 'honey_pot'], 491 | ['🥐', 'croissant'], 492 | ['🍞', 'bread'], 493 | ['🥖', 'baguette_bread'], 494 | ['🧀', 'cheese'], 495 | ['🥚', 'egg'], 496 | ['🍳', 'fried_egg'], 497 | ['🥓', 'bacon'], 498 | ['🥞', 'pancakes'], 499 | ['🍤', 'fried_shrimp'], 500 | ['🍗', 'poultry_leg'], 501 | ['🍖', 'meat_on_bone'], 502 | ['🍕', 'pizza'], 503 | ['🌭', 'hotdog'], 504 | ['🍔', 'hamburger'], 505 | ['🍟', 'fries'], 506 | ['🥙', 'stuffed_flatbread'], 507 | ['🌮', 'taco'], 508 | ['🌯', 'burrito'], 509 | ['🥗', 'green_salad'], 510 | ['🥘', 'shallow_pan_of_food'], 511 | ['🍝', 'spaghetti'], 512 | ['🍜', 'ramen'], 513 | ['🍲', 'stew'], 514 | ['🍥', 'fish_cake'], 515 | ['🍣', 'sushi'], 516 | ['🍱', 'bento'], 517 | ['🍛', 'curry'], 518 | ['🍚', 'rice'], 519 | ['🍙', 'rice_ball'], 520 | ['🍘', 'rice_cracker'], 521 | ['🍢', 'oden'], 522 | ['🍡', 'dango'], 523 | ['🍧', 'shaved_ice'], 524 | ['🍨', 'ice_cream'], 525 | ['🍦', 'icecream'], 526 | ['🍰', 'cake'], 527 | ['🎂', 'birthday'], 528 | ['🍮', 'custard'], 529 | ['🍭', 'lollipop'], 530 | ['🍬', 'candy'], 531 | ['🍫', 'chocolate_bar'], 532 | ['🍿', 'popcorn'], 533 | ['🍩', 'doughnut'], 534 | ['🍪', 'cookie'], 535 | ['🥛', 'milk_glass'], 536 | ['🍼', 'baby_bottle'], 537 | ['☕️', 'coffee'], 538 | ['🍵', 'tea'], 539 | ['🍶', 'sake'], 540 | ['🍺', 'beer'], 541 | ['🍻', 'beers'], 542 | ['🥂', 'clinking_glasses'], 543 | ['🍷', 'wine_glass'], 544 | ['🥃', 'tumbler_glass'], 545 | ['🍸', 'cocktail'], 546 | ['🍹', 'tropical_drink'], 547 | ['🍾', 'champagne'], 548 | ['🥄', 'spoon'], 549 | ['🍴', 'fork_and_knife'], 550 | ['🍽', 'plate_with_cutlery'] 551 | ], 552 | name: 'Foods', 553 | icon: ['🍔', 'hamburger'] 554 | }, 555 | { 556 | emojis: [ 557 | ['⚽️', 'soccer'], 558 | ['🏀', 'basketball'], 559 | ['🏈', 'football'], 560 | ['⚾️', 'baseball'], 561 | ['🎾', 'tennis'], 562 | ['🏐', 'volleyball'], 563 | ['🏉', 'rugby_football'], 564 | ['🎱', '8ball'], 565 | ['🏓', 'ping_pong'], 566 | ['🏸', 'badminton'], 567 | ['🥅', 'goal_net'], 568 | ['🏒', 'ice_hockey'], 569 | ['🏑', 'field_hockey'], 570 | ['🏏', 'cricket'], 571 | ['⛳️', 'golf'], 572 | ['🏹', 'bow_and_arrow'], 573 | ['🎣', 'fishing_pole_and_fish'], 574 | ['🥊', 'boxing_glove'], 575 | ['🥋', 'martial_arts_uniform'], 576 | ['⛸', 'ice_skate'], 577 | ['🎿', 'ski'], 578 | ['⛷', 'skier'], 579 | ['🏂', 'snowboarder'], 580 | ['🏋️‍♀️', 'weight_lifting_woman'], 581 | ['🏋', 'weight_lifting_man'], 582 | ['🤺', 'person_fencing'], 583 | ['🤼‍♀', 'women_wrestling'], 584 | ['🤼‍♂', 'men_wrestling'], 585 | ['🤸‍♀', 'woman_cartwheeling'], 586 | ['🤸‍♂', 'man_cartwheeling'], 587 | ['⛹️‍♀️', 'basketball_woman'], 588 | ['⛹', 'basketball_man'], 589 | ['🤾‍♀', 'woman_playing_handball'], 590 | ['🤾‍♂', 'man_playing_handball'], 591 | ['🏌️‍♀️', 'golfing_woman'], 592 | ['🏌', 'golfing_man'], 593 | ['🏄‍♀', 'surfing_woman'], 594 | ['🏄', 'surfing_man'], 595 | ['🏊‍♀', 'swimming_woman'], 596 | ['🏊', 'swimming_man'], 597 | ['🤽‍♀', 'woman_playing_water_polo'], 598 | ['🤽‍♂', 'man_playing_water_polo'], 599 | ['🚣‍♀', 'rowing_woman'], 600 | ['🚣', 'rowing_man'], 601 | ['🏇', 'horse_racing'], 602 | ['🚴‍♀', 'biking_woman'], 603 | ['🚴', 'biking_man'], 604 | ['🚵‍♀', 'mountain_biking_woman'], 605 | ['🚵', 'mountain_biking_man'], 606 | ['🎽', 'running_shirt_with_sash'], 607 | ['🏅', 'medal_sports'], 608 | ['🎖', 'medal_military'], 609 | ['🥇', '1st_place_medal'], 610 | ['🥈', '2nd_place_medal'], 611 | ['🥉', '3rd_place_medal'], 612 | ['🏆', 'trophy'], 613 | ['🏵', 'rosette'], 614 | ['🎗', 'reminder_ribbon'], 615 | ['🎫', 'ticket'], 616 | ['🎟', 'tickets'], 617 | ['🎪', 'circus_tent'], 618 | ['🤹‍♀', 'woman_juggling'], 619 | ['🤹‍♂', 'man_juggling'], 620 | ['🎭', 'performing_arts'], 621 | ['🎨', 'art'], 622 | ['🎬', 'clapper'], 623 | ['🎤', 'microphone'], 624 | ['🎧', 'headphones'], 625 | ['🎼', 'musical_score'], 626 | ['🎹', 'musical_keyboard'], 627 | ['🥁', 'drum'], 628 | ['🎷', 'saxophone'], 629 | ['🎺', 'trumpet'], 630 | ['🎸', 'guitar'], 631 | ['🎻', 'violin'], 632 | ['🎲', 'game_die'], 633 | ['🎯', 'dart'], 634 | ['🎳', 'bowling'], 635 | ['🎮', 'video_game'], 636 | ['🎰', 'slot_machine'] 637 | ], 638 | name: 'Activity', 639 | icon: ['⚽️', 'soccer'] 640 | }, 641 | { 642 | emojis: [ 643 | ['🚗', 'car'], 644 | ['🚕', 'taxi'], 645 | ['🚙', 'blue_car'], 646 | ['🚌', 'bus'], 647 | ['🚎', 'trolleybus'], 648 | ['🏎', 'racing_car'], 649 | ['🚓', 'police_car'], 650 | ['🚑', 'ambulance'], 651 | ['🚒', 'fire_engine'], 652 | ['🚐', 'minibus'], 653 | ['🚚', 'truck'], 654 | ['🚛', 'articulated_lorry'], 655 | ['🚜', 'tractor'], 656 | ['🛴', 'kick_scooter'], 657 | ['🚲', 'bike'], 658 | ['🛵', 'motor_scooter'], 659 | ['🏍', 'motorcycle'], 660 | ['🚨', 'rotating_light'], 661 | ['🚔', 'oncoming_police_car'], 662 | ['🚍', 'oncoming_bus'], 663 | ['🚘', 'oncoming_automobile'], 664 | ['🚖', 'oncoming_taxi'], 665 | ['🚡', 'aerial_tramway'], 666 | ['🚠', 'mountain_cableway'], 667 | ['🚟', 'suspension_railway'], 668 | ['🚃', 'railway_car'], 669 | ['🚋', 'train'], 670 | ['🚞', 'mountain_railway'], 671 | ['🚝', 'monorail'], 672 | ['🚄', 'bullettrain_side'], 673 | ['🚅', 'bullettrain_front'], 674 | ['🚈', 'light_rail'], 675 | ['🚂', 'steam_locomotive'], 676 | ['🚆', 'train2'], 677 | ['🚇', 'metro'], 678 | ['🚊', 'tram'], 679 | ['🚉', 'station'], 680 | ['🚁', 'helicopter'], 681 | ['🛩', 'small_airplane'], 682 | ['✈️', 'airplane'], 683 | ['🛫', 'flight_departure'], 684 | ['🛬', 'flight_arrival'], 685 | ['🚀', 'rocket'], 686 | ['🛰', 'artificial_satellite'], 687 | ['💺', 'seat'], 688 | ['🛶', 'canoe'], 689 | ['⛵️', 'boat'], 690 | ['🛥', 'motor_boat'], 691 | ['🚤', 'speedboat'], 692 | ['🛳', 'passenger_ship'], 693 | ['⛴', 'ferry'], 694 | ['🚢', 'ship'], 695 | ['⚓️', 'anchor'], 696 | ['🚧', 'construction'], 697 | ['⛽️', 'fuelpump'], 698 | ['🚏', 'busstop'], 699 | ['🚦', 'vertical_traffic_light'], 700 | ['🚥', 'traffic_light'], 701 | ['🗺', 'world_map'], 702 | ['🗿', 'moyai'], 703 | ['🗽', 'statue_of_liberty'], 704 | ['⛲️', 'fountain'], 705 | ['🗼', 'tokyo_tower'], 706 | ['🏰', 'european_castle'], 707 | ['🏯', 'japanese_castle'], 708 | ['🏟', 'stadium'], 709 | ['🎡', 'ferris_wheel'], 710 | ['🎢', 'roller_coaster'], 711 | ['🎠', 'carousel_horse'], 712 | ['⛱', 'parasol_on_ground'], 713 | ['🏖', 'beach_umbrella'], 714 | ['🏝', 'desert_island'], 715 | ['⛰', 'mountain'], 716 | ['🏔', 'mountain_snow'], 717 | ['🗻', 'mount_fuji'], 718 | ['🌋', 'volcano'], 719 | ['🏜', 'desert'], 720 | ['🏕', 'camping'], 721 | ['⛺️', 'tent'], 722 | ['🛤', 'railway_track'], 723 | ['🛣', 'motorway'], 724 | ['🏗', 'building_construction'], 725 | ['🏭', 'factory'], 726 | ['🏠', 'house'], 727 | ['🏡', 'house_with_garden'], 728 | ['🏘', 'houses'], 729 | ['🏚', 'derelict_house'], 730 | ['🏢', 'office'], 731 | ['🏬', 'department_store'], 732 | ['🏣', 'post_office'], 733 | ['🏤', 'european_post_office'], 734 | ['🏥', 'hospital'], 735 | ['🏦', 'bank'], 736 | ['🏨', 'hotel'], 737 | ['🏪', 'convenience_store'], 738 | ['🏫', 'school'], 739 | ['🏩', 'love_hotel'], 740 | ['💒', 'wedding'], 741 | ['🏛', 'classical_building'], 742 | ['⛪️', 'church'], 743 | ['🕌', 'mosque'], 744 | ['🕍', 'synagogue'], 745 | ['🕋', 'kaaba'], 746 | ['⛩', 'shinto_shrine'], 747 | ['🗾', 'japan'], 748 | ['🎑', 'rice_scene'], 749 | ['🏞', 'national_park'], 750 | ['🌅', 'sunrise'], 751 | ['🌄', 'sunrise_over_mountains'], 752 | ['🌠', 'stars'], 753 | ['🎇', 'sparkler'], 754 | ['🎆', 'fireworks'], 755 | ['🌇', 'city_sunrise'], 756 | ['🌆', 'city_sunset'], 757 | ['🏙', 'cityscape'], 758 | ['🌃', 'night_with_stars'], 759 | ['🌌', 'milky_way'], 760 | ['🌉', 'bridge_at_night'], 761 | ['🌁', 'foggy'] 762 | ], 763 | name: 'Places', 764 | icon: ['🚗', 'car'] 765 | }, 766 | { 767 | emojis: [ 768 | ['⌚️', 'watch'], 769 | ['📱', 'iphone'], 770 | ['📲', 'calling'], 771 | ['💻', 'computer'], 772 | ['⌨️', 'keyboard'], 773 | ['🖥', 'desktop_computer'], 774 | ['🖨', 'printer'], 775 | ['🖱', 'computer_mouse'], 776 | ['🖲', 'trackball'], 777 | ['🕹', 'joystick'], 778 | ['🗜', 'clamp'], 779 | ['💽', 'minidisc'], 780 | ['💾', 'floppy_disk'], 781 | ['💿', 'cd'], 782 | ['📀', 'dvd'], 783 | ['📼', 'vhs'], 784 | ['📷', 'camera'], 785 | ['📸', 'camera_flash'], 786 | ['📹', 'video_camera'], 787 | ['🎥', 'movie_camera'], 788 | ['📽', 'film_projector'], 789 | ['🎞', 'film_strip'], 790 | ['📞', 'telephone_receiver'], 791 | ['☎️', 'phone'], 792 | ['📟', 'pager'], 793 | ['📠', 'fax'], 794 | ['📺', 'tv'], 795 | ['📻', 'radio'], 796 | ['🎙', 'studio_microphone'], 797 | ['🎚', 'level_slider'], 798 | ['🎛', 'control_knobs'], 799 | ['⏱', 'stopwatch'], 800 | ['⏲', 'timer_clock'], 801 | ['⏰', 'alarm_clock'], 802 | ['🕰', 'mantelpiece_clock'], 803 | ['⌛️', 'hourglass'], 804 | ['⏳', 'hourglass_flowing_sand'], 805 | ['📡', 'satellite'], 806 | ['🔋', 'battery'], 807 | ['🔌', 'electric_plug'], 808 | ['💡', 'bulb'], 809 | ['🔦', 'flashlight'], 810 | ['🕯', 'candle'], 811 | ['🗑', 'wastebasket'], 812 | ['🛢', 'oil_drum'], 813 | ['💸', 'money_with_wings'], 814 | ['💵', 'dollar'], 815 | ['💴', 'yen'], 816 | ['💶', 'euro'], 817 | ['💷', 'pound'], 818 | ['💰', 'moneybag'], 819 | ['💳', 'credit_card'], 820 | ['💎', 'gem'], 821 | ['⚖️', 'balance_scale'], 822 | ['🔧', 'wrench'], 823 | ['🔨', 'hammer'], 824 | ['⚒', 'hammer_and_pick'], 825 | ['🛠', 'hammer_and_wrench'], 826 | ['⛏', 'pick'], 827 | ['🔩', 'nut_and_bolt'], 828 | ['⚙️', 'gear'], 829 | ['⛓', 'chains'], 830 | ['🔫', 'gun'], 831 | ['💣', 'bomb'], 832 | ['🔪', 'hocho'], 833 | ['🗡', 'dagger'], 834 | ['⚔️', 'crossed_swords'], 835 | ['🛡', 'shield'], 836 | ['🚬', 'smoking'], 837 | ['⚰️', 'coffin'], 838 | ['⚱️', 'funeral_urn'], 839 | ['🏺', 'amphora'], 840 | ['🔮', 'crystal_ball'], 841 | ['📿', 'prayer_beads'], 842 | ['💈', 'barber'], 843 | ['⚗️', 'alembic'], 844 | ['🔭', 'telescope'], 845 | ['🔬', 'microscope'], 846 | ['🕳', 'hole'], 847 | ['💊', 'pill'], 848 | ['💉', 'syringe'], 849 | ['🌡', 'thermometer'], 850 | ['🚽', 'toilet'], 851 | ['🚰', 'potable_water'], 852 | ['🚿', 'shower'], 853 | ['🛁', 'bathtub'], 854 | ['🛀', 'bath'], 855 | ['🛎', 'bellhop_bell'], 856 | ['🔑', 'key'], 857 | ['🗝', 'old_key'], 858 | ['🚪', 'door'], 859 | ['🛋', 'couch_and_lamp'], 860 | ['🛏', 'bed'], 861 | ['🛌', 'sleeping_bed'], 862 | ['🖼', 'framed_picture'], 863 | ['🛍', 'shopping'], 864 | ['🛒', 'shopping_cart'], 865 | ['🎁', 'gift'], 866 | ['🎈', 'balloon'], 867 | ['🎏', 'flags'], 868 | ['🎀', 'ribbon'], 869 | ['🎊', 'confetti_ball'], 870 | ['🎉', 'tada'], 871 | ['🎎', 'dolls'], 872 | ['🏮', 'izakaya_lantern'], 873 | ['🎐', 'wind_chime'], 874 | ['✉️', 'email'], 875 | ['📩', 'envelope_with_arrow'], 876 | ['📨', 'incoming_envelope'], 877 | ['📧', 'e-mail'], 878 | ['💌', 'love_letter'], 879 | ['📥', 'inbox_tray'], 880 | ['📤', 'outbox_tray'], 881 | ['📦', 'package'], 882 | ['🏷', 'label'], 883 | ['📪', 'mailbox_closed'], 884 | ['📫', 'mailbox'], 885 | ['📬', 'mailbox_with_mail'], 886 | ['📭', 'mailbox_with_no_mail'], 887 | ['📮', 'postbox'], 888 | ['📯', 'postal_horn'], 889 | ['📜', 'scroll'], 890 | ['📃', 'page_with_curl'], 891 | ['📄', 'page_facing_up'], 892 | ['📑', 'bookmark_tabs'], 893 | ['📊', 'bar_chart'], 894 | ['📈', 'chart_with_upwards_trend'], 895 | ['📉', 'chart_with_downwards_trend'], 896 | ['🗒', 'spiral_notepad'], 897 | ['🗓', 'spiral_calendar'], 898 | ['📆', 'calendar'], 899 | ['📅', 'date'], 900 | ['📇', 'card_index'], 901 | ['🗃', 'card_file_box'], 902 | ['🗳', 'ballot_box'], 903 | ['🗄', 'file_cabinet'], 904 | ['📋', 'clipboard'], 905 | ['📁', 'file_folder'], 906 | ['📂', 'open_file_folder'], 907 | ['🗂', 'card_index_dividers'], 908 | ['🗞', 'newspaper_roll'], 909 | ['📰', 'newspaper'], 910 | ['📓', 'notebook'], 911 | ['📔', 'notebook_with_decorative_cover'], 912 | ['📒', 'ledger'], 913 | ['📕', 'closed_book'], 914 | ['📗', 'green_book'], 915 | ['📘', 'blue_book'], 916 | ['📙', 'orange_book'], 917 | ['📚', 'books'], 918 | ['📖', 'book'], 919 | ['🔖', 'bookmark'], 920 | ['🔗', 'link'], 921 | ['📎', 'paperclip'], 922 | ['🖇', 'paperclips'], 923 | ['📐', 'triangular_ruler'], 924 | ['📏', 'straight_ruler'], 925 | ['📌', 'pushpin'], 926 | ['📍', 'round_pushpin'], 927 | ['✂️', 'scissors'], 928 | ['🖊', 'pen'], 929 | ['🖋', 'fountain_pen'], 930 | ['✒️', 'black_nib'], 931 | ['🖌', 'paintbrush'], 932 | ['🖍', 'crayon'], 933 | ['📝', 'memo'], 934 | ['✏️', 'pencil2'], 935 | ['🔍', 'mag'], 936 | ['🔎', 'mag_right'], 937 | ['🔏', 'lock_with_ink_pen'], 938 | ['🔐', 'closed_lock_with_key'], 939 | ['🔒', 'lock'], 940 | ['🔓', 'unlock'] 941 | ], 942 | name: 'Objects', 943 | icon: ['🔔', 'bell'] 944 | }, 945 | { 946 | emojis: [ 947 | ['❤️', 'heart'], 948 | ['💛', 'yellow_heart'], 949 | ['💚', 'green_heart'], 950 | ['💙', 'blue_heart'], 951 | ['💜', 'purple_heart'], 952 | ['🖤', 'black_heart'], 953 | ['💔', 'broken_heart'], 954 | ['❣️', 'heavy_heart_exclamation'], 955 | ['💕', 'two_hearts'], 956 | ['💞', 'revolving_hearts'], 957 | ['💓', 'heartbeat'], 958 | ['💗', 'heartpulse'], 959 | ['💖', 'sparkling_heart'], 960 | ['💘', 'cupid'], 961 | ['💝', 'gift_heart'], 962 | ['💟', 'heart_decoration'], 963 | ['☮️', 'peace_symbol'], 964 | ['✝️', 'latin_cross'], 965 | ['☪️', 'star_and_crescent'], 966 | ['🕉', 'om'], 967 | ['☸️', 'wheel_of_dharma'], 968 | ['✡️', 'star_of_david'], 969 | ['🔯', 'six_pointed_star'], 970 | ['🕎', 'menorah'], 971 | ['☯️', 'yin_yang'], 972 | ['☦️', 'orthodox_cross'], 973 | ['🛐', 'place_of_worship'], 974 | ['⛎', 'ophiuchus'], 975 | ['♈️', 'aries'], 976 | ['♉️', 'taurus'], 977 | ['♊️', 'gemini'], 978 | ['♋️', 'cancer'], 979 | ['♌️', 'leo'], 980 | ['♍️', 'virgo'], 981 | ['♎️', 'libra'], 982 | ['♏️', 'scorpius'], 983 | ['♐️', 'sagittarius'], 984 | ['♑️', 'capricorn'], 985 | ['♒️', 'aquarius'], 986 | ['♓️', 'pisces'], 987 | ['🆔', 'id'], 988 | ['⚛️', 'atom_symbol'], 989 | ['🉑', 'accept'], 990 | ['☢️', 'radioactive'], 991 | ['☣️', 'biohazard'], 992 | ['📴', 'mobile_phone_off'], 993 | ['📳', 'vibration_mode'], 994 | ['🈶', 'u6709'], 995 | ['🈚️', 'u7121'], 996 | ['🈸', 'u7533'], 997 | ['🈺', 'u55b6'], 998 | ['🈷️', 'u6708'], 999 | ['✴️', 'eight_pointed_black_star'], 1000 | ['🆚', 'vs'], 1001 | ['💮', 'white_flower'], 1002 | ['🉐', 'ideograph_advantage'], 1003 | ['㊙️', 'secret'], 1004 | ['㊗️', 'congratulations'], 1005 | ['🈴', 'u5408'], 1006 | ['🈵', 'u6e80'], 1007 | ['🈹', 'u5272'], 1008 | ['🈲', 'u7981'], 1009 | ['🅰️', 'a'], 1010 | ['🅱️', 'b'], 1011 | ['🆎', 'ab'], 1012 | ['🆑', 'cl'], 1013 | ['🅾️', 'o2'], 1014 | ['🆘', 'sos'], 1015 | ['❌', 'x'], 1016 | ['⭕️', 'o'], 1017 | ['🛑', 'stop_sign'], 1018 | ['⛔️', 'no_entry'], 1019 | ['📛', 'name_badge'], 1020 | ['🚫', 'no_entry_sign'], 1021 | ['💯', '100'], 1022 | ['💢', 'anger'], 1023 | ['♨️', 'hotsprings'], 1024 | ['🚷', 'no_pedestrians'], 1025 | ['🚯', 'do_not_litter'], 1026 | ['🚳', 'no_bicycles'], 1027 | ['🚱', 'non-potable_water'], 1028 | ['🔞', 'underage'], 1029 | ['📵', 'no_mobile_phones'], 1030 | ['🚭', 'no_smoking'], 1031 | ['❗️', 'exclamation'], 1032 | ['❕', 'grey_exclamation'], 1033 | ['❓', 'question'], 1034 | ['❔', 'grey_question'], 1035 | ['‼️', 'bangbang'], 1036 | ['⁉️', 'interrobang'], 1037 | ['🔅', 'low_brightness'], 1038 | ['🔆', 'high_brightness'], 1039 | ['〽️', 'part_alternation_mark'], 1040 | ['⚠️', 'warning'], 1041 | ['🚸', 'children_crossing'], 1042 | ['🔱', 'trident'], 1043 | ['⚜️', 'fleur_de_lis'], 1044 | ['🔰', 'beginner'], 1045 | ['♻️', 'recycle'], 1046 | ['✅', 'white_check_mark'], 1047 | ['🈯️', 'u6307'], 1048 | ['💹', 'chart'], 1049 | ['❇️', 'sparkle'], 1050 | ['✳️', 'eight_spoked_asterisk'], 1051 | ['❎', 'negative_squared_cross_mark'], 1052 | ['🌐', 'globe_with_meridians'], 1053 | ['💠', 'diamond_shape_with_a_dot_inside'], 1054 | ['Ⓜ️', 'm'], 1055 | ['🌀', 'cyclone'], 1056 | ['💤', 'zzz'], 1057 | ['🏧', 'atm'], 1058 | ['🚾', 'wc'], 1059 | ['♿️', 'wheelchair'], 1060 | ['🅿️', 'parking'], 1061 | ['🈳', 'u7a7a'], 1062 | ['🈂️', 'sa'], 1063 | ['🛂', 'passport_control'], 1064 | ['🛃', 'customs'], 1065 | ['🛄', 'baggage_claim'], 1066 | ['🛅', 'left_luggage'], 1067 | ['🚹', 'mens'], 1068 | ['🚺', 'womens'], 1069 | ['🚼', 'baby_symbol'], 1070 | ['🚻', 'restroom'], 1071 | ['🚮', 'put_litter_in_its_place'], 1072 | ['🎦', 'cinema'], 1073 | ['📶', 'signal_strength'], 1074 | ['🈁', 'koko'], 1075 | ['🔣', 'symbols'], 1076 | ['ℹ️', 'information_source'], 1077 | ['🔤', 'abc'], 1078 | ['🔡', 'abcd'], 1079 | ['🔠', 'capital_abcd'], 1080 | ['🆖', 'ng'], 1081 | ['🆗', 'ok'], 1082 | ['🆙', 'up'], 1083 | ['🆒', 'cool'], 1084 | ['🆕', 'new'], 1085 | ['🆓', 'free'], 1086 | ['0️⃣', 'zero'], 1087 | ['1️⃣', 'one'], 1088 | ['2️⃣', 'two'], 1089 | ['3️⃣', 'three'], 1090 | ['4️⃣', 'four'], 1091 | ['5️⃣', 'five'], 1092 | ['6️⃣', 'six'], 1093 | ['7️⃣', 'seven'], 1094 | ['8️⃣', 'eight'], 1095 | ['9️⃣', 'nine'], 1096 | ['🔟', 'keycap_ten'], 1097 | ['🔢', '1234'], 1098 | ['#️⃣', 'hash'], 1099 | ['*️⃣', 'asterisk'], 1100 | ['▶️', 'arrow_forward'], 1101 | ['⏸', 'pause_button'], 1102 | ['⏯', 'play_or_pause_button'], 1103 | ['⏹', 'stop_button'], 1104 | ['⏺', 'record_button'], 1105 | ['⏭', 'next_track_button'], 1106 | ['⏮', 'previous_track_button'], 1107 | ['⏩', 'fast_forward'], 1108 | ['⏪', 'rewind'], 1109 | ['⏫', 'arrow_double_up'], 1110 | ['⏬', 'arrow_double_down'], 1111 | ['◀️', 'arrow_backward'], 1112 | ['🔼', 'arrow_up_small'], 1113 | ['🔽', 'arrow_down_small'], 1114 | ['➡️', 'arrow_right'], 1115 | ['⬅️', 'arrow_left'], 1116 | ['⬆️', 'arrow_up'], 1117 | ['⬇️', 'arrow_down'], 1118 | ['↗️', 'arrow_upper_right'], 1119 | ['↘️', 'arrow_lower_right'], 1120 | ['↙️', 'arrow_lower_left'], 1121 | ['↖️', 'arrow_upper_left'], 1122 | ['↕️', 'arrow_up_down'], 1123 | ['↔️', 'left_right_arrow'], 1124 | ['↪️', 'arrow_right_hook'], 1125 | ['↩️', 'leftwards_arrow_with_hook'], 1126 | ['⤴️', 'arrow_heading_up'], 1127 | ['⤵️', 'arrow_heading_down'], 1128 | ['🔀', 'twisted_rightwards_arrows'], 1129 | ['🔁', 'repeat'], 1130 | ['🔂', 'repeat_one'], 1131 | ['🔄', 'arrows_counterclockwise'], 1132 | ['🔃', 'arrows_clockwise'], 1133 | ['🎵', 'musical_note'], 1134 | ['🎶', 'notes'], 1135 | ['➕', 'heavy_plus_sign'], 1136 | ['➖', 'heavy_minus_sign'], 1137 | ['➗', 'heavy_division_sign'], 1138 | ['✖️', 'heavy_multiplication_x'], 1139 | ['💲', 'heavy_dollar_sign'], 1140 | ['💱', 'currency_exchange'], 1141 | ['™️', 'tm'], 1142 | ['©️', 'copyright'], 1143 | ['®️', 'registered'], 1144 | ['〰️', 'wavy_dash'], 1145 | ['➰', 'curly_loop'], 1146 | ['➿', 'loop'], 1147 | ['🔚', 'end'], 1148 | ['🔙', 'back'], 1149 | ['🔛', 'on'], 1150 | ['🔝', 'top'], 1151 | ['🔜', 'soon'], 1152 | ['✔️', 'heavy_check_mark'], 1153 | ['☑️', 'ballot_box_with_check'], 1154 | ['🔘', 'radio_button'], 1155 | ['⚪️', 'white_circle'], 1156 | ['⚫️', 'black_circle'], 1157 | ['🔴', 'red_circle'], 1158 | ['🔵', 'large_blue_circle'], 1159 | ['🔺', 'small_red_triangle'], 1160 | ['🔻', 'small_red_triangle_down'], 1161 | ['🔸', 'small_orange_diamond'], 1162 | ['🔹', 'small_blue_diamond'], 1163 | ['🔶', 'large_orange_diamond'], 1164 | ['🔷', 'large_blue_diamond'], 1165 | ['🔳', 'white_square_button'], 1166 | ['🔲', 'black_square_button'], 1167 | ['▪️', 'black_small_square'], 1168 | ['▫️', 'white_small_square'], 1169 | ['◾️', 'black_medium_small_square'], 1170 | ['◽️', 'white_medium_small_square'], 1171 | ['◼️', 'black_medium_square'], 1172 | ['◻️', 'white_medium_square'], 1173 | ['⬛️', 'black_large_square'], 1174 | ['⬜️', 'white_large_square'], 1175 | ['🔈', 'speaker'], 1176 | ['🔇', 'mute'], 1177 | ['🔉', 'sound'], 1178 | ['🔊', 'loud_sound'], 1179 | ['🔔', 'bell'], 1180 | ['🔕', 'no_bell'], 1181 | ['📣', 'mega'], 1182 | ['📢', 'loudspeaker'], 1183 | ['👁‍🗨', 'eye_speech_bubble'], 1184 | ['💬', 'speech_balloon'], 1185 | ['💭', 'thought_balloon'], 1186 | ['🗯', 'right_anger_bubble'], 1187 | ['♠️', 'spades'], 1188 | ['♣️', 'clubs'], 1189 | ['♥️', 'hearts'], 1190 | ['♦️', 'diamonds'], 1191 | ['🃏', 'black_joker'], 1192 | ['🎴', 'flower_playing_cards'], 1193 | ['🀄️', 'mahjong'], 1194 | ['🕐', 'clock1'], 1195 | ['🕑', 'clock2'], 1196 | ['🕒', 'clock3'], 1197 | ['🕓', 'clock4'], 1198 | ['🕔', 'clock5'], 1199 | ['🕕', 'clock6'], 1200 | ['🕖', 'clock7'], 1201 | ['🕗', 'clock8'], 1202 | ['🕘', 'clock9'], 1203 | ['🕙', 'clock10'], 1204 | ['🕚', 'clock11'], 1205 | ['🕛', 'clock12'], 1206 | ['🕜', 'clock130'], 1207 | ['🕝', 'clock230'], 1208 | ['🕞', 'clock330'], 1209 | ['🕟', 'clock430'], 1210 | ['🕠', 'clock530'], 1211 | ['🕡', 'clock630'], 1212 | ['🕢', 'clock730'], 1213 | ['🕣', 'clock830'], 1214 | ['🕤', 'clock930'], 1215 | ['🕥', 'clock1030'], 1216 | ['🕦', 'clock1130'], 1217 | ['🕧', 'clock1230'] 1218 | ], 1219 | name: 'Symbols', 1220 | icon: ['🔠', 'capital_abcd'] 1221 | } 1222 | ] 1223 | --------------------------------------------------------------------------------