@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 +4 -4
- package/dist/build.js +1 -1
- package/dist/{chunk-UG4NPOXS.js → chunk-MJPQEHGK.js} +5 -2
- package/dist/cli.js +1 -1
- package/dist/frequents.js +1 -2
- package/dist/frequents.js.map +1 -1
- package/dist/index.d.ts +20 -8
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/labels.js +3 -2
- package/dist/labels.js.map +1 -1
- package/dist/picker/picker.js +57 -19
- package/dist/picker/picker.js.map +1 -1
- package/dist/picker/sections.js +4 -4
- package/dist/picker/sections.js.map +1 -1
- package/dist/platform.js +20 -3
- package/dist/platform.js.map +1 -1
- package/dist/search/query.js +2 -2
- package/dist/search/query.js.map +1 -1
- package/dist/search/search-index.js +3 -3
- package/dist/search/search-index.js.map +1 -1
- package/dist/search/tokenize.js +4 -1
- package/dist/search/tokenize.js.map +1 -1
- package/dist/{sections-Bwe4sMgE.d.ts → sections-D7PEWAVf.d.ts} +3 -2
- package/dist/store.js +5 -4
- package/dist/store.js.map +1 -1
- package/dist/suggestions.js +2 -2
- package/dist/suggestions.js.map +1 -1
- package/package.json +4 -3
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
|
|
25
|
-
input.addEventListener("keydown", (e) =>
|
|
26
|
-
const stop =
|
|
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`, `
|
|
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
|
@@ -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
|
|
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
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 =
|
|
33
|
+
defaultFrequents = NONE,
|
|
35
34
|
onFrequentsChange,
|
|
36
35
|
deferStorage
|
|
37
36
|
} = {}) {
|
package/dist/frequents.js.map
CHANGED
|
@@ -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":"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","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":"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","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-
|
|
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-
|
|
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
|
|
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
|
|
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,
|
|
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 {
|
|
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 {
|
|
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":[]}
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as Subscribe } from './sections-
|
|
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-
|
|
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
|
-
|
|
24
|
+
frequent: "Frequently used",
|
|
25
25
|
custom: "Custom",
|
|
26
|
+
suggestions: "Emoji suggestions",
|
|
26
27
|
reactions: "Reactions",
|
|
27
28
|
expandReactions: "More emoji",
|
|
28
|
-
previewCaption: "
|
|
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]}`
|
package/dist/labels.js.map
CHANGED
|
@@ -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
|
|
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":[]}
|
package/dist/picker/picker.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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,
|
|
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 = () =>
|
|
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
|
|
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,
|
|
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
|
|
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?.
|
|
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: (
|
|
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
|
|
167
|
-
|
|
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(
|
|
175
|
-
const details = query.set(
|
|
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,
|
|
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: (
|
|
216
|
-
hold(
|
|
217
|
-
if (Boolean(ui.getSnapshot().held) ===
|
|
218
|
-
set({ held:
|
|
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
|