@emoji-components/core 1.0.0-alpha.1 → 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 +8 -7
- 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 +26 -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 +65 -22
- 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/reactive.js +5 -4
- package/dist/reactive.js.map +1 -1
- package/dist/search/query.js +9 -9
- 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-BfhdYJn0.d.ts → sections-D7PEWAVf.d.ts} +4 -3
- package/dist/store.js +12 -10
- package/dist/store.js.map +1 -1
- package/dist/suggestions.js +4 -4
- package/dist/suggestions.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -13,28 +13,29 @@ npm i @emoji-components/core
|
|
|
13
13
|
```ts
|
|
14
14
|
import { createEmojiPicker, createEmojiStore, createEmojiSuggestions } from "@emoji-components/core";
|
|
15
15
|
|
|
16
|
+
const input = document.querySelector("input")!;
|
|
16
17
|
const store = createEmojiStore();
|
|
17
18
|
await store.load();
|
|
18
19
|
|
|
19
20
|
const picker = createEmojiPicker(store, { frequent: 16 });
|
|
20
|
-
picker.subscribe(() =>
|
|
21
|
+
picker.subscribe(() => console.log(picker.getSnapshot().sections));
|
|
21
22
|
picker.setQuery("cat");
|
|
22
23
|
|
|
23
|
-
const
|
|
24
|
-
input.addEventListener("keydown", (e) =>
|
|
25
|
-
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);
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
`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.
|
|
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.
|
|
29
30
|
|
|
30
31
|
## Entry points
|
|
31
32
|
|
|
32
33
|
| Import | Contents |
|
|
33
34
|
|---|---|
|
|
34
|
-
| `@emoji-components/core` | `createEmojiStore`, `createEmojiPicker`, `createEmojiSuggestions`, `
|
|
35
|
+
| `@emoji-components/core` | `createEmojiStore`, `createEmojiPicker`, `createEmojiSuggestions`, `createEmojiSearch`, `lexicalSearch`, `resolveEmoji`, `findShortcodeTrigger`, `isComposingKey`, `localeData`, `twemojiUrl`, unified-code helpers |
|
|
35
36
|
| `/grid` | Grid layout and keyboard moves, for your own virtualizer |
|
|
36
37
|
| `/native` | Whether the system font draws an emoji |
|
|
37
|
-
| `/build` | `buildDataset` from `emojibase-data
|
|
38
|
+
| `/build` | `buildDataset` from `emojibase-data` (install it alongside), also as the `npx @emoji-components/core build-data <locale>` CLI |
|
|
38
39
|
|
|
39
40
|
## Docs
|
|
40
41
|
|
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":";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,28 +1,32 @@
|
|
|
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
|
|
|
7
7
|
interface EmojiSearchState {
|
|
8
8
|
results: readonly EmojiRecord[];
|
|
9
9
|
pending: boolean;
|
|
10
|
+
error: Error | null;
|
|
10
11
|
}
|
|
11
12
|
interface EmojiSearchQuery {
|
|
12
13
|
query: string;
|
|
13
14
|
limit: number;
|
|
14
15
|
}
|
|
15
|
-
interface
|
|
16
|
+
interface EmojiSearchStore {
|
|
16
17
|
getSnapshot(): EmojiSearchState;
|
|
17
18
|
subscribe: Subscribe;
|
|
18
19
|
setQuery(query: string, limit: number): void;
|
|
19
20
|
abort(): void;
|
|
20
21
|
}
|
|
21
|
-
declare function
|
|
22
|
+
declare function createEmojiSearch(store: EmojiStore, initial?: EmojiSearchQuery): EmojiSearchStore;
|
|
22
23
|
|
|
23
24
|
interface KeyLike {
|
|
24
25
|
key: string;
|
|
25
26
|
shiftKey?: boolean;
|
|
27
|
+
metaKey?: boolean;
|
|
28
|
+
ctrlKey?: boolean;
|
|
29
|
+
altKey?: boolean;
|
|
26
30
|
isComposing?: boolean;
|
|
27
31
|
keyCode?: number;
|
|
28
32
|
preventDefault(): void;
|
|
@@ -93,6 +97,8 @@ type HighlightChangeDetails = ChangeDetails<"keyboard" | "pointer" | "imperative
|
|
|
93
97
|
};
|
|
94
98
|
type QueryChangeDetails = ChangeDetails<"input-change" | "clear-press" | "escape-key" | "imperative-action" | "none">;
|
|
95
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">;
|
|
96
102
|
interface ChangeSource<D extends ChangeDetails> {
|
|
97
103
|
reason?: D["reason"];
|
|
98
104
|
event?: Event;
|
|
@@ -100,9 +106,9 @@ interface ChangeSource<D extends ChangeDetails> {
|
|
|
100
106
|
interface EmojiPickerOptions<Icon = unknown> {
|
|
101
107
|
labels?: EmojiLabels;
|
|
102
108
|
categories?: readonly CategoryOption<Icon>[];
|
|
103
|
-
frequent?: number | false;
|
|
104
|
-
suggested?: readonly string[];
|
|
109
|
+
frequent?: number | false | readonly string[];
|
|
105
110
|
emojiVersion?: number;
|
|
111
|
+
drawable?: (record: EmojiRecord, unified: string) => boolean;
|
|
106
112
|
searchResults?: SearchResultsMode;
|
|
107
113
|
searchLimit?: number;
|
|
108
114
|
query?: string;
|
|
@@ -114,8 +120,15 @@ interface EmojiPickerOptions<Icon = unknown> {
|
|
|
114
120
|
highlight?: string | null;
|
|
115
121
|
defaultHighlight?: string | null;
|
|
116
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;
|
|
117
127
|
disableVariations?: boolean;
|
|
118
128
|
reactions?: readonly string[] | number;
|
|
129
|
+
reactionsOpen?: boolean;
|
|
130
|
+
defaultReactionsOpen?: boolean;
|
|
131
|
+
onReactionsOpenChange?: (open: boolean, details: ReactionsOpenChangeDetails) => void;
|
|
119
132
|
}
|
|
120
133
|
type FocusMode = "virtual" | "roving";
|
|
121
134
|
interface EmojiPickerSnapshot<Icon = unknown> {
|
|
@@ -123,6 +136,7 @@ interface EmojiPickerSnapshot<Icon = unknown> {
|
|
|
123
136
|
searching: boolean;
|
|
124
137
|
results: readonly EmojiRecord[];
|
|
125
138
|
pending: boolean;
|
|
139
|
+
searchError: Error | null;
|
|
126
140
|
categories: readonly Category<Icon>[];
|
|
127
141
|
categorySections: readonly EmojiSection[];
|
|
128
142
|
sections: readonly EmojiSection[];
|
|
@@ -135,6 +149,8 @@ interface EmojiPickerSnapshot<Icon = unknown> {
|
|
|
135
149
|
activeSection: string | null;
|
|
136
150
|
variations: string | null;
|
|
137
151
|
reactions: readonly string[] | undefined;
|
|
152
|
+
reactionsOpen: boolean;
|
|
153
|
+
value: string | null | undefined;
|
|
138
154
|
tabStop: string | null;
|
|
139
155
|
pressed: string | null;
|
|
140
156
|
}
|
|
@@ -154,6 +170,8 @@ interface EmojiPickerStore<Icon = unknown> {
|
|
|
154
170
|
setTabStop(id: string | null): void;
|
|
155
171
|
press(id: string | null): void;
|
|
156
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;
|
|
157
175
|
openVariations(id: string | null): boolean;
|
|
158
176
|
closeVariations(): void;
|
|
159
177
|
registerInput(): () => void;
|
|
@@ -163,5 +181,5 @@ interface EmojiPickerStore<Icon = unknown> {
|
|
|
163
181
|
}
|
|
164
182
|
declare function createEmojiPicker<Icon = unknown>(store: EmojiStore, options?: EmojiPickerOptions<Icon>): EmojiPickerStore<Icon>;
|
|
165
183
|
|
|
166
|
-
export { Category, CategoryOption, ChangeDetails, CompletionOrder, EmojiItem, EmojiLabels, EmojiRecord, EmojiSection, EmojiSelection, EmojiStore, SearchResultsMode, ShortcodeCompletion, SkinToneValue, createEmojiPicker,
|
|
167
|
-
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,
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
itemFactory,
|
|
12
12
|
resultSections,
|
|
13
13
|
setCategoryCollapsed,
|
|
14
|
-
shownIn
|
|
14
|
+
shownIn,
|
|
15
|
+
toned
|
|
15
16
|
} from "./sections.js";
|
|
16
17
|
var reduceInput = (input, action) => {
|
|
17
18
|
switch (action.type) {
|
|
@@ -57,7 +58,9 @@ function createEmojiPicker(store, options = {}) {
|
|
|
57
58
|
domFocus: false,
|
|
58
59
|
held: null,
|
|
59
60
|
tabStop: null,
|
|
60
|
-
pressed: null
|
|
61
|
+
pressed: null,
|
|
62
|
+
value: options.defaultValue ?? null,
|
|
63
|
+
reactionsOpen: options.defaultReactionsOpen ?? true
|
|
61
64
|
},
|
|
62
65
|
patch
|
|
63
66
|
);
|
|
@@ -65,12 +68,13 @@ function createEmojiPicker(store, options = {}) {
|
|
|
65
68
|
const opts = () => input.getSnapshot().options;
|
|
66
69
|
const searchOf = (state, current) => ({ query: queryOf(state, current), limit: current.searchLimit ?? DEFAULT_SEARCH_LIMIT });
|
|
67
70
|
const initial = searchOf(ui.getSnapshot(), options);
|
|
68
|
-
const search =
|
|
71
|
+
const search = createEmojiSearch(store, initial);
|
|
69
72
|
const syncSearch = () => {
|
|
70
73
|
const { query: query2, limit } = searchOf(ui.getSnapshot(), opts());
|
|
71
74
|
search.setQuery(query2, limit);
|
|
72
75
|
};
|
|
73
76
|
let kept = { query: "" };
|
|
77
|
+
const tracks = (current) => options.defaultValue !== void 0 || current.value !== void 0 || current.onValueChange !== void 0;
|
|
74
78
|
const items = memo(itemFactory);
|
|
75
79
|
const categoriesOf = memo(
|
|
76
80
|
(groupNames, customGroups, categories, labels) => categoryList(
|
|
@@ -81,16 +85,22 @@ function createEmojiPicker(store, options = {}) {
|
|
|
81
85
|
)
|
|
82
86
|
);
|
|
83
87
|
const sectionsOf = memo(
|
|
84
|
-
(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
|
|
85
89
|
);
|
|
86
|
-
const resultsOf = memo((results, emojiVersion) =>
|
|
90
|
+
const resultsOf = memo((results, emojiVersion, drawable, tone) => {
|
|
91
|
+
const shown = emojiVersion == null ? results : results.filter(shownIn(emojiVersion));
|
|
92
|
+
return drawable ? shown.filter((r) => drawable(r, toned(r, tone))) : shown;
|
|
93
|
+
});
|
|
87
94
|
const resultSectionsOf = memo((results, mode, categories, labels, groupNames, tone, item) => {
|
|
88
95
|
const name = (group) => categories.find((c) => c.id === group)?.name ?? (group === "custom" ? labels.custom : groupName(groupNames, group));
|
|
89
96
|
return resultSections(results, mode, name, tone, item, labels.searchResults);
|
|
90
97
|
});
|
|
91
98
|
const activeOf = memo(findItem);
|
|
92
99
|
const keep = stable();
|
|
93
|
-
const frequentCount = () =>
|
|
100
|
+
const frequentCount = () => {
|
|
101
|
+
const { frequent = DEFAULT_FREQUENT } = opts();
|
|
102
|
+
return typeof frequent === "number" ? frequent || 1 : 1;
|
|
103
|
+
};
|
|
94
104
|
const reactionsOf = (reactions) => typeof reactions === "number" ? store.frequent(reactions) : reactions;
|
|
95
105
|
const select = memo(
|
|
96
106
|
(state, current, ready, records, tone, frequent, found, storeLabels) => {
|
|
@@ -100,19 +110,21 @@ function createEmojiPicker(store, options = {}) {
|
|
|
100
110
|
const customGroups = store.customGroups();
|
|
101
111
|
const query2 = queryOf(state, current);
|
|
102
112
|
const searching = query2.trim().length > 0;
|
|
103
|
-
const results = resultsOf(found.results, current.emojiVersion);
|
|
113
|
+
const results = resultsOf(found.results, current.emojiVersion, current.drawable, tone);
|
|
104
114
|
const categories = categoriesOf(groupNames, customGroups, current.categories, labels);
|
|
105
|
-
const
|
|
115
|
+
const shownFrequent = Array.isArray(current.frequent) ? current.frequent : current.frequent === false ? NO_IDS : frequent;
|
|
106
116
|
const collapsed2 = collapsedOf(state, current);
|
|
107
|
-
const categorySections2 = sectionsOf(records, customGroups, ready, categories,
|
|
117
|
+
const categorySections2 = sectionsOf(records, customGroups, ready, categories, shownFrequent, collapsed2, current.emojiVersion, tone, item);
|
|
108
118
|
const sections = searching ? resultSectionsOf(results, current.searchResults ?? "ranked", categories, labels, groupNames, tone, item) : categorySections2;
|
|
109
119
|
const highlighted2 = resolveKey(sections, current.highlight === void 0 ? state.highlighted : current.highlight, state.highlighted);
|
|
110
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);
|
|
111
122
|
return keep({
|
|
112
123
|
query: query2,
|
|
113
124
|
searching,
|
|
114
125
|
results,
|
|
115
126
|
pending: found.pending,
|
|
127
|
+
searchError: found.error,
|
|
116
128
|
categories,
|
|
117
129
|
categorySections: categorySections2,
|
|
118
130
|
sections,
|
|
@@ -124,7 +136,10 @@ function createEmojiPicker(store, options = {}) {
|
|
|
124
136
|
active: activeOf(sections, highlighted2 ?? state.hovered),
|
|
125
137
|
activeSection: state.activeSection,
|
|
126
138
|
variations: state.variations,
|
|
127
|
-
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,
|
|
128
143
|
tabStop: state.tabStop,
|
|
129
144
|
pressed: state.pressed
|
|
130
145
|
});
|
|
@@ -132,14 +147,14 @@ function createEmojiPicker(store, options = {}) {
|
|
|
132
147
|
);
|
|
133
148
|
function getSnapshot() {
|
|
134
149
|
const state = ui.getSnapshot();
|
|
135
|
-
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());
|
|
136
151
|
}
|
|
137
152
|
const set = (fields) => ui.dispatch(fields);
|
|
138
153
|
const query = controllable({
|
|
139
154
|
controlled: () => opts().query,
|
|
140
155
|
own: () => ui.getSnapshot().query,
|
|
141
156
|
onChange: () => opts().onQueryChange,
|
|
142
|
-
write: (
|
|
157
|
+
write: (value2) => set({ query: value2 })
|
|
143
158
|
});
|
|
144
159
|
const collapsed = controllable({
|
|
145
160
|
controlled: () => input.getSnapshot().draft ?? opts().collapsedCategories,
|
|
@@ -158,21 +173,33 @@ function createEmojiPicker(store, options = {}) {
|
|
|
158
173
|
},
|
|
159
174
|
write: (key) => set({ highlighted: key })
|
|
160
175
|
});
|
|
161
|
-
const
|
|
162
|
-
|
|
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 = {
|
|
163
190
|
getSnapshot,
|
|
164
191
|
subscribe: subscribeAll(ui.subscribe, input.subscribe, store.subscribe, (listener) => store.frequents.subscribe(() => !ui.getSnapshot().held && listener()), search.subscribe),
|
|
165
192
|
setOptions(next) {
|
|
166
193
|
input.dispatch({ type: "options", options: next });
|
|
167
194
|
syncSearch();
|
|
168
195
|
},
|
|
169
|
-
setQuery(
|
|
170
|
-
const details = query.set(
|
|
196
|
+
setQuery(value2, { reason = "none", event } = {}) {
|
|
197
|
+
const details = query.set(value2, createChangeDetails(reason, event));
|
|
171
198
|
syncSearch();
|
|
172
199
|
return details;
|
|
173
200
|
},
|
|
174
201
|
toggleCollapsed: (id, source = {}) => changeCollapsed(id, void 0, source),
|
|
175
|
-
setCollapsed: (id,
|
|
202
|
+
setCollapsed: (id, value2, source = {}) => changeCollapsed(id, value2, source),
|
|
176
203
|
highlight(emoji, { reason = "none", event } = {}) {
|
|
177
204
|
const { sections, highlighted: current, searching } = getSnapshot();
|
|
178
205
|
const key = resolveKey(sections, emoji, current);
|
|
@@ -195,6 +222,21 @@ function createEmojiPicker(store, options = {}) {
|
|
|
195
222
|
setTabStop: (id) => set({ tabStop: id }),
|
|
196
223
|
press: (id) => set({ pressed: id }),
|
|
197
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
|
+
},
|
|
198
240
|
openVariations(id) {
|
|
199
241
|
const record = findItem(getSnapshot().sections, id)?.record;
|
|
200
242
|
if (!record?.tones.length || record.imageUrl || opts().disableVariations) return false;
|
|
@@ -207,12 +249,13 @@ function createEmojiPicker(store, options = {}) {
|
|
|
207
249
|
return () => set({ inputs: ui.getSnapshot().inputs - 1 });
|
|
208
250
|
},
|
|
209
251
|
mount: () => set({ mounted: true }),
|
|
210
|
-
setDomFocus: (
|
|
211
|
-
hold(
|
|
212
|
-
if (Boolean(ui.getSnapshot().held) ===
|
|
213
|
-
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 });
|
|
214
256
|
}
|
|
215
257
|
};
|
|
258
|
+
return self;
|
|
216
259
|
}
|
|
217
260
|
export {
|
|
218
261
|
createEmojiPicker
|