@tandyleo110/memos-emotes
v1.0.6
Published
Lightweight React emoji picker component for Memos — built-in Chinese emoji packs with dynamic loading
Maintainers
Readme
@tandyleo110/memos-emotes
Lightweight React emoji picker component for Memos — built-in Chinese emoji packs with dynamic loading and code splitting.
Features
- Zero runtime dependencies (except React)
- Tree-shakeable ES modules
- Dynamic pack loading with code splitting
- Built-in search across all emotes
- CSS Modules with dark mode support
- TypeScript types included
- Responsive size presets (sm / md / lg)
- Customizable via CSS variables
Installation
npm install @tandyleo110/memos-emotesQuick Start
import { EmotePicker } from "@tandyleo110/memos-emotes";
// Import emote data packs (each is a separate JSON data file for code splitting)
import joyData from "@tandyleo110/memos-emotes/dist/emotes/JOY.json";
import tvData from "@tandyleo110/memos-emotes/dist/emotes/TV.json";
function App() {
const handleSelect = (shortcode: string, dataUri: string) => {
// Insert the emote into your editor
console.log(shortcode); // e.g. "[joy_1]"
console.log(dataUri); // e.g. "data:image/png;base64,..."
};
return (
<EmotePicker
packs={[joyData, tvData]}
onSelect={handleSelect}
packNames={["JOY日常", "小电视"]}
size="md"
/>
);
}Available Emote Packs
| Pack ID | Name | Shortcode Format | Example |
|---------|------|-----------------|---------|
| JOY | JOY日常 | [joy_*] | [joy_1] |
| TV | 小电视 | [tv_*] | [tv_doge] |
| YellowFace | 黄脸 | [*] | [OK] |
| dy | 抖音 | [dy_*] | [dy_666] |
| xhs | 小红书 | [xhs_*] | [xhs_love] |
| weixin | 微信 | [wx_*] | [wx_smile] |
| weibo | 微博 | [wb_*] | [wb_doge] |
| qq2015 | QQ2015 | [q2015_*] | [q2015_cry] |
| qqn | QQ新 | [qnew_*] | [qnew_smile] |
| bear | 熊熊 | [bear_*] | [bear_happy] |
| pig1 | 小猪1 | [pig1_*] | [pig1_cry] |
| pig2 | 小猪2 | [pig2_*] | [pig2_happy] |
| my | 自定义 | [my_*] | [my_custom] |
API Reference
<EmotePicker>
Full-featured picker component with tabs, search, and grid.
<EmotePicker
packs={[joyData, tvData]}
onSelect={(shortcode, dataUri) => insertText(shortcode)}
packNames={["JOY日常", "小电视"]}
size="md"
layout="grid"
searchable={true}
defaultEmoteSize={2}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| packs | EmotePackData[] | required | Array of emote pack data modules |
| onSelect | (shortcode: string, dataUri: string) => void | required | Called when an emote is selected |
| packNames | string[] | undefined | Display names for each pack tab |
| size | "sm" \| "md" \| "lg" | "md" | Preset size of the picker |
| layout | "grid" \| "list" | "grid" | Layout mode for emotes |
| searchable | boolean | true | Whether to show the search bar |
| defaultEmoteSize | EmoteSize | 2 | Default size multiplier (1-9) |
<Emote>
Single emote image component.
<Emote src={dataUri} alt="[joy_1]" size={3} interactive onClick={handleClick} />| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string | required | Base64 data URI |
| alt | string | "" | Alt text |
| size | EmoteSize | 2 | Size multiplier (1-9) |
| interactive | boolean | false | Enable hover/click effects |
| onClick | () => void | undefined | Click handler |
<EmoteGrid>
Grid layout for displaying emotes.
<EmoteGrid
emotes={pack.emotes}
imageData={pack.data}
emoteSize={3}
onSelect={(code, file) => console.log(code)}
/><EmoteSearch>
Search input for filtering emotes.
<EmoteSearch value={query} onChange={setQuery} placeholder="Search..." /><EmoteTab>
Tab button for switching between packs.
<EmoteTab label="JOY日常" active onClick={() => setActive(0)} /><EmoteTooltip>
Hover tooltip showing emote preview and shortcode.
<EmoteTooltip shortcode="[joy_1]" dataUri={dataUri}>
<button>Hover me</button>
</EmoteTooltip><EmoteCategory>
Section header for grouping emotes.
<EmoteCategory name="JOY日常" count={15} />Hooks
useEmoteData(url)
Dynamically load an emote pack from a URL.
const { data, loading, error, reload } = useEmoteData(
"https://unpkg.com/@tandyleo110/[email protected]/dist/emotes/JOY.json"
);useEmoteSearch(emotes)
Filter emotes by shortcode search query.
const { query, setQuery, filtered, isSearching } = useEmoteSearch(emotes);useEmotePicker(packs)
Manage picker tab state and emote selection.
const { activeTabIndex, setActiveTab, activeEmotes, handleSelect } = useEmotePicker(packs);useEmoteSize(defaultSize)
Manage emote display size with responsive controls.
const { size, increase, decrease, pixelSize } = useEmoteSize(2);Utilities
import {
loadEmotePack, // Dynamic pack loading
parseShortcodes, // Parse text for shortcodes
replaceShortcodesWithImages, // Replace shortcodes with <img> tags
isValidShortcode, // Validate shortcode format
getAllPacks, // Get all registered packs
getPackById, // Find pack by ID
} from "@tandyleo110/memos-emotes";Styling
The component uses CSS Modules by default. You can override styles using CSS variables:
:root {
--emote-picker-bg: #ffffff;
--emote-picker-border: #e5e7eb;
--emote-tab-active-bg: #eff6ff;
--emote-tab-active-color: #2563eb;
--emote-search-bg: #f9fafb;
--emote-hover-bg: #f3f4f6;
}Dark mode is automatically supported via prefers-color-scheme: dark.
Loading Packs Dynamically
For code splitting, load packs on demand:
import { useEmoteData } from "@tandyleo110/memos-emotes";
function LazyPicker() {
const { data, loading } = useEmoteData(
"https://unpkg.com/@tandyleo110/[email protected]/dist/emotes/JOY.json"
);
if (loading) return <div>Loading...</div>;
if (!data) return null;
return <EmotePicker packs={[data]} onSelect={handleSelect} />;
}License
MIT
