Browse Source

Add plugin support

master
Joe Attardi 4 years ago
parent
commit
44753eaaa1
  1. 3
      .gitignore
  2. 6
      css/emoji-button.css
  3. 6
      index.d.ts
  4. 5
      site/src/components/Sidebar.js
  5. 26
      site/src/examples/plugin.js
  6. 13
      site/src/pages/404.js
  7. 86
      site/src/pages/docs/plugins.js
  8. 17
      site/src/pages/docs/plugins.module.css
  9. 1
      src/classes.ts
  10. 24
      src/index.ts
  11. 7
      src/types.ts

3
.gitignore

@ -1,2 +1,5 @@
node_modules
dist
test.js
test.html

6
css/emoji-button.css

@ -240,6 +240,12 @@
background: var(--dark-hover-color);
}
.emoji-picker__plugin-container {
margin: 0.5em;
display: flex;
flex-direction: row;
}
.emoji-picker__search-container {
margin: 0.5em;
position: relative;

6
index.d.ts

@ -35,6 +35,12 @@ declare namespace EmojiButton {
emojiSize?: string;
initialCategory?: Category | 'recents';
custom?: EmojiRecord[];
plugins?: Plugin[];
}
export interface Plugin {
render(picker: EmojiButton): HTMLElement;
destroy?(): void;
}
export interface EmojiSelection {

5
site/src/components/Sidebar.js

@ -31,6 +31,11 @@ export default function Sidebar() {
Custom Emojis
</Link>
</li>
<li>
<Link activeClassName={styles.active} to="/docs/plugins">
Plugins
</Link>
</li>
<li>
<Link activeClassName={styles.active} to="/docs/themes">
Themes

26
site/src/examples/plugin.js

@ -0,0 +1,26 @@
import { EmojiButton } from './dist/index.js';
const trigger = document.querySelector('#trigger');
const removePlugin = {
render(picker) {
const button = document.createElement('button');
button.innerHTML = 'Remove';
button.addEventListener('click', () => {
trigger.innerHTML = '';
picker.hidePicker();
});
return button;
}
};
const picker = new EmojiButton({
plugins: [removePlugin]
});
picker.on('emoji', selection => {
trigger.innerHTML = selection.emoji;
});
trigger.addEventListener('click', () => picker.togglePicker(trigger));

13
site/src/pages/404.js

@ -0,0 +1,13 @@
import React from 'react';
import DocLayout from '../components/DocLayout';
export default function NotFoundPage() {
return (
<DocLayout>
<h1>Not Found</h1>
<div style={{fontSize: '3rem'}}>😔</div>
<p>Sorry, the page you requested could not be found.</p>
</DocLayout>
);
}

86
site/src/pages/docs/plugins.js

@ -0,0 +1,86 @@
import React, { useRef, useState, useEffect } from 'react';
import { EmojiButton } from '@joeattardi/emoji-button';
import DocLayout from '../../components/DocLayout';
import SourceFile from '../../components/SourceFile';
import pluginExample from '!!raw-loader!../../examples/plugin.js';
import styles from './plugins.module.css';
export default function PluginsExample() {
const buttonRef = useRef();
const [picker, setPicker] = useState(null);
const [emoji, setEmoji] = useState(null);
useEffect(() => {
const removePlugin = {
render(picker) {
const button = document.createElement('button');
button.innerHTML = 'Remove';
button.addEventListener('click', () => {
setEmoji(null);
picker.hidePicker();
});
return button;
}
};
const pickerObj = new EmojiButton({
plugins: [removePlugin]
});
pickerObj.on('emoji', selection => setEmoji(selection.emoji));
setPicker(pickerObj);
}, []);
function togglePicker() {
picker.togglePicker(buttonRef.current);
}
return (
<DocLayout>
<h1>Plugins</h1>
<p>You can extend the emoji picker's UI using plugins.</p>
<p>
A plugin is a JavaScript object with a property called{' '}
<code>render</code>. The value of that property should be a function
that takes in the picker instance as its sole parameter. The return
value of the <code>render</code> function should be an{' '}
<code>HTMLElement</code> that contain the user interface for the plugin.
</p>
<p>
A plugin can also optionally define a <code>destroy</code> function
which will be called when the picker is destroyed, which can be used to
perform any necessary cleanup.
</p>
<p>
Plugins are passed as an array into the <code>plugins</code> picker
option. The plugins will be rendered at the top of the picker.
</p>
<p>
In the example on this page, we show a blank button until an emoji is
chosen. We have a plugin that adds a "Remove" button to the picker. If
that button is clicked, it removes the current emoji selection and
closes the picker.
</p>
<div>
<button
className={styles.emojiButton}
ref={buttonRef}
onClick={togglePicker}
>
{emoji}
</button>
</div>
<SourceFile src={pluginExample} />
</DocLayout>
);
}

17
site/src/pages/docs/plugins.module.css

@ -0,0 +1,17 @@
.emoji-button {
font-size: 2rem;
background: #eeeeee;
border: 1px solid #cccccc;
border-radius: 5px;
padding: 0.5rem 1rem;
cursor: pointer;
margin: 0.5rem;
width: 4rem;
height: 4rem;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Segoe UI Emoji', 'Segoe UI Symbol', 'Segoe UI',
'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji', 'EmojiOne Color',
'Android Emoji';
}

1
src/classes.ts

@ -11,6 +11,7 @@ export const CLASS_NOT_FOUND_ICON = 'emoji-picker__search-not-found-icon';
export const CLASS_OVERLAY = 'emoji-picker__overlay';
export const CLASS_PICKER = 'emoji-picker';
export const CLASS_PICKER_CONTENT = 'emoji-picker__content';
export const CLASS_PLUGIN_CONTAINER = 'emoji-picker__plugin-container';
export const CLASS_PREVIEW = 'emoji-picker__preview';
export const CLASS_PREVIEW_EMOJI = 'emoji-picker__preview-emoji';
export const CLASS_PREVIEW_NAME = 'emoji-picker__preview-name';

24
src/index.ts

@ -28,7 +28,8 @@ import {
CLASS_VARIANT_OVERLAY,
CLASS_WRAPPER,
CLASS_OVERLAY,
CLASS_CUSTOM_EMOJI
CLASS_CUSTOM_EMOJI,
CLASS_PLUGIN_CONTAINER
} from './classes';
import { EmojiButtonOptions, I18NStrings, EmojiRecord } from './types';
@ -150,6 +151,21 @@ export class EmojiButton {
this.pickerContent = createElement('div', CLASS_PICKER_CONTENT);
if (this.options.plugins) {
const pluginContainer = createElement('div', CLASS_PLUGIN_CONTAINER);
this.options.plugins.forEach(plugin => {
if (!plugin.render) {
throw new Error(
'Emoji Button plugins must have a "render" function.'
);
}
pluginContainer.appendChild(plugin.render(this));
});
this.pickerEl.appendChild(pluginContainer);
}
if (this.options.showSearch) {
const searchContainer = new Search(
this.events,
@ -345,6 +361,12 @@ export class EmojiButton {
}
this.observer && this.observer.disconnect();
if (this.options.plugins) {
this.options.plugins.forEach(plugin => {
plugin.destroy && plugin.destroy();
});
}
}
hidePicker(): void {

7
src/types.ts

@ -1,4 +1,5 @@
import { Placement } from '@popperjs/core';
import { EmojiButton } from './index';
export interface EmojiRecord {
name: string;
@ -28,6 +29,11 @@ export interface EmojiEventData {
button: HTMLElement;
}
export interface Plugin {
render(picker: EmojiButton): HTMLElement;
destroy?(): void;
}
export interface EmojiButtonOptions {
position?: Placement;
autoHide?: boolean;
@ -50,6 +56,7 @@ export interface EmojiButtonOptions {
emojiSize?: string;
initialCategory?: Category | 'recents';
custom?: EmojiRecord[];
plugins?: Plugin[];
}
export type EmojiStyle = 'native' | 'twemoji';

Loading…
Cancel
Save