Emoji Picker
Accessible, unstyled, data-free emoji picker: consumers supply typed emoji data.
|
|
| Package |
@vizejs/ui/emoji-picker |
| Maturity |
stable |
| Own the source |
vize lib pull emoji-picker |
| Requires |
context, controllable-state, id, listbox-grid |
| Aliases |
emoji picker, emoji selector, reaction picker |
| Covers |
WAI-ARIA grid pattern, emoji-mart, Frimousse, Ark UI |
Usage
import { EmojiPicker, EmojiPickerRoot, EmojiPickerCategory, EmojiPickerEmpty, EmojiPickerGrid, EmojiPickerItem, EmojiPickerPreview, EmojiPickerSearch, EmojiPickerSkinTone } from "@vizejs/ui/emoji-picker";
Or copy the source into your project with vize lib pull emoji-picker (see Source Distribution).
API
EmojiPicker<T>
Source: src/families/selection/emoji-picker/emoji-picker-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
items (required) |
readonly T[] |
required |
Every emoji item, in display order. No dataset ships with the picker. |
getEmoji (required) |
(item: T) => string |
required |
Native glyph for the default tone. |
getName (required) |
(item: T) => string |
required |
Human-readable name used for search, labels, and the preview. |
getKeywords |
(item: T) => readonly string[] |
undefined |
Extra search keywords. |
getCategory |
(item: T) => string |
undefined |
Category id of an item; omit for a single uncategorized section. |
getSkins |
(item: T) => readonly string[] | undefined |
undefined |
Skin-tone variants indexed 0–5 (0 is the default tone). |
categories |
readonly EmojiPickerCategory[] |
undefined |
Category order and labels; unknown categories follow in first-seen order. |
columns |
number |
8 |
Cells per row; must match the consumer's CSS grid. |
skinTone |
EmojiSkinTone |
undefined |
Controlled skin tone. undefined selects uncontrolled behavior. |
defaultSkinTone |
EmojiSkinTone |
0 |
Initial skin tone for uncontrolled use. |
search |
string |
undefined |
Controlled search text. undefined selects uncontrolled behavior. |
filter |
EmojiPickerFilter<T> |
undefined |
Search filter. Defaults to an accent- and case-insensitive name/keyword match. |
recent |
readonly T[] |
undefined |
Consumer-owned recently used items, shown as the first section. |
recentLabel |
string |
"Recently used" |
Label of the recent section. |
searchLabel |
string |
"Search results" |
Label of the search results section. |
defaultLabel |
string |
"Emoji" |
Label of the single section used when getCategory is omitted. |
dir |
"ltr" | "rtl" |
"ltr" |
Reading direction; rtl mirrors ArrowLeft and ArrowRight. |
Events
| Event |
Payload |
Description |
select |
[item: T, glyph: string] |
Fired when an emoji is chosen, with the item and its glyph with the skin tone applied. |
update:skinTone |
[tone: EmojiSkinTone] |
Fired when the skin tone requests a new controlled value. |
update:search |
[text: string] |
Fired when the search text requests a new controlled value. |
Slots
| Slot |
Slot props |
Description |
default |
EmojiPickerSlotState<T> |
Search, grid, categories, skin tones, and preview. Receives typed sections. |
Exposed
| Member |
Type |
Description |
activeItem |
— |
|
focusGrid |
— |
|
setSearch |
— |
|
skinTone |
— |
|
EmojiPickerRoot<T>
Source: src/families/selection/emoji-picker/emoji-picker-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
items (required) |
readonly T[] |
required |
Every emoji item, in display order. No dataset ships with the picker. |
getEmoji (required) |
(item: T) => string |
required |
Native glyph for the default tone. |
getName (required) |
(item: T) => string |
required |
Human-readable name used for search, labels, and the preview. |
getKeywords |
(item: T) => readonly string[] |
undefined |
Extra search keywords. |
getCategory |
(item: T) => string |
undefined |
Category id of an item; omit for a single uncategorized section. |
getSkins |
(item: T) => readonly string[] | undefined |
undefined |
Skin-tone variants indexed 0–5 (0 is the default tone). |
categories |
readonly EmojiPickerCategory[] |
undefined |
Category order and labels; unknown categories follow in first-seen order. |
columns |
number |
8 |
Cells per row; must match the consumer's CSS grid. |
skinTone |
EmojiSkinTone |
undefined |
Controlled skin tone. undefined selects uncontrolled behavior. |
defaultSkinTone |
EmojiSkinTone |
0 |
Initial skin tone for uncontrolled use. |
search |
string |
undefined |
Controlled search text. undefined selects uncontrolled behavior. |
filter |
EmojiPickerFilter<T> |
undefined |
Search filter. Defaults to an accent- and case-insensitive name/keyword match. |
recent |
readonly T[] |
undefined |
Consumer-owned recently used items, shown as the first section. |
recentLabel |
string |
"Recently used" |
Label of the recent section. |
searchLabel |
string |
"Search results" |
Label of the search results section. |
defaultLabel |
string |
"Emoji" |
Label of the single section used when getCategory is omitted. |
dir |
"ltr" | "rtl" |
"ltr" |
Reading direction; rtl mirrors ArrowLeft and ArrowRight. |
Events
| Event |
Payload |
Description |
select |
[item: T, glyph: string] |
Fired when an emoji is chosen, with the item and its glyph with the skin tone applied. |
update:skinTone |
[tone: EmojiSkinTone] |
Fired when the skin tone requests a new controlled value. |
update:search |
[text: string] |
Fired when the search text requests a new controlled value. |
Slots
| Slot |
Slot props |
Description |
default |
EmojiPickerSlotState<T> |
Search, grid, categories, skin tones, and preview. Receives typed sections. |
Exposed
| Member |
Type |
Description |
activeItem |
T | undefined |
Highlighted item. |
skinTone |
EmojiSkinTone |
Current skin tone. |
focusGrid |
() => void |
Move DOM focus into the grid. |
setSearch |
(text: string) => void |
Replace the search text. |
EmojiPickerCategory<T>
Source: src/families/selection/emoji-picker/emoji-picker-category.vue
Props
| Prop |
Type |
Default |
Description |
section (required) |
EmojiPickerSection<T> |
required |
Section from the root slot state sections. |
Slots
| Slot |
Slot props |
Description |
default |
EmojiPickerCellSlotState<T> |
Render one EmojiPickerItem per cell with :item and :index. |
label |
{ readonly label: string } |
Custom section label content. |
EmojiPickerEmpty
Source: src/families/selection/emoji-picker/emoji-picker-empty.vue
Slots
| Slot |
Slot props |
Description |
default |
{ readonly search: string } |
Message shown while no emoji is visible. Receives the search text. |
EmojiPickerGrid
Source: src/families/selection/emoji-picker/emoji-picker-grid.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Emoji" |
Accessible name of the grid. |
Slots
| Slot |
Slot props |
Description |
default |
— |
One EmojiPickerCategory per section from the root slot state. |
EmojiPickerItem<T>
Source: src/families/selection/emoji-picker/emoji-picker-item.vue
Props
| Prop |
Type |
Default |
Description |
item (required) |
T |
required |
Emoji item rendered by this cell. |
index (required) |
number |
required |
Item index inside its section, from the category slot. |
Slots
| Slot |
Slot props |
Description |
default |
EmojiPickerItemSlotState<T> |
Cell content; defaults to the glyph. Receives glyph, name, and highlight state. |
EmojiPickerPreview
Source: src/families/selection/emoji-picker/emoji-picker-preview.vue
Slots
| Slot |
Slot props |
Description |
default |
EmojiPickerPreviewSlotState |
Preview of the highlighted emoji. Defaults to its glyph and name. |
EmojiPickerSearch
Source: src/families/selection/emoji-picker/emoji-picker-search.vue
Props
| Prop |
Type |
Default |
Description |
placeholder |
string |
undefined |
Hint text shown while empty. |
ariaLabel |
string |
"Search emoji" |
Accessible name of the search field. |
EmojiPickerSkinTone
Source: src/families/selection/emoji-picker/emoji-picker-skin-tone.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Skin tone" |
Accessible name of the radio group. |
labels |
readonly string[] |
["Default", "Light", "Medium-light", "Medium", "Medium-dark", "Dark"] |
Accessible names of tones 0–5. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly tone: EmojiSkinTone; readonly label: string; readonly checked: boolean; } |
Content of one tone swatch. Receives the tone, its label, and checked state. |
Behavior
Contract
EmojiPicker is a headless, data-free emoji picker: no dataset ships, and the
consumer maps typed items through required getEmoji/getName accessors plus
optional getKeywords, getCategory, and getSkins. emoji-picker-root.vue
is generic over the item type T, builds sections (recent first, then
categories in order, then unknown categories; a non-empty search collapses
into one "search" section), owns the controllable skinTone and search, and
emits select(item, glyph) with the tone applied.
emoji-picker-grid.vue renders the APG role="grid" focus owner with
aria-activedescendant. Navigation runs over the concatenated rows of every
visible section: each section starts a new row and short rows are padded, so
the listbox-grid moveInGrid helper moves in two dimensions and skips padding.
emoji-picker-category.vue renders a labelled role="rowgroup" with a
columnheader label row and role="row" chunks, emoji-picker-item.vue a
role="gridcell", emoji-picker-search.vue a search field,
emoji-picker-skin-tone.vue a roving radiogroup of six tones,
emoji-picker-preview.vue a polite live preview, and emoji-picker-empty.vue
the no-results state. No CSS ships.
Normative Behavior
| # |
State |
Input |
Outcome |
Proven by |
| E1 |
any |
render |
focusable grid with aria-colcount/aria-rowcount (item plus label rows), labelled rowgroups, named gridcells |
renders an APG grid with labelled category rowgroups and gridcells |
| E2 |
grid focused |
arrows / Home / End / Ctrl+Home/End / PageUp/Down |
focus highlights the first cell; moves in 2-D across section boundaries, skipping padding cells |
2-D keyboard navigation crosses section boundaries over padded rows |
| E3 |
highlighted cell |
Enter / Space / click |
emits select(item, glyph) with the skin tone applied; clicked cells become active |
Enter, Space, and click emit select with the skin tone applied |
| E4 |
skin tones |
arrows / Home / End / click |
roving tabindex radiogroup; arrows wrap and select; glyphs update; update:skinTone emits |
the skin-tone radiogroup uses roving focus and updates glyphs |
| E5 |
controlled tone |
click a tone |
emits and waits for the parent |
controlled skin tone waits for the parent |
| E6 |
search |
type / Enter / Escape / ArrowDown |
filters names and keywords accent-insensitively into one section, highlights the first match, Enter selects it, empty state shows, Escape clears, ArrowDown enters the grid |
search filters by name and keyword, highlights the first match, and drives the empty state |
| E7 |
recent |
render / navigate |
recent items form the first section; repeated items keep unique cell ids |
recent items form the first section and repeat safely |
| E8 |
pointer |
pointer move |
highlights the cell and the live preview shows glyph and name |
the preview follows the highlighted emoji |
| E9 |
no provider |
mount a part |
throws VIZE_UI_CONTEXT_MISSING |
parts require an EmojiPicker provider |
| E10 |
pure helpers |
sections / glyphs / virtual grid |
unknown categories follow in first-seen order; padding cells are empty; tones clamp to 0–5 |
pure helpers build sections, glyphs, and the padded virtual grid |
SSR
Cell ids derive from the root id plus section and item positions, so server
markup is byte-identical across isolated requests and hydrates without warnings
(renders byte-identical EmojiPicker markup across isolated SSR requests,
hydrates EmojiPicker without mismatches or node replacement). Large sets are
filtered in O(n) per keystroke; rows are not virtualized.