@emoji-components/core 1.0.0-alpha.2 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,9 +21,9 @@ const picker = createEmojiPicker(store, { frequent: 16 });
21
21
  picker.subscribe(() => console.log(picker.getSnapshot().sections));
22
22
  picker.setQuery("cat");
23
23
 
24
- const typeahead = createEmojiSuggestions(store, { onChoose: (s) => console.log(typeahead.textFor(s)) });
25
- input.addEventListener("keydown", (e) => typeahead.keydown(e));
26
- const stop = typeahead.syncCombobox(input);
24
+ const suggestions = createEmojiSuggestions(store, { onChoose: (s) => console.log(suggestions.textFor(s)) });
25
+ input.addEventListener("keydown", (e) => suggestions.keydown(e));
26
+ const stop = suggestions.syncCombobox(input);
27
27
  ```
28
28
 
29
29
  `data` defaults to the English dataset of [`@emoji-components/data`](../data/README.md) on jsDelivr, pinned to this version. Uncontrolled frequently used emoji and skin tone persist to `localStorage`; `store.customEmojiErrors()` reports a custom emoji loader that failed, and `store.load()` runs it again.
@@ -32,7 +32,7 @@ const stop = typeahead.syncCombobox(input);
32
32
 
33
33
  | Import | Contents |
34
34
  |---|---|
35
- | `@emoji-components/core` | `createEmojiStore`, `createEmojiPicker`, `createEmojiSuggestions`, `createSearch`, `lexicalSearch`, `resolveEmoji`, `findShortcodeTrigger`, `isComposingKey`, `localeData`, `twemojiUrl`, unified-code helpers |
35
+ | `@emoji-components/core` | `createEmojiStore`, `createEmojiPicker`, `createEmojiSuggestions`, `createEmojiSearch`, `lexicalSearch`, `resolveEmoji`, `findShortcodeTrigger`, `isComposingKey`, `localeData`, `twemojiUrl`, unified-code helpers |
36
36
  | `/grid` | Grid layout and keyboard moves, for your own virtualizer |
37
37
  | `/native` | Whether the system font draws an emoji |
38
38
  | `/build` | `buildDataset` from `emojibase-data` (install it alongside), also as the `npx @emoji-components/core build-data <locale>` CLI |
package/dist/build.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  defaultShortcodeSets,
4
4
  emojibaseLocales,
5
5
  findEmojibase
6
- } from "./chunk-UG4NPOXS.js";
6
+ } from "./chunk-MJPQEHGK.js";
7
7
  export {
8
8
  buildDataset,
9
9
  defaultShortcodeSets,
@@ -5,6 +5,7 @@ import path from "path";
5
5
 
6
6
  // ../../node_modules/emojibase/esm/index.js
7
7
  var SEQUENCE_REMOVAL_PATTERN = /200D|FE0E|FE0F/g;
8
+ var GROUP_KEY_SMILEYS_EMOTION = "smileys-emotion";
8
9
  var GROUP_KEY_PEOPLE_BODY = "people-body";
9
10
  var GROUP_KEY_ANIMALS_NATURE = "animals-nature";
10
11
  var GROUP_KEY_FOOD_DRINK = "food-drink";
@@ -57,7 +58,7 @@ var isEnglish = (locale) => locale.split("-")[0] === "en";
57
58
  var defaultShortcodeSets = (locale) => isEnglish(locale) ? ["iamcal"] : ["iamcal", "cldr"];
58
59
  var GROUP_OF = { 0: 0, 1: 0, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7 };
59
60
  var MESSAGE_KEY = {
60
- "smileys-people": GROUP_KEY_PEOPLE_BODY,
61
+ "smileys-people": [GROUP_KEY_SMILEYS_EMOTION, GROUP_KEY_PEOPLE_BODY],
61
62
  "animals-nature": GROUP_KEY_ANIMALS_NATURE,
62
63
  "food-drink": GROUP_KEY_FOOD_DRINK,
63
64
  "travel-places": GROUP_KEY_TRAVEL_PLACES,
@@ -108,7 +109,9 @@ function buildDataset({ locale, shortcodes: sets = defaultShortcodeSets(locale),
108
109
  emojis.push([unified, lower(r.label), [...new Set(tags)], joinShortcodesToEmoji({ ...r, skins: void 0 }, shortcodes).shortcodes ?? [], group, r.version, hasTones]);
109
110
  }
110
111
  const capitalize = (s) => s.charAt(0).toLocaleUpperCase(locale) + s.slice(1);
111
- const groups = isEnglish(locale) ? ENGLISH_GROUPS : GROUPS.map((key) => capitalize(messages.groups.find((g) => g.key === MESSAGE_KEY[key])?.message ?? key));
112
+ const message = (key) => messages.groups.find((g) => g.key === key)?.message ?? key;
113
+ const list = new Intl.ListFormat(locale, { type: "conjunction" });
114
+ const groups = isEnglish(locale) ? ENGLISH_GROUPS : GROUPS.map((key) => capitalize(list.format(toArray(MESSAGE_KEY[key]).map(message))));
112
115
  return {
113
116
  dataset: { format: 1, locale, emojibase: version, groups, emojis, toneExceptions },
114
117
  stats: { emoji: emojis.length, withShortcodes: emojis.filter((e) => e[3].length).length, computedTones, toneExceptions: Object.keys(toneExceptions).length }
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  defaultShortcodeSets,
5
5
  emojibaseLocales,
6
6
  findEmojibase
7
- } from "./chunk-UG4NPOXS.js";
7
+ } from "./chunk-MJPQEHGK.js";
8
8
 
9
9
  // scripts/cli.ts
10
10
  import fs from "fs";
package/dist/frequents.js CHANGED
@@ -3,7 +3,6 @@ import { controllable, createChangeDetails } from "./change.js";
3
3
  import { cached, createStore } from "./reactive.js";
4
4
  var HALF_LIFE = 14 * 864e5;
5
5
  var MAX = 64;
6
- var DEFAULTS = Object.fromEntries(["2764-fe0f", "1f44d", "1f44e", "1f602", "203c-fe0f", "2753"].map((u, i) => [u, { count: 0, lastUsed: -i }]));
7
6
  var NONE = {};
8
7
  var decayed = ({ count, lastUsed }, now) => count * 0.5 ** ((now - lastUsed) / HALF_LIFE);
9
8
  var ranked = (entries, now) => Object.entries(entries).sort((a, b) => decayed(b[1], now) - decayed(a[1], now) || b[1].lastUsed - a[1].lastUsed);
@@ -31,7 +30,7 @@ function reduceFrequents(entries, action) {
31
30
  var reduce = (state, action) => action.type === "own" ? action.entries === state.own ? state : { ...state, own: action.entries } : action.entries === state.controlled ? state : { ...state, controlled: action.entries };
32
31
  function createFrequencyStore({
33
32
  frequents,
34
- defaultFrequents = DEFAULTS,
33
+ defaultFrequents = NONE,
35
34
  onFrequentsChange,
36
35
  deferStorage
37
36
  } = {}) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/frequents.ts"],"sourcesContent":["import { controllable, createChangeDetails, type ChangeDetails } from \"./change\";\nimport { cached, createStore } from \"./reactive\";\n\n// count is the use count decayed by half every 14 days, as of lastUsed\nexport type FrequentEntry = { count: number; lastUsed: number };\nexport type Frequents = Readonly<Record<string, FrequentEntry>>;\nexport type FrequentsChangeDetails = ChangeDetails<\"pick\" | \"remove\" | \"clear\"> & { emoji?: string };\n\nexport interface FrequencyStore {\n pick(unified: string): void;\n remove(unified: string): void;\n clear(): void;\n top(n: number): readonly string[];\n score(unified: string): number;\n entries(): Frequents;\n subscribe(listener: () => void): () => void;\n}\n\ntype FrequencyStoreOptions = {\n frequents?: Frequents;\n defaultFrequents?: Frequents;\n onFrequentsChange?: (frequents: Frequents, details: FrequentsChangeDetails) => void;\n deferStorage?: boolean;\n};\n\nconst HALF_LIFE = 14 * 864e5;\nconst MAX = 64;\n\n// Real entries that were never used: remove() takes them out and any pick ranks above them\nconst DEFAULTS: Frequents = Object.fromEntries([\"2764-fe0f\", \"1f44d\", \"1f44e\", \"1f602\", \"203c-fe0f\", \"2753\"].map((u, i) => [u, { count: 0, lastUsed: -i }]));\nconst NONE: Frequents = {};\n\nconst decayed = ({ count, lastUsed }: FrequentEntry, now: number) => count * 0.5 ** ((now - lastUsed) / HALF_LIFE);\n// Ties go to the later use: digit-only keys such as \"2753\" would otherwise sort first by object key order\nconst ranked = (entries: Frequents, now: number) => Object.entries(entries).sort((a, b) => decayed(b[1], now) - decayed(a[1], now) || b[1].lastUsed - a[1].lastUsed);\n// The new pick stays: it would rank last against a full list of older uses and never build up a count\nconst capped = (entries: Frequents, now: number, keep: string): Frequents => {\n const top = ranked(entries, now).slice(0, MAX);\n return Object.fromEntries(top.some(([u]) => u === keep) ? top : [...top.slice(0, -1), [keep, entries[keep]!]]);\n};\n\ntype FrequentsAction = { type: \"pick\"; emoji: string; now: number } | { type: \"remove\"; emoji: string } | { type: \"clear\" };\n\nfunction reduceFrequents(entries: Frequents, action: FrequentsAction): Frequents {\n switch (action.type) {\n case \"pick\": {\n const prev = entries[action.emoji];\n return capped({ ...entries, [action.emoji]: { count: (prev ? decayed(prev, action.now) : 0) + 1, lastUsed: action.now } }, action.now, action.emoji);\n }\n case \"remove\": {\n if (!(action.emoji in entries)) return entries;\n const { [action.emoji]: _, ...rest } = entries;\n return rest;\n }\n case \"clear\":\n return NONE;\n default:\n return action satisfies never;\n }\n}\n\ntype FrequencyState = { own: Frequents; controlled: Frequents | undefined };\ntype FrequencyAction = { type: \"own\"; entries: Frequents } | { type: \"control\"; entries: Frequents | undefined };\n\nconst reduce = (state: FrequencyState, action: FrequencyAction): FrequencyState =>\n action.type === \"own\" ? (action.entries === state.own ? state : { ...state, own: action.entries }) : action.entries === state.controlled ? state : { ...state, controlled: action.entries };\n\nexport function createFrequencyStore({\n frequents,\n defaultFrequents = DEFAULTS,\n onFrequentsChange,\n deferStorage,\n}: FrequencyStoreOptions = {}): FrequencyStore & { control(value: Frequents | undefined, onChange: FrequencyStoreOptions[\"onFrequentsChange\"]): void } {\n const storage = persisted(\"emoji-components:frequents\");\n const read = () => parse(storage.read()) ?? defaultFrequents;\n const store = createStore<FrequencyState, FrequencyAction>({ own: deferStorage ? defaultFrequents : read(), controlled: frequents }, reduce);\n let onChange = onFrequentsChange;\n const tops = new WeakMap<Frequents, Map<number, readonly string[]>>();\n const value = controllable<Frequents, FrequentsChangeDetails>({\n controlled: () => store.getSnapshot().controlled,\n own: () => store.getSnapshot().own,\n onChange: () => onChange,\n write(next) {\n storage.write(JSON.stringify(next));\n store.dispatch({ type: \"own\", entries: next });\n },\n });\n const entries = value.value;\n const change = (action: FrequentsAction) => value.set(reduceFrequents(entries(), action), Object.assign(createChangeDetails(action.type), { emoji: action.type === \"clear\" ? undefined : action.emoji }));\n\n return {\n pick: (emoji) => void change({ type: \"pick\", emoji, now: Date.now() }),\n remove: (emoji) => void change({ type: \"remove\", emoji }),\n clear: () => void change({ type: \"clear\" }),\n top(n) {\n const state = entries();\n return cached(\n cached(tops, state, () => new Map()),\n n,\n // Decay keeps the order at any reference time; the newest use avoids reading the clock during render\n () =>\n ranked(state, Math.max(0, ...Object.values(state).map((e) => e.lastUsed)))\n .slice(0, n)\n .map(([u]) => u),\n );\n },\n score(unified) {\n const entry = entries()[unified];\n return entry ? decayed(entry, Date.now()) : 0;\n },\n entries,\n subscribe: readOnFirstSubscribe(store.subscribe, () => store.dispatch({ type: \"own\", entries: read() })),\n control(next, handler) {\n onChange = handler;\n store.dispatch({ type: \"control\", entries: next });\n },\n };\n}\n\n// A server-rendered page hydrates with the defaults; storage is read once something subscribes after mount\nexport function readOnFirstSubscribe(subscribe: (listener: () => void) => () => void, sync: () => void) {\n let synced = false;\n return (listener: () => void) => {\n const unsubscribe = subscribe(listener);\n if (!synced) {\n synced = true;\n sync();\n }\n return unsubscribe;\n };\n}\n\nconst isEntry = (value: unknown): value is FrequentEntry => typeof (value as FrequentEntry)?.count === \"number\" && typeof (value as FrequentEntry).lastUsed === \"number\";\n\nfunction parse(raw: string | null): Frequents | undefined {\n try {\n const value: unknown = raw ? JSON.parse(raw) : undefined;\n return value && typeof value === \"object\" ? Object.fromEntries(Object.entries(value).filter(([, entry]) => isEntry(entry))) : undefined;\n } catch {\n return undefined;\n }\n}\n\n// Node 25+ defines a localStorage global (a warning, or a file shared by every request); only a browser's is per user\nconst browserStorage = () => (typeof window === \"undefined\" ? undefined : window.localStorage);\n\nexport function persisted(key: string): { read(): string | null; write(value: string): void } {\n let memory: string | null = null;\n return {\n read() {\n try {\n return browserStorage()?.getItem(key) ?? memory;\n } catch {\n return memory;\n }\n },\n write(value) {\n memory = value;\n try {\n browserStorage()?.setItem(key, value);\n } catch {}\n },\n };\n}\n"],"mappings":";AAAA,SAAS,cAAc,2BAA+C;AACtE,SAAS,QAAQ,mBAAmB;AAwBpC,IAAM,YAAY,KAAK;AACvB,IAAM,MAAM;AAGZ,IAAM,WAAsB,OAAO,YAAY,CAAC,aAAa,SAAS,SAAS,SAAS,aAAa,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3J,IAAM,OAAkB,CAAC;AAEzB,IAAM,UAAU,CAAC,EAAE,OAAO,SAAS,GAAkB,QAAgB,QAAQ,SAAS,MAAM,YAAY;AAExG,IAAM,SAAS,CAAC,SAAoB,QAAgB,OAAO,QAAQ,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ;AAEnK,IAAM,SAAS,CAAC,SAAoB,KAAa,SAA4B;AAC3E,QAAM,MAAM,OAAO,SAAS,GAAG,EAAE,MAAM,GAAG,GAAG;AAC7C,SAAO,OAAO,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,MAAM,IAAI,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,QAAQ,IAAI,CAAE,CAAC,CAAC;AAC/G;AAIA,SAAS,gBAAgB,SAAoB,QAAoC;AAC/E,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,QAAQ;AACX,YAAM,OAAO,QAAQ,OAAO,KAAK;AACjC,aAAO,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,QAAQ,MAAM,OAAO,GAAG,IAAI,KAAK,GAAG,UAAU,OAAO,IAAI,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK;AAAA,IACrJ;AAAA,IACA,KAAK,UAAU;AACb,UAAI,EAAE,OAAO,SAAS,SAAU,QAAO;AACvC,YAAM,EAAE,CAAC,OAAO,KAAK,GAAG,GAAG,GAAG,KAAK,IAAI;AACvC,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,IAAM,SAAS,CAAC,OAAuB,WACrC,OAAO,SAAS,QAAS,OAAO,YAAY,MAAM,MAAM,QAAQ,EAAE,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAK,OAAO,YAAY,MAAM,aAAa,QAAQ,EAAE,GAAG,OAAO,YAAY,OAAO,QAAQ;AAErL,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AACF,IAA2B,CAAC,GAA2H;AACrJ,QAAM,UAAU,UAAU,4BAA4B;AACtD,QAAM,OAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,KAAK;AAC5C,QAAM,QAAQ,YAA6C,EAAE,KAAK,eAAe,mBAAmB,KAAK,GAAG,YAAY,UAAU,GAAG,MAAM;AAC3I,MAAI,WAAW;AACf,QAAM,OAAO,oBAAI,QAAmD;AACpE,QAAM,QAAQ,aAAgD;AAAA,IAC5D,YAAY,MAAM,MAAM,YAAY,EAAE;AAAA,IACtC,KAAK,MAAM,MAAM,YAAY,EAAE;AAAA,IAC/B,UAAU,MAAM;AAAA,IAChB,MAAM,MAAM;AACV,cAAQ,MAAM,KAAK,UAAU,IAAI,CAAC;AAClC,YAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF,CAAC;AACD,QAAM,UAAU,MAAM;AACtB,QAAM,SAAS,CAAC,WAA4B,MAAM,IAAI,gBAAgB,QAAQ,GAAG,MAAM,GAAG,OAAO,OAAO,oBAAoB,OAAO,IAAI,GAAG,EAAE,OAAO,OAAO,SAAS,UAAU,SAAY,OAAO,MAAM,CAAC,CAAC;AAExM,SAAO;AAAA,IACL,MAAM,CAAC,UAAU,KAAK,OAAO,EAAE,MAAM,QAAQ,OAAO,KAAK,KAAK,IAAI,EAAE,CAAC;AAAA,IACrE,QAAQ,CAAC,UAAU,KAAK,OAAO,EAAE,MAAM,UAAU,MAAM,CAAC;AAAA,IACxD,OAAO,MAAM,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAAA,IAC1C,IAAI,GAAG;AACL,YAAM,QAAQ,QAAQ;AACtB,aAAO;AAAA,QACL,OAAO,MAAM,OAAO,MAAM,oBAAI,IAAI,CAAC;AAAA,QACnC;AAAA;AAAA,QAEA,MACE,OAAO,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EACtE,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,MACrB;AAAA,IACF;AAAA,IACA,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,EAAE,OAAO;AAC/B,aAAO,QAAQ,QAAQ,OAAO,KAAK,IAAI,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,WAAW,qBAAqB,MAAM,WAAW,MAAM,MAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,EAAE,CAAC,CAAC;AAAA,IACvG,QAAQ,MAAM,SAAS;AACrB,iBAAW;AACX,YAAM,SAAS,EAAE,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IACnD;AAAA,EACF;AACF;AAGO,SAAS,qBAAqB,WAAiD,MAAkB;AACtG,MAAI,SAAS;AACb,SAAO,CAAC,aAAyB;AAC/B,UAAM,cAAc,UAAU,QAAQ;AACtC,QAAI,CAAC,QAAQ;AACX,eAAS;AACT,WAAK;AAAA,IACP;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAM,UAAU,CAAC,UAA2C,OAAQ,OAAyB,UAAU,YAAY,OAAQ,MAAwB,aAAa;AAEhK,SAAS,MAAM,KAA2C;AACxD,MAAI;AACF,UAAM,QAAiB,MAAM,KAAK,MAAM,GAAG,IAAI;AAC/C,WAAO,SAAS,OAAO,UAAU,WAAW,OAAO,YAAY,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,IAAI;AAAA,EAChI,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,IAAM,iBAAiB,MAAO,OAAO,WAAW,cAAc,SAAY,OAAO;AAE1E,SAAS,UAAU,KAAoE;AAC5F,MAAI,SAAwB;AAC5B,SAAO;AAAA,IACL,OAAO;AACL,UAAI;AACF,eAAO,eAAe,GAAG,QAAQ,GAAG,KAAK;AAAA,MAC3C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM,OAAO;AACX,eAAS;AACT,UAAI;AACF,uBAAe,GAAG,QAAQ,KAAK,KAAK;AAAA,MACtC,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/frequents.ts"],"sourcesContent":["import { controllable, createChangeDetails, type ChangeDetails } from \"./change\";\nimport { cached, createStore } from \"./reactive\";\n\n// count is the use count decayed by half every 14 days, as of lastUsed\nexport type FrequentEntry = { count: number; lastUsed: number };\nexport type Frequents = Readonly<Record<string, FrequentEntry>>;\nexport type FrequentsChangeDetails = ChangeDetails<\"pick\" | \"remove\" | \"clear\"> & { emoji?: string };\n\nexport interface FrequencyStore {\n pick(unified: string): void;\n remove(unified: string): void;\n clear(): void;\n top(n: number): readonly string[];\n score(unified: string): number;\n entries(): Frequents;\n subscribe(listener: () => void): () => void;\n}\n\ntype FrequencyStoreOptions = {\n frequents?: Frequents;\n defaultFrequents?: Frequents;\n onFrequentsChange?: (frequents: Frequents, details: FrequentsChangeDetails) => void;\n deferStorage?: boolean;\n};\n\nconst HALF_LIFE = 14 * 864e5;\nconst MAX = 64;\n\nconst NONE: Frequents = {};\n\nconst decayed = ({ count, lastUsed }: FrequentEntry, now: number) => count * 0.5 ** ((now - lastUsed) / HALF_LIFE);\n// Ties go to the later use: digit-only keys such as \"2753\" would otherwise sort first by object key order\nconst ranked = (entries: Frequents, now: number) => Object.entries(entries).sort((a, b) => decayed(b[1], now) - decayed(a[1], now) || b[1].lastUsed - a[1].lastUsed);\n// The new pick stays: it would rank last against a full list of older uses and never build up a count\nconst capped = (entries: Frequents, now: number, keep: string): Frequents => {\n const top = ranked(entries, now).slice(0, MAX);\n return Object.fromEntries(top.some(([u]) => u === keep) ? top : [...top.slice(0, -1), [keep, entries[keep]!]]);\n};\n\ntype FrequentsAction = { type: \"pick\"; emoji: string; now: number } | { type: \"remove\"; emoji: string } | { type: \"clear\" };\n\nfunction reduceFrequents(entries: Frequents, action: FrequentsAction): Frequents {\n switch (action.type) {\n case \"pick\": {\n const prev = entries[action.emoji];\n return capped({ ...entries, [action.emoji]: { count: (prev ? decayed(prev, action.now) : 0) + 1, lastUsed: action.now } }, action.now, action.emoji);\n }\n case \"remove\": {\n if (!(action.emoji in entries)) return entries;\n const { [action.emoji]: _, ...rest } = entries;\n return rest;\n }\n case \"clear\":\n return NONE;\n default:\n return action satisfies never;\n }\n}\n\ntype FrequencyState = { own: Frequents; controlled: Frequents | undefined };\ntype FrequencyAction = { type: \"own\"; entries: Frequents } | { type: \"control\"; entries: Frequents | undefined };\n\nconst reduce = (state: FrequencyState, action: FrequencyAction): FrequencyState =>\n action.type === \"own\" ? (action.entries === state.own ? state : { ...state, own: action.entries }) : action.entries === state.controlled ? state : { ...state, controlled: action.entries };\n\nexport function createFrequencyStore({\n frequents,\n defaultFrequents = NONE,\n onFrequentsChange,\n deferStorage,\n}: FrequencyStoreOptions = {}): FrequencyStore & { control(value: Frequents | undefined, onChange: FrequencyStoreOptions[\"onFrequentsChange\"]): void } {\n const storage = persisted(\"emoji-components:frequents\");\n const read = () => parse(storage.read()) ?? defaultFrequents;\n const store = createStore<FrequencyState, FrequencyAction>({ own: deferStorage ? defaultFrequents : read(), controlled: frequents }, reduce);\n let onChange = onFrequentsChange;\n const tops = new WeakMap<Frequents, Map<number, readonly string[]>>();\n const value = controllable<Frequents, FrequentsChangeDetails>({\n controlled: () => store.getSnapshot().controlled,\n own: () => store.getSnapshot().own,\n onChange: () => onChange,\n write(next) {\n storage.write(JSON.stringify(next));\n store.dispatch({ type: \"own\", entries: next });\n },\n });\n const entries = value.value;\n const change = (action: FrequentsAction) => value.set(reduceFrequents(entries(), action), Object.assign(createChangeDetails(action.type), { emoji: action.type === \"clear\" ? undefined : action.emoji }));\n\n return {\n pick: (emoji) => void change({ type: \"pick\", emoji, now: Date.now() }),\n remove: (emoji) => void change({ type: \"remove\", emoji }),\n clear: () => void change({ type: \"clear\" }),\n top(n) {\n const state = entries();\n return cached(\n cached(tops, state, () => new Map()),\n n,\n // Decay keeps the order at any reference time; the newest use avoids reading the clock during render\n () =>\n ranked(state, Math.max(0, ...Object.values(state).map((e) => e.lastUsed)))\n .slice(0, n)\n .map(([u]) => u),\n );\n },\n score(unified) {\n const entry = entries()[unified];\n return entry ? decayed(entry, Date.now()) : 0;\n },\n entries,\n subscribe: readOnFirstSubscribe(store.subscribe, () => store.dispatch({ type: \"own\", entries: read() })),\n control(next, handler) {\n onChange = handler;\n store.dispatch({ type: \"control\", entries: next });\n },\n };\n}\n\n// A server-rendered page hydrates with the defaults; storage is read once something subscribes after mount\nexport function readOnFirstSubscribe(subscribe: (listener: () => void) => () => void, sync: () => void) {\n let synced = false;\n return (listener: () => void) => {\n const unsubscribe = subscribe(listener);\n if (!synced) {\n synced = true;\n sync();\n }\n return unsubscribe;\n };\n}\n\nconst isEntry = (value: unknown): value is FrequentEntry => typeof (value as FrequentEntry)?.count === \"number\" && typeof (value as FrequentEntry).lastUsed === \"number\";\n\nfunction parse(raw: string | null): Frequents | undefined {\n try {\n const value: unknown = raw ? JSON.parse(raw) : undefined;\n return value && typeof value === \"object\" ? Object.fromEntries(Object.entries(value).filter(([, entry]) => isEntry(entry))) : undefined;\n } catch {\n return undefined;\n }\n}\n\n// Node 25+ defines a localStorage global (a warning, or a file shared by every request); only a browser's is per user\nconst browserStorage = () => (typeof window === \"undefined\" ? undefined : window.localStorage);\n\nexport function persisted(key: string): { read(): string | null; write(value: string): void } {\n let memory: string | null = null;\n return {\n read() {\n try {\n return browserStorage()?.getItem(key) ?? memory;\n } catch {\n return memory;\n }\n },\n write(value) {\n memory = value;\n try {\n browserStorage()?.setItem(key, value);\n } catch {}\n },\n };\n}\n"],"mappings":";AAAA,SAAS,cAAc,2BAA+C;AACtE,SAAS,QAAQ,mBAAmB;AAwBpC,IAAM,YAAY,KAAK;AACvB,IAAM,MAAM;AAEZ,IAAM,OAAkB,CAAC;AAEzB,IAAM,UAAU,CAAC,EAAE,OAAO,SAAS,GAAkB,QAAgB,QAAQ,SAAS,MAAM,YAAY;AAExG,IAAM,SAAS,CAAC,SAAoB,QAAgB,OAAO,QAAQ,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,EAAE,CAAC,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ;AAEnK,IAAM,SAAS,CAAC,SAAoB,KAAa,SAA4B;AAC3E,QAAM,MAAM,OAAO,SAAS,GAAG,EAAE,MAAM,GAAG,GAAG;AAC7C,SAAO,OAAO,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,MAAM,IAAI,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,QAAQ,IAAI,CAAE,CAAC,CAAC;AAC/G;AAIA,SAAS,gBAAgB,SAAoB,QAAoC;AAC/E,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,QAAQ;AACX,YAAM,OAAO,QAAQ,OAAO,KAAK;AACjC,aAAO,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,QAAQ,MAAM,OAAO,GAAG,IAAI,KAAK,GAAG,UAAU,OAAO,IAAI,EAAE,GAAG,OAAO,KAAK,OAAO,KAAK;AAAA,IACrJ;AAAA,IACA,KAAK,UAAU;AACb,UAAI,EAAE,OAAO,SAAS,SAAU,QAAO;AACvC,YAAM,EAAE,CAAC,OAAO,KAAK,GAAG,GAAG,GAAG,KAAK,IAAI;AACvC,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,IAAM,SAAS,CAAC,OAAuB,WACrC,OAAO,SAAS,QAAS,OAAO,YAAY,MAAM,MAAM,QAAQ,EAAE,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAK,OAAO,YAAY,MAAM,aAAa,QAAQ,EAAE,GAAG,OAAO,YAAY,OAAO,QAAQ;AAErL,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AACF,IAA2B,CAAC,GAA2H;AACrJ,QAAM,UAAU,UAAU,4BAA4B;AACtD,QAAM,OAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,KAAK;AAC5C,QAAM,QAAQ,YAA6C,EAAE,KAAK,eAAe,mBAAmB,KAAK,GAAG,YAAY,UAAU,GAAG,MAAM;AAC3I,MAAI,WAAW;AACf,QAAM,OAAO,oBAAI,QAAmD;AACpE,QAAM,QAAQ,aAAgD;AAAA,IAC5D,YAAY,MAAM,MAAM,YAAY,EAAE;AAAA,IACtC,KAAK,MAAM,MAAM,YAAY,EAAE;AAAA,IAC/B,UAAU,MAAM;AAAA,IAChB,MAAM,MAAM;AACV,cAAQ,MAAM,KAAK,UAAU,IAAI,CAAC;AAClC,YAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF,CAAC;AACD,QAAM,UAAU,MAAM;AACtB,QAAM,SAAS,CAAC,WAA4B,MAAM,IAAI,gBAAgB,QAAQ,GAAG,MAAM,GAAG,OAAO,OAAO,oBAAoB,OAAO,IAAI,GAAG,EAAE,OAAO,OAAO,SAAS,UAAU,SAAY,OAAO,MAAM,CAAC,CAAC;AAExM,SAAO;AAAA,IACL,MAAM,CAAC,UAAU,KAAK,OAAO,EAAE,MAAM,QAAQ,OAAO,KAAK,KAAK,IAAI,EAAE,CAAC;AAAA,IACrE,QAAQ,CAAC,UAAU,KAAK,OAAO,EAAE,MAAM,UAAU,MAAM,CAAC;AAAA,IACxD,OAAO,MAAM,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAAA,IAC1C,IAAI,GAAG;AACL,YAAM,QAAQ,QAAQ;AACtB,aAAO;AAAA,QACL,OAAO,MAAM,OAAO,MAAM,oBAAI,IAAI,CAAC;AAAA,QACnC;AAAA;AAAA,QAEA,MACE,OAAO,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EACtE,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAAA,MACrB;AAAA,IACF;AAAA,IACA,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,EAAE,OAAO;AAC/B,aAAO,QAAQ,QAAQ,OAAO,KAAK,IAAI,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,WAAW,qBAAqB,MAAM,WAAW,MAAM,MAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,EAAE,CAAC,CAAC;AAAA,IACvG,QAAQ,MAAM,SAAS;AACrB,iBAAW;AACX,YAAM,SAAS,EAAE,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA,IACnD;AAAA,EACF;AACF;AAGO,SAAS,qBAAqB,WAAiD,MAAkB;AACtG,MAAI,SAAS;AACb,SAAO,CAAC,aAAyB;AAC/B,UAAM,cAAc,UAAU,QAAQ;AACtC,QAAI,CAAC,QAAQ;AACX,eAAS;AACT,WAAK;AAAA,IACP;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAM,UAAU,CAAC,UAA2C,OAAQ,OAAyB,UAAU,YAAY,OAAQ,MAAwB,aAAa;AAEhK,SAAS,MAAM,KAA2C;AACxD,MAAI;AACF,UAAM,QAAiB,MAAM,KAAK,MAAM,GAAG,IAAI;AAC/C,WAAO,SAAS,OAAO,UAAU,WAAW,OAAO,YAAY,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,IAAI;AAAA,EAChI,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,IAAM,iBAAiB,MAAO,OAAO,WAAW,cAAc,SAAY,OAAO;AAE1E,SAAS,UAAU,KAAoE;AAC5F,MAAI,SAAwB;AAC5B,SAAO;AAAA,IACL,OAAO;AACL,UAAI;AACF,eAAO,eAAe,GAAG,QAAQ,GAAG,KAAK;AAAA,MAC3C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM,OAAO;AACX,eAAS;AACT,UAAI;AACF,uBAAe,GAAG,QAAQ,KAAK,KAAK;AAAA,MACtC,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { E as EmojiStore, C as CompletionOrder, a as ShortcodeCompletion, b as EmojiSelection, c as ChangeDetails, d as EmojiLabels, e as CategoryOption, f as SearchResultsMode, g as Category, h as EmojiSection, i as EmojiItem } from './sections-Bwe4sMgE.js';
1
+ import { E as EmojiStore, C as CompletionOrder, a as ShortcodeCompletion, b as EmojiSelection, c as ChangeDetails, d as EmojiLabels, e as CategoryOption, f as SearchResultsMode, g as Category, h as EmojiSection, i as EmojiItem } from './sections-D7PEWAVf.js';
2
2
  import { Subscribe } from './internal.js';
3
- export { j as CustomEmoji, k as CustomEmojiLoader, l as CustomEmojiSource, m as EmojiDataSource, n as EmojiInput, o as EmojiSearch, p as EmojiStoreOptions, q as EmojiStoreStatus, F as Frequents, r as FrequentsChangeDetails, R as Renderer, s as ResolvedEmoji, t as SkinToneChangeDetails, u as createEmojiStore, v as findShortcodeTrigger, w as isComposingKey, x as lexicalSearch, y as localeData, z as resolveEmoji, A as twemojiUrl } from './sections-Bwe4sMgE.js';
3
+ export { j as CustomEmoji, k as CustomEmojiLoader, l as CustomEmojiSource, m as EmojiDataSource, n as EmojiInput, o as EmojiSearch, p as EmojiStoreOptions, q as EmojiStoreStatus, F as Frequents, r as FrequentsChangeDetails, R as Renderer, s as ResolvedEmoji, t as SkinToneChangeDetails, u as createEmojiStore, v as findShortcodeTrigger, w as isComposingKey, x as lexicalSearch, y as localeData, z as resolveEmoji, A as twemojiUrl } from './sections-D7PEWAVf.js';
4
4
  import { E as EmojiRecord, S as SkinToneValue } from './dataset-BQY5KQKL.js';
5
5
  export { D as Dataset, a as EmojiDataInput, n as nativeToUnified, u as unifiedToNative, w as withTone, b as withoutTone } from './dataset-BQY5KQKL.js';
6
6
 
@@ -13,13 +13,13 @@ interface EmojiSearchQuery {
13
13
  query: string;
14
14
  limit: number;
15
15
  }
16
- interface EmojiSearchRunner {
16
+ interface EmojiSearchStore {
17
17
  getSnapshot(): EmojiSearchState;
18
18
  subscribe: Subscribe;
19
19
  setQuery(query: string, limit: number): void;
20
20
  abort(): void;
21
21
  }
22
- declare function createSearch(store: EmojiStore, initial?: EmojiSearchQuery): EmojiSearchRunner;
22
+ declare function createEmojiSearch(store: EmojiStore, initial?: EmojiSearchQuery): EmojiSearchStore;
23
23
 
24
24
  interface KeyLike {
25
25
  key: string;
@@ -97,6 +97,8 @@ type HighlightChangeDetails = ChangeDetails<"keyboard" | "pointer" | "imperative
97
97
  };
98
98
  type QueryChangeDetails = ChangeDetails<"input-change" | "clear-press" | "escape-key" | "imperative-action" | "none">;
99
99
  type CollapsedCategoriesChangeDetails = ChangeDetails<"trigger-press" | "none">;
100
+ type ValueChangeDetails = ChangeDetails<"item-press" | "imperative-action">;
101
+ type ReactionsOpenChangeDetails = ChangeDetails<"trigger-press" | "item-press" | "escape-key" | "none">;
100
102
  interface ChangeSource<D extends ChangeDetails> {
101
103
  reason?: D["reason"];
102
104
  event?: Event;
@@ -104,8 +106,7 @@ interface ChangeSource<D extends ChangeDetails> {
104
106
  interface EmojiPickerOptions<Icon = unknown> {
105
107
  labels?: EmojiLabels;
106
108
  categories?: readonly CategoryOption<Icon>[];
107
- frequent?: number | false;
108
- suggested?: readonly string[];
109
+ frequent?: number | false | readonly string[];
109
110
  emojiVersion?: number;
110
111
  drawable?: (record: EmojiRecord, unified: string) => boolean;
111
112
  searchResults?: SearchResultsMode;
@@ -119,8 +120,15 @@ interface EmojiPickerOptions<Icon = unknown> {
119
120
  highlight?: string | null;
120
121
  defaultHighlight?: string | null;
121
122
  onHighlightChange?: (unified: string | null, details: HighlightChangeDetails) => void;
123
+ value?: string | null;
124
+ defaultValue?: string | null;
125
+ onValueChange?: (unified: string | null, details: ValueChangeDetails) => void;
126
+ onSelect?: (selection: EmojiSelection, details: ValueChangeDetails) => void;
122
127
  disableVariations?: boolean;
123
128
  reactions?: readonly string[] | number;
129
+ reactionsOpen?: boolean;
130
+ defaultReactionsOpen?: boolean;
131
+ onReactionsOpenChange?: (open: boolean, details: ReactionsOpenChangeDetails) => void;
124
132
  }
125
133
  type FocusMode = "virtual" | "roving";
126
134
  interface EmojiPickerSnapshot<Icon = unknown> {
@@ -141,6 +149,8 @@ interface EmojiPickerSnapshot<Icon = unknown> {
141
149
  activeSection: string | null;
142
150
  variations: string | null;
143
151
  reactions: readonly string[] | undefined;
152
+ reactionsOpen: boolean;
153
+ value: string | null | undefined;
144
154
  tabStop: string | null;
145
155
  pressed: string | null;
146
156
  }
@@ -160,6 +170,8 @@ interface EmojiPickerStore<Icon = unknown> {
160
170
  setTabStop(id: string | null): void;
161
171
  press(id: string | null): void;
162
172
  choose(id?: string | null): EmojiSelection | undefined;
173
+ pick(id?: string | null, source?: ChangeSource<ValueChangeDetails>): EmojiSelection | undefined;
174
+ setReactionsOpen(open: boolean, source?: ChangeSource<ReactionsOpenChangeDetails>): ReactionsOpenChangeDetails | undefined;
163
175
  openVariations(id: string | null): boolean;
164
176
  closeVariations(): void;
165
177
  registerInput(): () => void;
@@ -169,5 +181,5 @@ interface EmojiPickerStore<Icon = unknown> {
169
181
  }
170
182
  declare function createEmojiPicker<Icon = unknown>(store: EmojiStore, options?: EmojiPickerOptions<Icon>): EmojiPickerStore<Icon>;
171
183
 
172
- export { Category, CategoryOption, ChangeDetails, CompletionOrder, EmojiItem, EmojiLabels, EmojiRecord, EmojiSection, EmojiSelection, EmojiStore, SearchResultsMode, ShortcodeCompletion, SkinToneValue, createEmojiPicker, createEmojiSuggestions, createSearch };
173
- export type { CollapsedCategoriesChangeDetails, EmojiPickerOptions, EmojiPickerSnapshot, EmojiPickerStore, EmojiSearchState, EmojiSuggestionsOptions, EmojiSuggestionsStore, EmojiTrigger, HighlightChangeDetails, KeyLike, QueryChangeDetails };
184
+ export { Category, CategoryOption, ChangeDetails, CompletionOrder, EmojiItem, EmojiLabels, EmojiRecord, EmojiSection, EmojiSelection, EmojiStore, SearchResultsMode, ShortcodeCompletion, SkinToneValue, createEmojiPicker, createEmojiSearch, createEmojiSuggestions };
185
+ export type { CollapsedCategoriesChangeDetails, EmojiPickerOptions, EmojiPickerSnapshot, EmojiPickerStore, EmojiSearchState, EmojiSearchStore, EmojiSuggestionsOptions, EmojiSuggestionsStore, EmojiTrigger, HighlightChangeDetails, KeyLike, QueryChangeDetails, ReactionsOpenChangeDetails, ValueChangeDetails };
package/dist/index.js CHANGED
@@ -3,16 +3,16 @@ import { createEmojiStore } from "./store.js";
3
3
  import { localeData, twemojiUrl } from "./platform.js";
4
4
  import { nativeToUnified, unifiedToNative, withTone, withoutTone } from "./dataset.js";
5
5
  import { lexicalSearch } from "./search/search.js";
6
- import { createSearch } from "./search/query.js";
6
+ import { createEmojiSearch } from "./search/query.js";
7
7
  import { resolveEmoji } from "./resolve.js";
8
8
  import { findShortcodeTrigger, isComposingKey } from "./shortcodes.js";
9
9
  import { createEmojiSuggestions } from "./suggestions.js";
10
10
  import { createEmojiPicker } from "./picker/picker.js";
11
11
  export {
12
12
  createEmojiPicker,
13
+ createEmojiSearch,
13
14
  createEmojiStore,
14
15
  createEmojiSuggestions,
15
- createSearch,
16
16
  findShortcodeTrigger,
17
17
  isComposingKey,
18
18
  lexicalSearch,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createEmojiStore } from \"./store\";\nexport type { CustomEmoji, CustomEmojiLoader, CustomEmojiSource, EmojiDataSource, EmojiSelection, EmojiStore, EmojiStoreOptions, EmojiStoreStatus, SkinToneChangeDetails } from \"./store\";\nexport type { ChangeDetails } from \"./change\";\nexport type { Frequents, FrequentsChangeDetails } from \"./frequents\";\nexport { localeData, twemojiUrl } from \"./platform\";\nexport type { Renderer } from \"./platform\";\nexport { nativeToUnified, unifiedToNative, withTone, withoutTone } from \"./dataset\";\nexport type { Dataset, EmojiDataInput, EmojiRecord, SkinToneValue } from \"./dataset\";\nexport { lexicalSearch } from \"./search/search\";\nexport type { EmojiSearch } from \"./search/search\";\nexport type { CompletionOrder, ShortcodeCompletion } from \"./search/search-index\";\nexport { createSearch } from \"./search/query\";\nexport type { EmojiSearchState } from \"./search/query\";\nexport type { EmojiLabels } from \"./labels\";\nexport { resolveEmoji } from \"./resolve\";\nexport type { EmojiInput, ResolvedEmoji } from \"./resolve\";\nexport { findShortcodeTrigger, isComposingKey } from \"./shortcodes\";\nexport { createEmojiSuggestions } from \"./suggestions\";\nexport type { EmojiSuggestionsStore, EmojiSuggestionsOptions, EmojiTrigger, KeyLike } from \"./suggestions\";\nexport { createEmojiPicker } from \"./picker/picker\";\nexport type { CollapsedCategoriesChangeDetails, EmojiPickerStore, EmojiPickerOptions, EmojiPickerSnapshot, HighlightChangeDetails, QueryChangeDetails } from \"./picker/picker\";\nexport type { Category, CategoryOption, EmojiItem, EmojiSection, SearchResultsMode } from \"./picker/sections\";\n"],"mappings":";AAAA,SAAS,wBAAwB;AAIjC,SAAS,YAAY,kBAAkB;AAEvC,SAAS,iBAAiB,iBAAiB,UAAU,mBAAmB;AAExE,SAAS,qBAAqB;AAG9B,SAAS,oBAAoB;AAG7B,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB,sBAAsB;AACrD,SAAS,8BAA8B;AAEvC,SAAS,yBAAyB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createEmojiStore } from \"./store\";\nexport type { CustomEmoji, CustomEmojiLoader, CustomEmojiSource, EmojiDataSource, EmojiSelection, EmojiStore, EmojiStoreOptions, EmojiStoreStatus, SkinToneChangeDetails } from \"./store\";\nexport type { ChangeDetails } from \"./change\";\nexport type { Frequents, FrequentsChangeDetails } from \"./frequents\";\nexport { localeData, twemojiUrl } from \"./platform\";\nexport type { Renderer } from \"./platform\";\nexport { nativeToUnified, unifiedToNative, withTone, withoutTone } from \"./dataset\";\nexport type { Dataset, EmojiDataInput, EmojiRecord, SkinToneValue } from \"./dataset\";\nexport { lexicalSearch } from \"./search/search\";\nexport type { EmojiSearch } from \"./search/search\";\nexport type { CompletionOrder, ShortcodeCompletion } from \"./search/search-index\";\nexport { createEmojiSearch } from \"./search/query\";\nexport type { EmojiSearchState, EmojiSearchStore } from \"./search/query\";\nexport type { EmojiLabels } from \"./labels\";\nexport { resolveEmoji } from \"./resolve\";\nexport type { EmojiInput, ResolvedEmoji } from \"./resolve\";\nexport { findShortcodeTrigger, isComposingKey } from \"./shortcodes\";\nexport { createEmojiSuggestions } from \"./suggestions\";\nexport type { EmojiSuggestionsStore, EmojiSuggestionsOptions, EmojiTrigger, KeyLike } from \"./suggestions\";\nexport { createEmojiPicker } from \"./picker/picker\";\nexport type { CollapsedCategoriesChangeDetails, EmojiPickerStore, EmojiPickerOptions, EmojiPickerSnapshot, HighlightChangeDetails, QueryChangeDetails, ReactionsOpenChangeDetails, ValueChangeDetails } from \"./picker/picker\";\nexport type { Category, CategoryOption, EmojiItem, EmojiSection, SearchResultsMode } from \"./picker/sections\";\n"],"mappings":";AAAA,SAAS,wBAAwB;AAIjC,SAAS,YAAY,kBAAkB;AAEvC,SAAS,iBAAiB,iBAAiB,UAAU,mBAAmB;AAExE,SAAS,qBAAqB;AAG9B,SAAS,yBAAyB;AAGlC,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB,sBAAsB;AACrD,SAAS,8BAA8B;AAEvC,SAAS,yBAAyB;","names":[]}
@@ -1,6 +1,6 @@
1
- import { S as Subscribe } from './sections-Bwe4sMgE.js';
1
+ import { S as Subscribe } from './sections-D7PEWAVf.js';
2
2
  import { EmojiSection } from './index.js';
3
- export { T as TWEMOJI_EMOJI_VERSION, B as createChangeDetails, D as customRecord, G as isCustomEmoji, H as localizeLabels, I as missingEmojiText, J as nameWithTone, K as shallowEqual, L as subscribeAll } from './sections-Bwe4sMgE.js';
3
+ export { T as TWEMOJI_EMOJI_VERSION, B as createChangeDetails, D as customRecord, G as isCustomEmoji, H as localizeLabels, I as missingEmojiText, J as nameWithTone, K as shallowEqual, L as subscribeAll } from './sections-D7PEWAVf.js';
4
4
  export { c as SKIN_TONE_VALUES, i as isUnified } from './dataset-BQY5KQKL.js';
5
5
  import { EmojiGrid, EmojiGridMove } from './grid.js';
6
6
  export { P as PROBE_BATCH, o as onNativeSupportChange } from './native-B0Oa7IDL.js';
package/dist/labels.js CHANGED
@@ -21,11 +21,12 @@ var defaultLabels = {
21
21
  emojiList: "Emoji",
22
22
  resultCount: plural("en", COUNT),
23
23
  categoryNavigation: "Category navigation",
24
- suggested: "Frequently used",
24
+ frequent: "Frequently used",
25
25
  custom: "Custom",
26
+ suggestions: "Emoji suggestions",
26
27
  reactions: "Reactions",
27
28
  expandReactions: "More emoji",
28
- previewCaption: "What's your mood?",
29
+ previewCaption: "Pick an emoji",
29
30
  skinTone: "Skin tone",
30
31
  skinTones: ["Default skin tone", "Light skin tone", "Medium-light skin tone", "Medium skin tone", "Medium-dark skin tone", "Dark skin tone"],
31
32
  withSkinTone: (name, tone) => `${name}: ${TONE_SUFFIXES[tone - 1]}`
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/labels.ts"],"sourcesContent":["type PluralForms = { other: string } & Partial<Record<Intl.LDMLPluralRule | `=${number}`, string>>;\n\n// The default resultCount; apps plug their own i18n library into labels.resultCount\nexport function plural(locale: string, forms: PluralForms): (count: number) => string {\n const rules = new Intl.PluralRules(locale);\n const number = new Intl.NumberFormat(locale);\n return (count) => {\n const form = forms[`=${count}`] ?? forms[rules.select(count)] ?? forms.other;\n return form.replaceAll(\"#\", number.format(count));\n };\n}\n\nexport type SkinToneNames = readonly [string, string, string, string, string, string];\n\nexport type EmojiLabels = {\n picker: string;\n searchPlaceholder: string;\n searchLabel: string;\n searchClear: string;\n searchResults: string;\n emojiList: string;\n resultCount: (count: number) => string;\n categoryNavigation: string;\n suggested: string;\n custom: string;\n reactions: string;\n expandReactions: string;\n previewCaption: string;\n skinTone: string;\n skinTones: SkinToneNames;\n withSkinTone: (name: string, tone: number) => string;\n};\n\nconst COUNT: PluralForms = { \"=0\": \"No results\", one: \"# result\", other: \"# results\" };\n\n// English, with the app's overrides: a locale alone doesn't translate the strings, so it doesn't change their plural rules either\nexport function localizeLabels(overrides?: Partial<EmojiLabels>): EmojiLabels {\n return overrides ? { ...defaultLabels, ...overrides } : defaultLabels;\n}\n\nconst TONE_SUFFIXES = [\"light skin tone\", \"medium-light skin tone\", \"medium skin tone\", \"medium-dark skin tone\", \"dark skin tone\"];\n\nexport const defaultLabels: EmojiLabels = {\n picker: \"Emoji picker\",\n searchPlaceholder: \"Search\",\n searchLabel: \"Type to search for an emoji\",\n searchClear: \"Clear\",\n searchResults: \"Search results\",\n emojiList: \"Emoji\",\n resultCount: plural(\"en\", COUNT),\n categoryNavigation: \"Category navigation\",\n suggested: \"Frequently used\",\n custom: \"Custom\",\n reactions: \"Reactions\",\n expandReactions: \"More emoji\",\n previewCaption: \"What's your mood?\",\n skinTone: \"Skin tone\",\n skinTones: [\"Default skin tone\", \"Light skin tone\", \"Medium-light skin tone\", \"Medium skin tone\", \"Medium-dark skin tone\", \"Dark skin tone\"],\n withSkinTone: (name, tone) => `${name}: ${TONE_SUFFIXES[tone - 1]}`,\n};\n"],"mappings":";AAGO,SAAS,OAAO,QAAgB,OAA+C;AACpF,QAAM,QAAQ,IAAI,KAAK,YAAY,MAAM;AACzC,QAAM,SAAS,IAAI,KAAK,aAAa,MAAM;AAC3C,SAAO,CAAC,UAAU;AAChB,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC,KAAK,MAAM;AACvE,WAAO,KAAK,WAAW,KAAK,OAAO,OAAO,KAAK,CAAC;AAAA,EAClD;AACF;AAuBA,IAAM,QAAqB,EAAE,MAAM,cAAc,KAAK,YAAY,OAAO,YAAY;AAG9E,SAAS,eAAe,WAA+C;AAC5E,SAAO,YAAY,EAAE,GAAG,eAAe,GAAG,UAAU,IAAI;AAC1D;AAEA,IAAM,gBAAgB,CAAC,mBAAmB,0BAA0B,oBAAoB,yBAAyB,gBAAgB;AAE1H,IAAM,gBAA6B;AAAA,EACxC,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,aAAa,OAAO,MAAM,KAAK;AAAA,EAC/B,oBAAoB;AAAA,EACpB,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,WAAW,CAAC,qBAAqB,mBAAmB,0BAA0B,oBAAoB,yBAAyB,gBAAgB;AAAA,EAC3I,cAAc,CAAC,MAAM,SAAS,GAAG,IAAI,KAAK,cAAc,OAAO,CAAC,CAAC;AACnE;","names":[]}
1
+ {"version":3,"sources":["../src/labels.ts"],"sourcesContent":["type PluralForms = { other: string } & Partial<Record<Intl.LDMLPluralRule | `=${number}`, string>>;\n\n// The default resultCount; apps plug their own i18n library into labels.resultCount\nexport function plural(locale: string, forms: PluralForms): (count: number) => string {\n const rules = new Intl.PluralRules(locale);\n const number = new Intl.NumberFormat(locale);\n return (count) => {\n const form = forms[`=${count}`] ?? forms[rules.select(count)] ?? forms.other;\n return form.replaceAll(\"#\", number.format(count));\n };\n}\n\nexport type SkinToneNames = readonly [string, string, string, string, string, string];\n\nexport type EmojiLabels = {\n picker: string;\n searchPlaceholder: string;\n searchLabel: string;\n searchClear: string;\n searchResults: string;\n emojiList: string;\n resultCount: (count: number) => string;\n categoryNavigation: string;\n frequent: string;\n custom: string;\n suggestions: string;\n reactions: string;\n expandReactions: string;\n previewCaption: string;\n skinTone: string;\n skinTones: SkinToneNames;\n withSkinTone: (name: string, tone: number) => string;\n};\n\nconst COUNT: PluralForms = { \"=0\": \"No results\", one: \"# result\", other: \"# results\" };\n\n// English, with the app's overrides: a locale alone doesn't translate the strings, so it doesn't change their plural rules either\nexport function localizeLabels(overrides?: Partial<EmojiLabels>): EmojiLabels {\n return overrides ? { ...defaultLabels, ...overrides } : defaultLabels;\n}\n\nconst TONE_SUFFIXES = [\"light skin tone\", \"medium-light skin tone\", \"medium skin tone\", \"medium-dark skin tone\", \"dark skin tone\"];\n\nexport const defaultLabels: EmojiLabels = {\n picker: \"Emoji picker\",\n searchPlaceholder: \"Search\",\n searchLabel: \"Type to search for an emoji\",\n searchClear: \"Clear\",\n searchResults: \"Search results\",\n emojiList: \"Emoji\",\n resultCount: plural(\"en\", COUNT),\n categoryNavigation: \"Category navigation\",\n frequent: \"Frequently used\",\n custom: \"Custom\",\n suggestions: \"Emoji suggestions\",\n reactions: \"Reactions\",\n expandReactions: \"More emoji\",\n previewCaption: \"Pick an emoji\",\n skinTone: \"Skin tone\",\n skinTones: [\"Default skin tone\", \"Light skin tone\", \"Medium-light skin tone\", \"Medium skin tone\", \"Medium-dark skin tone\", \"Dark skin tone\"],\n withSkinTone: (name, tone) => `${name}: ${TONE_SUFFIXES[tone - 1]}`,\n};\n"],"mappings":";AAGO,SAAS,OAAO,QAAgB,OAA+C;AACpF,QAAM,QAAQ,IAAI,KAAK,YAAY,MAAM;AACzC,QAAM,SAAS,IAAI,KAAK,aAAa,MAAM;AAC3C,SAAO,CAAC,UAAU;AAChB,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC,KAAK,MAAM;AACvE,WAAO,KAAK,WAAW,KAAK,OAAO,OAAO,KAAK,CAAC;AAAA,EAClD;AACF;AAwBA,IAAM,QAAqB,EAAE,MAAM,cAAc,KAAK,YAAY,OAAO,YAAY;AAG9E,SAAS,eAAe,WAA+C;AAC5E,SAAO,YAAY,EAAE,GAAG,eAAe,GAAG,UAAU,IAAI;AAC1D;AAEA,IAAM,gBAAgB,CAAC,mBAAmB,0BAA0B,oBAAoB,yBAAyB,gBAAgB;AAE1H,IAAM,gBAA6B;AAAA,EACxC,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,aAAa,OAAO,MAAM,KAAK;AAAA,EAC/B,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,WAAW,CAAC,qBAAqB,mBAAmB,0BAA0B,oBAAoB,yBAAyB,gBAAgB;AAAA,EAC3I,cAAc,CAAC,MAAM,SAAS,GAAG,IAAI,KAAK,cAAc,OAAO,CAAC,CAAC;AACnE;","names":[]}
@@ -2,7 +2,7 @@
2
2
  import { controllable, createChangeDetails } from "../change.js";
3
3
  import { withoutTone } from "../dataset.js";
4
4
  import { createStore, memo, patch, shallowEqual, stable, subscribeAll } from "../reactive.js";
5
- import { createSearch } from "../search/query.js";
5
+ import { createEmojiSearch } from "../search/query.js";
6
6
  import {
7
7
  categoryList,
8
8
  categorySections,
@@ -58,7 +58,9 @@ function createEmojiPicker(store, options = {}) {
58
58
  domFocus: false,
59
59
  held: null,
60
60
  tabStop: null,
61
- pressed: null
61
+ pressed: null,
62
+ value: options.defaultValue ?? null,
63
+ reactionsOpen: options.defaultReactionsOpen ?? true
62
64
  },
63
65
  patch
64
66
  );
@@ -66,12 +68,13 @@ function createEmojiPicker(store, options = {}) {
66
68
  const opts = () => input.getSnapshot().options;
67
69
  const searchOf = (state, current) => ({ query: queryOf(state, current), limit: current.searchLimit ?? DEFAULT_SEARCH_LIMIT });
68
70
  const initial = searchOf(ui.getSnapshot(), options);
69
- const search = createSearch(store, initial);
71
+ const search = createEmojiSearch(store, initial);
70
72
  const syncSearch = () => {
71
73
  const { query: query2, limit } = searchOf(ui.getSnapshot(), opts());
72
74
  search.setQuery(query2, limit);
73
75
  };
74
76
  let kept = { query: "" };
77
+ const tracks = (current) => options.defaultValue !== void 0 || current.value !== void 0 || current.onValueChange !== void 0;
75
78
  const items = memo(itemFactory);
76
79
  const categoriesOf = memo(
77
80
  (groupNames, customGroups, categories, labels) => categoryList(
@@ -82,7 +85,7 @@ function createEmojiPicker(store, options = {}) {
82
85
  )
83
86
  );
84
87
  const sectionsOf = memo(
85
- (records, customGroups, ready, categories, suggested, collapsed2, emojiVersion, tone, item) => ready ? categorySections({ categories, records, suggested, customGroup: (u) => customGroups.get(u), collapsed: collapsed2, emojiVersion, get: store.get, tone, item }) : NO_SECTIONS
88
+ (records, customGroups, ready, categories, frequent, collapsed2, emojiVersion, tone, item) => ready ? categorySections({ categories, records, frequent, customGroup: (u) => customGroups.get(u), collapsed: collapsed2, emojiVersion, get: store.get, tone, item }) : NO_SECTIONS
86
89
  );
87
90
  const resultsOf = memo((results, emojiVersion, drawable, tone) => {
88
91
  const shown = emojiVersion == null ? results : results.filter(shownIn(emojiVersion));
@@ -94,7 +97,10 @@ function createEmojiPicker(store, options = {}) {
94
97
  });
95
98
  const activeOf = memo(findItem);
96
99
  const keep = stable();
97
- const frequentCount = () => (opts().frequent ?? DEFAULT_FREQUENT) || 1;
100
+ const frequentCount = () => {
101
+ const { frequent = DEFAULT_FREQUENT } = opts();
102
+ return typeof frequent === "number" ? frequent || 1 : 1;
103
+ };
98
104
  const reactionsOf = (reactions) => typeof reactions === "number" ? store.frequent(reactions) : reactions;
99
105
  const select = memo(
100
106
  (state, current, ready, records, tone, frequent, found, storeLabels) => {
@@ -106,12 +112,13 @@ function createEmojiPicker(store, options = {}) {
106
112
  const searching = query2.trim().length > 0;
107
113
  const results = resultsOf(found.results, current.emojiVersion, current.drawable, tone);
108
114
  const categories = categoriesOf(groupNames, customGroups, current.categories, labels);
109
- const suggested = current.suggested ?? (current.frequent === false ? NO_IDS : frequent);
115
+ const shownFrequent = Array.isArray(current.frequent) ? current.frequent : current.frequent === false ? NO_IDS : frequent;
110
116
  const collapsed2 = collapsedOf(state, current);
111
- const categorySections2 = sectionsOf(records, customGroups, ready, categories, suggested, collapsed2, current.emojiVersion, tone, item);
117
+ const categorySections2 = sectionsOf(records, customGroups, ready, categories, shownFrequent, collapsed2, current.emojiVersion, tone, item);
112
118
  const sections = searching ? resultSectionsOf(results, current.searchResults ?? "ranked", categories, labels, groupNames, tone, item) : categorySections2;
113
119
  const highlighted2 = resolveKey(sections, current.highlight === void 0 ? state.highlighted : current.highlight, state.highlighted);
114
120
  const virtual = (state.inputs > 0 || !state.mounted) && !state.domFocus;
121
+ const reactions = state.held && typeof current.reactions === "number" ? state.held.reactions : reactionsOf(current.reactions);
115
122
  return keep({
116
123
  query: query2,
117
124
  searching,
@@ -129,7 +136,10 @@ function createEmojiPicker(store, options = {}) {
129
136
  active: activeOf(sections, highlighted2 ?? state.hovered),
130
137
  activeSection: state.activeSection,
131
138
  variations: state.variations,
132
- reactions: state.held ? state.held.reactions : reactionsOf(current.reactions),
139
+ reactions,
140
+ // An empty row, such as frequently used before any pick, shows the picker instead
141
+ reactionsOpen: Boolean(reactions?.length) && (current.reactionsOpen ?? state.reactionsOpen),
142
+ value: tracks(current) ? current.value === void 0 ? state.value : current.value : void 0,
133
143
  tabStop: state.tabStop,
134
144
  pressed: state.pressed
135
145
  });
@@ -137,14 +147,14 @@ function createEmojiPicker(store, options = {}) {
137
147
  );
138
148
  function getSnapshot() {
139
149
  const state = ui.getSnapshot();
140
- return select(state, opts(), store.status() === "ready", store.records(), store.skinTone(), state.held?.suggested ?? store.frequent(frequentCount()), search.getSnapshot(), store.labels());
150
+ return select(state, opts(), store.status() === "ready", store.records(), store.skinTone(), state.held?.frequent ?? store.frequent(frequentCount()), search.getSnapshot(), store.labels());
141
151
  }
142
152
  const set = (fields) => ui.dispatch(fields);
143
153
  const query = controllable({
144
154
  controlled: () => opts().query,
145
155
  own: () => ui.getSnapshot().query,
146
156
  onChange: () => opts().onQueryChange,
147
- write: (value) => set({ query: value })
157
+ write: (value2) => set({ query: value2 })
148
158
  });
149
159
  const collapsed = controllable({
150
160
  controlled: () => input.getSnapshot().draft ?? opts().collapsedCategories,
@@ -163,21 +173,33 @@ function createEmojiPicker(store, options = {}) {
163
173
  },
164
174
  write: (key) => set({ highlighted: key })
165
175
  });
166
- const changeCollapsed = (id, value, { reason = "none", event }) => void collapsed.set(setCategoryCollapsed(collapsed.value(), id, value), createChangeDetails(reason, event));
167
- return {
176
+ const value = controllable({
177
+ controlled: () => opts().value,
178
+ own: () => ui.getSnapshot().value,
179
+ onChange: () => opts().onValueChange,
180
+ write: (unified) => set({ value: unified })
181
+ });
182
+ const reactionsOpen = controllable({
183
+ controlled: () => opts().reactionsOpen,
184
+ own: () => ui.getSnapshot().reactionsOpen,
185
+ onChange: () => opts().onReactionsOpenChange,
186
+ write: (open) => set({ reactionsOpen: open })
187
+ });
188
+ const changeCollapsed = (id, value2, { reason = "none", event }) => void collapsed.set(setCategoryCollapsed(collapsed.value(), id, value2), createChangeDetails(reason, event));
189
+ const self = {
168
190
  getSnapshot,
169
191
  subscribe: subscribeAll(ui.subscribe, input.subscribe, store.subscribe, (listener) => store.frequents.subscribe(() => !ui.getSnapshot().held && listener()), search.subscribe),
170
192
  setOptions(next) {
171
193
  input.dispatch({ type: "options", options: next });
172
194
  syncSearch();
173
195
  },
174
- setQuery(value, { reason = "none", event } = {}) {
175
- const details = query.set(value, createChangeDetails(reason, event));
196
+ setQuery(value2, { reason = "none", event } = {}) {
197
+ const details = query.set(value2, createChangeDetails(reason, event));
176
198
  syncSearch();
177
199
  return details;
178
200
  },
179
201
  toggleCollapsed: (id, source = {}) => changeCollapsed(id, void 0, source),
180
- setCollapsed: (id, value, source = {}) => changeCollapsed(id, value, source),
202
+ setCollapsed: (id, value2, source = {}) => changeCollapsed(id, value2, source),
181
203
  highlight(emoji, { reason = "none", event } = {}) {
182
204
  const { sections, highlighted: current, searching } = getSnapshot();
183
205
  const key = resolveKey(sections, emoji, current);
@@ -200,6 +222,21 @@ function createEmojiPicker(store, options = {}) {
200
222
  setTabStop: (id) => set({ tabStop: id }),
201
223
  press: (id) => set({ pressed: id }),
202
224
  choose: (id = getSnapshot().highlighted) => id == null ? void 0 : store.select(findItem(getSnapshot().sections, id)?.unified ?? id),
225
+ pick(id, { reason = "item-press", event } = {}) {
226
+ const selection = self.choose(id);
227
+ if (!selection) return void 0;
228
+ const details = createChangeDetails(reason, event);
229
+ opts().onSelect?.(selection, details);
230
+ if (details.isCanceled) return selection;
231
+ if (tracks(opts())) value.set(selection.unified, createChangeDetails(reason, event));
232
+ if (opts().reactions && !reactionsOpen.value()) self.setReactionsOpen(true, { reason: "item-press", event });
233
+ return selection;
234
+ },
235
+ setReactionsOpen(open, { reason = "none", event } = {}) {
236
+ const details = reactionsOpen.set(open, createChangeDetails(reason, event));
237
+ if (open && details && !details.isCanceled) self.setQuery("");
238
+ return details;
239
+ },
203
240
  openVariations(id) {
204
241
  const record = findItem(getSnapshot().sections, id)?.record;
205
242
  if (!record?.tones.length || record.imageUrl || opts().disableVariations) return false;
@@ -212,12 +249,13 @@ function createEmojiPicker(store, options = {}) {
212
249
  return () => set({ inputs: ui.getSnapshot().inputs - 1 });
213
250
  },
214
251
  mount: () => set({ mounted: true }),
215
- setDomFocus: (value) => set({ domFocus: value }),
216
- hold(value) {
217
- if (Boolean(ui.getSnapshot().held) === value) return;
218
- set({ held: value ? { suggested: store.frequent(frequentCount()), reactions: reactionsOf(opts().reactions) } : null });
252
+ setDomFocus: (value2) => set({ domFocus: value2 }),
253
+ hold(value2) {
254
+ if (Boolean(ui.getSnapshot().held) === value2) return;
255
+ set({ held: value2 ? { frequent: store.frequent(frequentCount()), reactions: reactionsOf(opts().reactions) } : null });
219
256
  }
220
257
  };
258
+ return self;
221
259
  }
222
260
  export {
223
261
  createEmojiPicker