@devoxin/emoji-picker 1.0.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["useLayoutEffect","useOriginalLayoutEffect","$.object","$.number","$.boolean","$.string","$.naiveArray","$.optional","$.nullable"],"sources":["../src/utils/store.tsx","../src/store.ts","../src/constants.ts","../src/utils/get-skin-tone-variations.ts","../src/hooks.ts","../src/components/emoji-picker/active-emoji.tsx","../src/components/emoji-picker/category-nav.tsx","../src/utils/compare.ts","../src/utils/use-layout-effect.ts","../src/components/emoji-picker/list.tsx","../src/components/emoji-picker/status.tsx","../src/utils/capitalize.ts","../src/utils/is-emoji-supported.ts","../src/utils/storage.ts","../src/utils/validate.ts","../src/data/emoji.ts","../src/utils/chunk.ts","../src/data/emoji-picker.ts","../src/utils/noop.ts","../src/utils/request-idle-callback.ts","../src/utils/use-stable-callback.ts","../src/components/emoji-picker/root.tsx","../src/components/emoji-picker/search.tsx","../src/components/emoji-picker/skin-tone.tsx","../src/components/emoji-picker/viewport.tsx","../src/components/emoji-picker.tsx"],"sourcesContent":["import {\n createContext,\n type PropsWithChildren,\n useCallback,\n useContext,\n useDebugValue,\n useEffect,\n useState,\n} from \"react\";\n\n// A tiny store with batched updates, context support, and selectors.\n\nexport type Store<T> = {\n get: () => T;\n set: (partial: Partial<T> | ((state: T) => Partial<T>)) => void;\n subscribe: (subscriber: (state: T) => void) => () => void;\n};\n\nexport function createStore<T extends object>(\n createInitialState: (set: Store<T>[\"set\"], get: Store<T>[\"get\"]) => T,\n): Store<T> {\n let state = {} as T;\n let pending: T | null = null;\n let frameId: number | null = null;\n const subscribers = new Set<(store: T) => void>();\n\n const flush = () => {\n if (pending) {\n state = pending;\n pending = null;\n\n for (const subscriber of subscribers) {\n subscriber(state);\n }\n }\n\n frameId = null;\n };\n\n const get = () => pending ?? state;\n\n const set: Store<T>[\"set\"] = (partial) => {\n pending ??= state;\n Object.assign(\n pending as T,\n typeof partial === \"function\"\n ? (partial as (state: T) => Partial<T>)(get())\n : partial,\n );\n\n if (!frameId) {\n frameId = requestAnimationFrame(flush);\n }\n };\n\n const subscribe = (subscriber: (state: T) => void) => {\n subscribers.add(subscriber);\n\n return () => subscribers.delete(subscriber);\n };\n\n state = createInitialState(set, get);\n\n return { get, set, subscribe };\n}\n\nexport function useCreateStore<T>(createStore: () => Store<T>) {\n const [store] = useState(createStore);\n\n return store;\n}\n\nexport function createStoreContext<T>(missingProviderError?: string) {\n const Context = createContext<Store<T> | null>(null);\n\n const useStore = () => {\n const store = useContext(Context);\n\n if (!store) {\n throw new Error(missingProviderError);\n }\n\n return store as Store<T>;\n };\n\n const Provider = ({\n store,\n children,\n }: PropsWithChildren<{ store: Store<T> }>) => {\n return <Context.Provider value={store}>{children}</Context.Provider>;\n };\n\n return { useStore, Provider };\n}\n\nexport function useSelector<T, S>(\n store: Store<T>,\n selector: (state: T) => S,\n compare: (a: S, b: S) => boolean = Object.is,\n) {\n const [slice, setSlice] = useState(() => selector(store.get()));\n\n useEffect(() => {\n return store.subscribe(() => {\n const nextSlice = selector(store.get());\n\n setSlice((previousSlice) =>\n compare(previousSlice, nextSlice) ? previousSlice : nextSlice,\n );\n });\n }, [store, selector, compare]);\n\n useDebugValue(slice);\n\n return slice;\n}\n\nexport function useSelectorKey<T, K extends keyof T>(\n store: Store<T>,\n key: K,\n compare?: (a: T[K], b: T[K]) => boolean,\n) {\n const selector = useCallback((state: T) => state[key], [key]);\n\n return useSelector(store, selector, compare);\n}\n","import type { RefObject } from \"react\";\nimport type {\n EmojiPickerData,\n EmojiPickerDataRow,\n EmojiPickerEmoji,\n EmojiPickerRootProps,\n Locale,\n SkinTone,\n} from \"./types\";\nimport { createStore, createStoreContext } from \"./utils/store\";\n\nconst VIEWPORT_OVERSCAN = 2;\nconst VIEWPORT_SCROLL_EPSILON = 1;\n\ntype Interaction = \"keyboard\" | \"pointer\" | \"none\";\n\nexport type EmojiPickerStore = {\n locale: Locale;\n columns: number;\n sticky: boolean;\n skinTone: SkinTone;\n onEmojiSelect: NonNullable<EmojiPickerRootProps[\"onEmojiSelect\"]>;\n\n data: EmojiPickerData | null | undefined;\n search: string;\n interaction: Interaction;\n activeColumnIndex: number;\n activeRowIndex: number;\n\n rowHeight: number | null;\n categoryHeaderHeight: number | null;\n viewportWidth: number | null;\n viewportHeight: number | null;\n\n viewportCurrentCategoryIndex: number;\n viewportStartCategoryIndex: number;\n viewportStartRowIndex: number;\n viewportEndRowIndex: number;\n\n rootRef: RefObject<HTMLDivElement> | null;\n searchRef: RefObject<HTMLInputElement> | null;\n viewportRef: RefObject<HTMLDivElement> | null;\n listRef: RefObject<HTMLDivElement> | null;\n\n updateViewportState: (changes?: Partial<EmojiPickerStore>) => void;\n\n onDataChange: (data: EmojiPickerData) => void;\n onSearchChange: (search: string) => void;\n onActiveEmojiChange: (\n interaction: Exclude<Interaction, \"none\">,\n activeColumnIndex: number,\n activeRowIndex: number,\n ) => void;\n onActiveEmojiReset: () => void;\n onRowHeightChange: (rowHeight: number) => void;\n onCategoryHeaderHeightChange: (categoryHeaderHeight: number) => void;\n onViewportSizeChange: (viewportWidth: number, viewportHeight: number) => void;\n onViewportScroll: (scrollY: number) => void;\n};\n\nexport function createEmojiPickerStore(\n onEmojiSelect: NonNullable<EmojiPickerRootProps[\"onEmojiSelect\"]>,\n initialLocale: Locale,\n initialColumns: number,\n initialSticky: boolean,\n initialSkinTone: SkinTone,\n) {\n let viewportScrollY = 0;\n\n return createStore<EmojiPickerStore>((set, get) => ({\n locale: initialLocale,\n columns: initialColumns,\n sticky: initialSticky,\n skinTone: initialSkinTone,\n onEmojiSelect,\n\n data: null,\n search: \"\",\n interaction: \"none\",\n activeColumnIndex: 0,\n activeRowIndex: 0,\n\n rowHeight: null,\n categoryHeaderHeight: null,\n viewportWidth: null,\n viewportHeight: null,\n\n viewportCurrentCategoryIndex: 0,\n viewportStartCategoryIndex: 0,\n viewportStartRowIndex: 0,\n viewportEndRowIndex: 0,\n\n rootRef: null,\n searchRef: null,\n viewportRef: null,\n listRef: null,\n\n updateViewportState: (partial?: Partial<EmojiPickerStore>) => {\n const state = get();\n\n const data = partial?.data ?? state.data;\n const categoryHeaderHeight =\n partial?.categoryHeaderHeight ?? state.categoryHeaderHeight;\n const rowHeight = partial?.rowHeight ?? state.rowHeight;\n const viewportHeight = partial?.viewportHeight ?? state.viewportHeight;\n\n if (\n !data ||\n data.rows.length === 0 ||\n !categoryHeaderHeight ||\n !rowHeight ||\n !viewportHeight\n ) {\n return set({\n ...partial,\n viewportCurrentCategoryIndex: 0,\n viewportStartCategoryIndex: 0,\n viewportStartRowIndex: 0,\n viewportEndRowIndex: 0,\n });\n }\n\n let previousCategoryHeadersHeight = 0;\n let viewportCurrentCategoryIndex = 0;\n const viewportCurrentY = viewportScrollY + VIEWPORT_SCROLL_EPSILON;\n\n for (\n let categoryIndex = 0;\n categoryIndex < data.categories.length;\n categoryIndex++\n ) {\n const category = data.categories[categoryIndex]!;\n const categoryY =\n categoryIndex * categoryHeaderHeight +\n category.startRowIndex * rowHeight;\n\n if (categoryY <= viewportCurrentY) {\n viewportCurrentCategoryIndex = categoryIndex;\n }\n\n if (categoryY < viewportScrollY) {\n previousCategoryHeadersHeight += categoryHeaderHeight;\n } else {\n break;\n }\n }\n\n const totalHeight =\n data.categories.length * categoryHeaderHeight +\n data.rows.length * rowHeight;\n\n const overscanStart = Math.floor((VIEWPORT_OVERSCAN * rowHeight) / 2);\n const overscanEnd = Math.ceil((VIEWPORT_OVERSCAN * rowHeight) / 2);\n\n // Adjust the scroll position to account for previous category headers\n const viewportStartY = Math.min(\n viewportScrollY - previousCategoryHeadersHeight - overscanStart,\n totalHeight - viewportHeight,\n );\n\n const viewportEndY = viewportStartY + viewportHeight + overscanEnd;\n\n const viewportStartRowIndex = Math.max(\n 0,\n Math.floor(viewportStartY / rowHeight),\n );\n const viewportEndRowIndex = Math.min(\n data.rows.length - 1,\n Math.ceil(viewportEndY / rowHeight),\n );\n const viewportStartCategoryIndex =\n data.rows[viewportStartRowIndex]?.categoryIndex;\n\n if (viewportStartCategoryIndex === undefined && partial) {\n return set({ ...partial, viewportCurrentCategoryIndex });\n }\n\n return set({\n ...partial,\n viewportCurrentCategoryIndex,\n viewportStartCategoryIndex,\n viewportStartRowIndex,\n viewportEndRowIndex,\n });\n },\n\n onDataChange: (data: EmojiPickerData) => {\n get().updateViewportState({\n data,\n\n // Reset active emoji when data changes\n activeColumnIndex: 0,\n activeRowIndex: 0,\n });\n },\n onSearchChange: (search: string) => {\n set({ search, interaction: search ? \"keyboard\" : \"none\" });\n },\n onActiveEmojiChange: (\n interaction: Exclude<Interaction, \"none\">,\n activeColumnIndex: number,\n activeRowIndex: number,\n ) => {\n set({\n interaction,\n activeColumnIndex,\n activeRowIndex,\n });\n\n if (interaction !== \"keyboard\") {\n return;\n }\n\n const {\n listRef,\n viewportRef,\n sticky,\n rowHeight,\n viewportHeight,\n categoryHeaderHeight,\n } = get();\n\n const list = listRef?.current;\n const viewport = viewportRef?.current;\n\n if (\n !list ||\n !viewport ||\n !rowHeight ||\n !categoryHeaderHeight ||\n !viewportHeight\n ) {\n return;\n }\n\n const rowIndex = activeRowIndex;\n\n if (rowIndex === 0) {\n viewport.scrollTo({\n top: 0,\n behavior: \"instant\",\n });\n }\n\n const row = list.querySelector(`[aria-rowindex=\"${rowIndex}\"]`);\n\n if (!(row instanceof HTMLElement)) {\n return;\n }\n\n const rowY = row.offsetTop;\n const rowComputedStyle = getComputedStyle(row);\n const rowScrollMarginTop = Number.parseFloat(\n rowComputedStyle.scrollMarginTop,\n );\n const rowScrollMarginBottom = Number.parseFloat(\n rowComputedStyle.scrollMarginBottom,\n );\n\n let viewportStartY = viewportScrollY + rowScrollMarginTop;\n\n // Account for headers if they are sticky and if the row is in the upper half of the viewport\n if (sticky && rowY < viewportScrollY + viewportHeight / 2) {\n viewportStartY += categoryHeaderHeight;\n }\n\n const viewportEndY =\n viewportStartY + viewportHeight - rowScrollMarginBottom;\n\n if (rowY < viewportStartY || rowY + rowHeight > viewportEndY) {\n viewport.scrollTo({\n // Align to the viewport's top or bottom based on the row's position\n top: Math.max(\n rowY < viewportStartY + categoryHeaderHeight\n ? rowY -\n Math.max(\n sticky ? categoryHeaderHeight : 0,\n rowScrollMarginTop,\n )\n : rowY - viewportHeight + rowHeight + rowScrollMarginBottom,\n 0,\n ),\n behavior: \"instant\",\n });\n }\n },\n onActiveEmojiReset: () => {\n set({\n interaction: \"none\",\n\n // Reset active emoji when interaction goes back to none\n activeColumnIndex: 0,\n activeRowIndex: 0,\n });\n },\n onRowHeightChange: (rowHeight: number) => {\n get().updateViewportState({ rowHeight });\n },\n onCategoryHeaderHeightChange: (categoryHeaderHeight: number) => {\n get().updateViewportState({ categoryHeaderHeight });\n },\n onViewportSizeChange: (viewportWidth: number, viewportHeight: number) => {\n get().updateViewportState({ viewportWidth, viewportHeight });\n },\n onViewportScroll: (scrollY: number) => {\n viewportScrollY = scrollY;\n\n get().updateViewportState();\n },\n }));\n}\n\nexport const {\n useStore: useEmojiPickerStore,\n Provider: EmojiPickerStoreProvider,\n} = createStoreContext<EmojiPickerStore>(\"EmojiPicker.Root is missing.\");\n\nexport function $search(state: EmojiPickerStore) {\n return state.search;\n}\n\nexport function $activeEmoji(\n state: EmojiPickerStore,\n): EmojiPickerEmoji | undefined {\n if (state.interaction === \"none\") {\n return undefined;\n }\n\n const activeEmoji =\n state.data?.rows[state.activeRowIndex]?.emojis[state.activeColumnIndex];\n\n return activeEmoji;\n}\n\nexport function $isEmpty(state: EmojiPickerStore) {\n return (\n state.data === undefined ||\n (typeof state.data?.count === \"number\" && state.data.count === 0)\n );\n}\n\nexport function $isLoading(state: EmojiPickerStore) {\n return (\n state.data === null ||\n state.viewportHeight === null ||\n state.rowHeight === null ||\n state.categoryHeaderHeight === null\n );\n}\n\nexport function $rowsCount(state: EmojiPickerStore) {\n return state.data?.rows.length;\n}\n\nexport function $categoriesCount(state: EmojiPickerStore) {\n return state.data?.categories.length;\n}\n\nexport function $categoriesRowsStartIndices(state: EmojiPickerStore) {\n return state.data?.categoriesStartRowIndices;\n}\n\nexport function $skinTones(state: EmojiPickerStore) {\n return state.data?.skinTones;\n}\n\nexport function sameEmojiPickerEmoji(\n a: EmojiPickerEmoji | undefined,\n b: EmojiPickerEmoji | undefined,\n) {\n return a?.emoji === b?.emoji;\n}\n\nexport function sameEmojiPickerRow(\n a: EmojiPickerDataRow | undefined,\n b: EmojiPickerDataRow | undefined,\n) {\n if (a?.categoryIndex !== b?.categoryIndex) {\n return false;\n }\n\n if (a?.emojis.length !== b?.emojis.length) {\n return false;\n }\n\n return Boolean(\n a?.emojis.every((emoji, index) =>\n sameEmojiPickerEmoji(emoji, b?.emojis[index]),\n ),\n );\n}\n","import type { SkinTone } from \"./types\";\n\nexport const EMOJI_FONT_FAMILY =\n \"'Apple Color Emoji', 'Noto Color Emoji', 'Twemoji Mozilla', 'Android Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', EmojiSymbols, sans-serif\";\n\nexport const SKIN_TONES: SkinTone[] = [\n \"none\",\n \"light\",\n \"medium-light\",\n \"medium\",\n \"medium-dark\",\n \"dark\",\n];\n","import { SKIN_TONES } from \"../constants\";\nimport type { SkinTone, SkinToneVariation } from \"../types\";\n\nconst ZWJ = \"\\u200D\";\nconst EMOJI_MODIFIER_BASE = /\\p{Emoji_Modifier_Base}/u;\nconst ENDING_VARIATION_SELECTOR = /\\uFE0F$/;\nconst SKIN_TONE_MODIFIERS =\n /\\u{1F3FB}|\\u{1F3FC}|\\u{1F3FD}|\\u{1F3FE}|\\u{1F3FF}/gu;\n\nconst skinToneModifiers: Record<Exclude<SkinTone, \"none\">, string> = {\n light: \"\\u{1F3FB}\",\n \"medium-light\": \"\\u{1F3FC}\",\n medium: \"\\u{1F3FD}\",\n \"medium-dark\": \"\\u{1F3FE}\",\n dark: \"\\u{1F3FF}\",\n};\n\nexport function getSkinToneVariation(emoji: string, skinTone: SkinTone) {\n // The emoji does not support skin tones\n if (!emoji.split(ZWJ).some((segment) => EMOJI_MODIFIER_BASE.test(segment))) {\n return emoji;\n }\n\n const baseEmoji = emoji\n .split(ZWJ)\n .map((segment) => segment.replace(SKIN_TONE_MODIFIERS, \"\"))\n .join(ZWJ);\n\n if (skinTone === \"none\") {\n return baseEmoji;\n }\n\n return baseEmoji\n .split(ZWJ)\n .map((segment, _, segments) => {\n const isZwjSequence = segments.length > 1;\n\n if (\n !EMOJI_MODIFIER_BASE.test(segment) ||\n // The 🀝 emoji should not be toned within a ZWJ sequence (e.g. πŸ§‘β€πŸ€β€πŸ§‘)\n (isZwjSequence && segment === \"🀝\")\n ) {\n return segment;\n }\n\n return (\n segment.replace(ENDING_VARIATION_SELECTOR, \"\") +\n skinToneModifiers[skinTone]\n );\n })\n .join(ZWJ);\n}\n\nexport function getSkinToneVariations(emoji: string): SkinToneVariation[] {\n return SKIN_TONES.map((skinTone) => ({\n skinTone,\n emoji: getSkinToneVariation(emoji, skinTone),\n }));\n}\n","import { useCallback, useDeferredValue, useMemo } from \"react\";\nimport type * as EmojiPicker from \"./components/emoji-picker\";\nimport {\n $activeEmoji,\n sameEmojiPickerEmoji,\n useEmojiPickerStore,\n} from \"./store\";\nimport type { Emoji, SkinTone, SkinToneVariation } from \"./types\";\nimport { getSkinToneVariations } from \"./utils/get-skin-tone-variations\";\nimport { useSelector, useSelectorKey } from \"./utils/store\";\n\n/**\n * Returns the currently active emoji (either hovered or selected\n * via keyboard navigation).\n *\n * @example\n * ```tsx\n * const activeEmoji = useActiveEmoji();\n * ```\n *\n * It can be used to build a preview area next to the list.\n *\n * @example\n * ```tsx\n * const activeEmoji = useActiveEmoji();\n *\n * <div>\n * {activeEmoji ? (\n * <span>{activeEmoji.emoji} {activeEmoji.label}</span>\n * ) : (\n * <span>Select an emoji…</span>\n * )}\n * </div>\n * ```\n *\n * @see\n * If you prefer to use a component rather than a hook,\n * {@link EmojiPicker.ActiveEmoji|`<EmojiPicker.ActiveEmoji />`} is also available.\n */\nexport function useActiveEmoji(): Emoji | undefined {\n const store = useEmojiPickerStore();\n const activeEmoji = useSelector(store, $activeEmoji, sameEmojiPickerEmoji);\n\n return useDeferredValue(activeEmoji);\n}\n\n/**\n * Returns the current skin tone and a function to change it.\n *\n * @example\n * ```tsx\n * const [skinTone, setSkinTone] = useSkinTone();\n * ```\n *\n * It can be used to build a custom skin tone selector: pass an emoji\n * you want to use as visual (by default, βœ‹) and it will return its skin tone\n * variations.\n *\n * @example\n * ```tsx\n * const [skinTone, setSkinTone, skinToneVariations] = useSkinTone(\"πŸ‘‹\");\n *\n * // (πŸ‘‹) (πŸ‘‹πŸ») (πŸ‘‹πŸΌ) (πŸ‘‹πŸ½) (πŸ‘‹πŸΎ) (πŸ‘‹πŸΏ)\n * skinToneVariations.map(({ skinTone, emoji }) => (\n * <button key={skinTone} onClick={() => setSkinTone(skinTone)}>\n * {emoji}\n * </button>\n * ));\n * ```\n *\n * @see\n * If you prefer to use a component rather than a hook,\n * {@link EmojiPicker.SkinTone|`<EmojiPicker.SkinTone />`} is also available.\n *\n * @see\n * An already-built skin tone selector is also available,\n * {@link EmojiPicker.SkinToneSelector|`<EmojiPicker.SkinToneSelector />`}.\n */\nexport function useSkinTone(\n emoji = \"βœ‹\",\n): [SkinTone, (skinTone: SkinTone) => void, SkinToneVariation[]] {\n const store = useEmojiPickerStore();\n const skinTone = useSelectorKey(store, \"skinTone\");\n const skinToneVariations = useMemo(\n () => getSkinToneVariations(emoji),\n [emoji],\n );\n\n const setSkinTone = useCallback((skinTone: SkinTone) => {\n store.set({ skinTone });\n }, []);\n\n return [skinTone, setSkinTone, skinToneVariations];\n}\n","import { useActiveEmoji } from \"../../hooks\";\nimport type { EmojiPickerActiveEmojiProps } from \"../../types\";\n\n/**\n * Exposes the currently active emoji (either hovered or selected\n * via keyboard navigation) via a render callback.\n *\n * @example\n * ```tsx\n * <EmojiPicker.ActiveEmoji>\n * {({ emoji }) => <span>{emoji}</span>}\n * </EmojiPicker.ActiveEmoji>\n * ```\n *\n * It can be used to build a preview area next to the list.\n *\n * @example\n * ```tsx\n * <EmojiPicker.ActiveEmoji>\n * {({ emoji }) => (\n * <div>\n * {emoji ? (\n * <span>{emoji.emoji} {emoji.label}</span>\n * ) : (\n * <span>Select an emoji…</span>\n * )}\n * </div>\n * )}\n * </EmojiPicker.ActiveEmoji>\n * ```\n *\n * @see\n * If you prefer to use a hook rather than a component,\n * {@link useActiveEmoji} is also available.\n */\nfunction EmojiPickerActiveEmoji({ children }: EmojiPickerActiveEmojiProps) {\n const activeEmoji = useActiveEmoji();\n\n return children({ emoji: activeEmoji });\n}\n\nexport { EmojiPickerActiveEmoji };\n","import { useMemo } from \"react\";\nimport { useEmojiPickerStore } from \"../../store\";\nimport type { EmojiPickerCategoryNavProps } from \"../../types\";\nimport { useSelectorKey } from \"../../utils/store\";\n\n/**\n * Exposes the emoji categories and provides scroll handlers to navigate\n * to each category via a render callback.\n *\n * @example\n * ```tsx\n * <EmojiPicker.CategoryNav>\n * {({ categories }) => (\n * <div>\n * {categories.map(({ category, scrollTo }) => (\n * <button key={category.label} onClick={scrollTo}>\n * {category.label}\n * </button>\n * ))}\n * </div>\n * )}\n * </EmojiPicker.CategoryNav>\n * ```\n *\n * This component allows building custom category navigation that can scroll\n * to the corresponding section in the emoji list.\n */\nfunction EmojiPickerCategoryNav({\n children,\n}: EmojiPickerCategoryNavProps) {\n const store = useEmojiPickerStore();\n const data = useSelectorKey(store, \"data\");\n const viewportRef = useSelectorKey(store, \"viewportRef\");\n const rowHeight = useSelectorKey(store, \"rowHeight\");\n const categoryHeaderHeight = useSelectorKey(store, \"categoryHeaderHeight\");\n const onViewportScroll = useSelectorKey(store, \"onViewportScroll\");\n const viewportCurrentCategoryIndex = useSelectorKey(\n store,\n \"viewportCurrentCategoryIndex\",\n );\n\n const categories = useMemo(() => {\n if (\n !data?.categories ||\n !viewportRef ||\n !rowHeight ||\n !categoryHeaderHeight\n ) {\n return [];\n }\n\n return data.categories.map((category, index) => ({\n category: {\n label: category.label,\n icon: category.icon,\n isCustomIcon: category.isCustomIcon,\n },\n isActive: index === viewportCurrentCategoryIndex,\n scrollTo: () => {\n const viewport = viewportRef.current;\n\n if (!viewport) {\n return;\n }\n\n const scrollTop =\n index * categoryHeaderHeight + category.startRowIndex * rowHeight;\n\n viewport.scrollTo({\n top: scrollTop,\n });\n\n requestAnimationFrame(() => {\n onViewportScroll(viewport.scrollTop);\n });\n },\n }));\n }, [\n data?.categories,\n viewportRef,\n rowHeight,\n categoryHeaderHeight,\n viewportCurrentCategoryIndex,\n onViewportScroll,\n ]);\n\n return children({ categories });\n}\n\nexport { EmojiPickerCategoryNav };\n","export function shallow(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n\n if (\n typeof a !== \"object\" ||\n typeof b !== \"object\" ||\n a === null ||\n b === null\n ) {\n return false;\n }\n\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n return keysA.every(\n (key) => key in b && a[key as keyof typeof a] === b[key as keyof typeof b],\n );\n}\n","import { useEffect, useLayoutEffect as useOriginalLayoutEffect } from \"react\";\n\n// On React 18.2.0 and earlier, useLayoutEffect triggers a warning when executed on the server\nexport const useLayoutEffect =\n /* v8 ignore next */\n typeof window !== \"undefined\" ? useOriginalLayoutEffect : useEffect;\n","import {\n type ComponentProps,\n type CSSProperties,\n Fragment,\n forwardRef,\n memo,\n type SyntheticEvent as ReactSyntheticEvent,\n useCallback,\n useImperativeHandle,\n useMemo,\n useRef,\n} from \"react\";\nimport {\n $activeEmoji,\n $categoriesRowsStartIndices,\n $rowsCount,\n sameEmojiPickerRow,\n useEmojiPickerStore,\n} from \"../../store\";\nimport type {\n EmojiPickerCategory,\n EmojiPickerDataCategory,\n EmojiPickerEmoji,\n EmojiPickerListCategoryHeaderProps,\n EmojiPickerListComponents,\n EmojiPickerListEmojiProps,\n EmojiPickerListProps,\n EmojiPickerListRowProps,\n WithAttributes,\n} from \"../../types\";\nimport { shallow } from \"../../utils/compare\";\nimport { useSelector, useSelectorKey } from \"../../utils/store\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\n\nfunction listEmojiProps(\n emoji: EmojiPickerEmoji,\n columnIndex: number,\n isActive: boolean,\n): WithAttributes<EmojiPickerListEmojiProps> {\n return {\n emoji: { ...emoji, isActive },\n role: \"gridcell\",\n \"aria-colindex\": columnIndex,\n \"aria-selected\": isActive || undefined,\n \"aria-label\": emoji.label,\n \"data-active\": isActive ? \"\" : undefined,\n \"frimousse-emoji\": \"\",\n style: {\n fontFamily: \"var(--frimousse-emoji-font)\",\n },\n tabIndex: -1,\n };\n}\n\nfunction listRowProps(\n rowIndex: number,\n sizer = false,\n): WithAttributes<EmojiPickerListRowProps> {\n return {\n role: !sizer ? \"row\" : undefined,\n \"aria-rowindex\": !sizer ? rowIndex : undefined,\n \"frimousse-row\": \"\",\n style: {\n contain: !sizer ? \"content\" : undefined,\n height: !sizer ? \"var(--frimousse-row-height)\" : undefined,\n display: \"flex\",\n },\n };\n}\n\nfunction listCategoryProps(\n categoryIndex: number,\n category?: EmojiPickerDataCategory,\n): WithAttributes<ComponentProps<\"div\">> {\n return {\n \"frimousse-category\": \"\",\n style: {\n contain: \"content\",\n top: category\n ? `calc(${categoryIndex} * var(--frimousse-category-header-height) + ${category.startRowIndex} * var(--frimousse-row-height))`\n : undefined,\n height: category\n ? `calc(var(--frimousse-category-header-height) + ${category.rowsCount} * var(--frimousse-row-height))`\n : undefined,\n width: \"100%\",\n pointerEvents: \"none\",\n position: \"absolute\",\n },\n };\n}\n\nfunction listCategoryHeaderProps(\n category: EmojiPickerCategory,\n sizer = false,\n sticky = true,\n): WithAttributes<EmojiPickerListCategoryHeaderProps> {\n return {\n category,\n \"frimousse-category-header\": \"\",\n style: {\n contain: !sizer ? \"layout paint\" : undefined,\n height: !sizer ? \"var(--frimousse-category-header-height)\" : undefined,\n pointerEvents: \"auto\",\n position: sticky ? \"sticky\" : undefined,\n top: 0,\n },\n };\n}\n\nfunction listSizerProps(\n rowsCount: number,\n categoriesCount: number,\n viewportStartRowIndex: number,\n previousHeadersCount: number,\n): WithAttributes<ComponentProps<\"div\">> {\n return {\n \"frimousse-list-sizer\": \"\",\n style: {\n position: \"relative\",\n boxSizing: \"border-box\",\n height: `calc(${rowsCount} * var(--frimousse-row-height) + ${categoriesCount} * var(--frimousse-category-header-height))`,\n paddingTop: `calc(${viewportStartRowIndex} * var(--frimousse-row-height) + ${previousHeadersCount} * var(--frimousse-category-header-height))`,\n },\n };\n}\n\nfunction listProps(\n columns: number,\n rowsCount: number,\n style: CSSProperties | undefined,\n): WithAttributes<EmojiPickerListProps> {\n return {\n \"aria-colcount\": columns,\n \"aria-rowcount\": rowsCount,\n \"frimousse-list\": \"\",\n style: {\n \"--frimousse-list-columns\": columns,\n ...style,\n } as CSSProperties,\n role: \"grid\",\n };\n}\n\nfunction preventDefault(event: ReactSyntheticEvent) {\n event.preventDefault();\n}\n\nconst EmojiPickerListEmoji = memo(\n ({\n Emoji,\n emoji,\n columnIndex,\n rowIndex,\n }: {\n emoji: EmojiPickerEmoji;\n columnIndex: number;\n rowIndex: number;\n } & Pick<EmojiPickerListComponents, \"Emoji\">) => {\n const store = useEmojiPickerStore();\n const isActive = useSelector(\n store,\n (state) => $activeEmoji(state)?.emoji === emoji.emoji,\n );\n\n const handleSelect = useCallback(() => {\n store.get().onEmojiSelect(emoji);\n }, [emoji]);\n\n const handlePointerEnter = useCallback(() => {\n store.get().onActiveEmojiChange(\"pointer\", columnIndex, rowIndex);\n }, [columnIndex, rowIndex]);\n\n const handlePointerLeave = useCallback(() => {\n store.get().onActiveEmojiReset();\n }, []);\n\n return (\n <Emoji\n {...listEmojiProps(emoji, columnIndex, isActive)}\n onClick={handleSelect}\n onPointerDown={preventDefault}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n />\n );\n },\n);\n\nconst EmojiPickerListRow = memo(\n ({\n Row,\n Emoji,\n rowIndex,\n }: { rowIndex: number } & Pick<\n EmojiPickerListComponents,\n \"Emoji\" | \"Row\"\n >) => {\n const store = useEmojiPickerStore();\n const row = useSelector(\n store,\n (state) => state.data?.rows[rowIndex],\n sameEmojiPickerRow,\n );\n\n /* v8 ignore next 3 */\n if (!row) {\n return null;\n }\n\n return (\n <Row {...listRowProps(rowIndex)}>\n {row.emojis.map((emoji, columnIndex) => (\n <EmojiPickerListEmoji\n columnIndex={columnIndex}\n Emoji={Emoji}\n emoji={emoji}\n key={emoji.label}\n rowIndex={rowIndex}\n />\n ))}\n </Row>\n );\n },\n);\n\nconst EmojiPickerListCategory = memo(\n ({\n CategoryHeader,\n categoryIndex,\n }: { categoryIndex: number } & Pick<\n EmojiPickerListComponents,\n \"CategoryHeader\"\n >) => {\n const store = useEmojiPickerStore();\n const category = useSelector(\n store,\n (state) => state.data?.categories[categoryIndex],\n shallow,\n );\n const sticky = useSelectorKey(store, \"sticky\");\n\n /* v8 ignore next 3 */\n if (!category) {\n return null;\n }\n\n return (\n <div {...listCategoryProps(categoryIndex, category)}>\n <CategoryHeader\n {...listCategoryHeaderProps(\n {\n label: category.label,\n icon: category.icon,\n isCustomIcon: category.isCustomIcon,\n },\n false,\n sticky,\n )}\n />\n </div>\n );\n },\n);\n\nconst EmojiPickerListSizers = memo(\n ({\n CategoryHeader,\n Row,\n Emoji,\n }: Pick<EmojiPickerListComponents, \"CategoryHeader\" | \"Row\" | \"Emoji\">) => {\n const ref = useRef<HTMLDivElement>(null!);\n const store = useEmojiPickerStore();\n const columns = useSelectorKey(store, \"columns\");\n const emojis = useMemo(\n () =>\n Array<EmojiPickerEmoji>(columns).fill({\n emoji: \"πŸ™‚\",\n label: \"\",\n }),\n [columns],\n );\n const category: EmojiPickerCategory = useMemo(\n () => ({\n label: \"Category\",\n icon: \"πŸ“\",\n }),\n [],\n );\n const rowRef = useRef<HTMLDivElement>(null!);\n const categoryHeaderRef = useRef<HTMLDivElement>(null!);\n\n useLayoutEffect(() => {\n const list = ref.current?.parentElement?.parentElement;\n\n /* v8 ignore next 3 */\n if (!list || !rowRef.current || !categoryHeaderRef.current) {\n return;\n }\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const height = entry.contentRect.height;\n\n const {\n onRowHeightChange,\n onCategoryHeaderHeightChange,\n rowHeight,\n categoryHeaderHeight,\n } = store.get();\n\n if (entry.target === rowRef.current && rowHeight !== height) {\n onRowHeightChange(height);\n }\n\n if (\n entry.target === categoryHeaderRef.current &&\n categoryHeaderHeight !== height\n ) {\n onCategoryHeaderHeightChange(height);\n }\n }\n });\n\n resizeObserver.observe(list);\n resizeObserver.observe(rowRef.current);\n resizeObserver.observe(categoryHeaderRef.current);\n\n const { onRowHeightChange, onCategoryHeaderHeightChange } = store.get();\n\n onRowHeightChange(rowRef.current.clientHeight);\n onCategoryHeaderHeightChange(categoryHeaderRef.current.clientHeight);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n return (\n <div\n aria-hidden\n ref={ref}\n style={{\n height: 0,\n visibility: \"hidden\",\n }}\n >\n <div frimousse-row-sizer=\"\" ref={rowRef}>\n <Row {...listRowProps(-1, true)}>\n {emojis.map((emoji, index) => (\n <Emoji key={index} {...listEmojiProps(emoji, index, false)} />\n ))}\n </Row>\n </div>\n <div {...listCategoryProps(-1)}>\n <div frimousse-category-header-sizer=\"\" ref={categoryHeaderRef}>\n <CategoryHeader {...listCategoryHeaderProps(category, true)} />\n </div>\n </div>\n </div>\n );\n },\n);\n\nfunction DefaultEmojiPickerListCategoryHeader({\n category,\n ...props\n}: EmojiPickerListCategoryHeaderProps) {\n return (\n <div {...props}>\n {category.icon && (\n <span style={{ marginRight: \"0.5em\" }}>\n {category.isCustomIcon ? (\n <img\n alt=\"\"\n src={category.icon}\n style={{\n width: \"1em\",\n height: \"1em\",\n objectFit: \"contain\",\n verticalAlign: \"middle\",\n }}\n />\n ) : (\n category.icon\n )}\n </span>\n )}\n {category.label}\n </div>\n );\n}\n\nfunction DefaultEmojiPickerListEmoji({\n emoji,\n ...props\n}: EmojiPickerListEmojiProps) {\n return (\n <button type=\"button\" {...props}>\n {emoji.isCustom ? (\n <img\n alt={emoji.label}\n src={emoji.emoji}\n style={{\n width: \"1em\",\n height: \"1em\",\n objectFit: \"contain\",\n verticalAlign: \"middle\",\n }}\n />\n ) : (\n emoji.emoji\n )}\n </button>\n );\n}\n\nfunction DefaultEmojiPickerListRow({ ...props }: EmojiPickerListRowProps) {\n return <div {...props} />;\n}\n\n/**\n * The list of emojis.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n *\n * Inner components within the list can be customized via the `components` prop.\n *\n * @example\n * ```tsx\n * <EmojiPicker.List\n * components={{\n * CategoryHeader: ({ category, ...props }) => (\n * <div {...props}>{category.label}</div>\n * ),\n * Emoji: ({ emoji, ...props }) => (\n * <button {...props}>\n * {emoji.emoji}\n * </button>\n * ),\n * Row: ({ children, ...props }) => <div {...props}>{children}</div>,\n * }}\n * />\n * ```\n */\nconst EmojiPickerList = forwardRef<HTMLDivElement, EmojiPickerListProps>(\n ({ style, components, ...props }, forwardedRef) => {\n const store = useEmojiPickerStore();\n const ref = useRef<HTMLDivElement>(null!);\n const callbackRef = useCallback((element: HTMLDivElement | null) => {\n if (element) {\n ref.current = element;\n store.set({ listRef: ref });\n }\n }, []);\n const CategoryHeader =\n components?.CategoryHeader ?? DefaultEmojiPickerListCategoryHeader;\n const Emoji = components?.Emoji ?? DefaultEmojiPickerListEmoji;\n const Row = components?.Row ?? DefaultEmojiPickerListRow;\n const columns = useSelectorKey(store, \"columns\");\n const viewportStartRowIndex = useSelectorKey(\n store,\n \"viewportStartRowIndex\",\n );\n const viewportEndRowIndex = useSelectorKey(store, \"viewportEndRowIndex\");\n const rowsCount = useSelector(store, $rowsCount);\n const categoriesRowsStartIndices = useSelector(\n store,\n $categoriesRowsStartIndices,\n shallow,\n );\n const previousHeadersCount = useMemo(() => {\n return (\n categoriesRowsStartIndices?.filter(\n (index) => index < viewportStartRowIndex,\n ).length ?? 0\n );\n }, [categoriesRowsStartIndices, viewportStartRowIndex]);\n const categoriesCount = categoriesRowsStartIndices?.length ?? 0;\n\n useImperativeHandle(forwardedRef, () => ref.current);\n\n if (!rowsCount || !categoriesRowsStartIndices || categoriesCount === 0) {\n return (\n <div {...listProps(columns, 0, style)} {...props}>\n <div {...listSizerProps(0, 0, 0, 0)}>\n <EmojiPickerListSizers\n CategoryHeader={CategoryHeader}\n Emoji={Emoji}\n Row={Row}\n />\n </div>\n </div>\n );\n }\n\n return (\n <div\n {...listProps(columns, rowsCount, style)}\n {...props}\n ref={callbackRef}\n >\n <div\n {...listSizerProps(\n rowsCount,\n categoriesCount,\n viewportStartRowIndex,\n previousHeadersCount,\n )}\n >\n <EmojiPickerListSizers\n CategoryHeader={CategoryHeader}\n Emoji={Emoji}\n Row={Row}\n />\n {Array.from(\n { length: viewportEndRowIndex - viewportStartRowIndex + 1 },\n (_, index) => {\n const rowIndex = viewportStartRowIndex + index;\n const categoryIndex =\n categoriesRowsStartIndices.indexOf(rowIndex);\n\n return (\n <Fragment key={rowIndex}>\n {categoryIndex >= 0 && (\n <div\n style={{\n height: \"var(--frimousse-category-header-height)\",\n }}\n />\n )}\n <EmojiPickerListRow\n Emoji={Emoji}\n Row={Row}\n rowIndex={rowIndex}\n />\n </Fragment>\n );\n },\n )}\n {Array.from({ length: categoriesCount }, (_, index) => (\n <EmojiPickerListCategory\n CategoryHeader={CategoryHeader}\n categoryIndex={index}\n key={index}\n />\n ))}\n </div>\n </div>\n );\n },\n);\n\nexport { EmojiPickerList };\n","import type { ReactNode } from \"react\";\nimport { $isEmpty, $isLoading, $search, useEmojiPickerStore } from \"../../store\";\nimport type { EmojiPickerEmptyProps, EmojiPickerLoadingProps } from \"../../types\";\nimport { useSelector } from \"../../utils/store\";\n\n/**\n * Only renders when the emoji data is loading.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.Loading>Loading…</EmojiPicker.Loading>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n */\nfunction EmojiPickerLoading({ children, ...props }: EmojiPickerLoadingProps) {\n const store = useEmojiPickerStore();\n const isLoading = useSelector(store, $isLoading);\n\n if (!isLoading) {\n return null;\n }\n\n return (\n <span frimousse-loading=\"\" {...props}>\n {children}\n </span>\n );\n}\n\nfunction EmojiPickerEmptyWithSearch({\n children,\n}: {\n children: (props: { search: string }) => ReactNode;\n}) {\n const store = useEmojiPickerStore();\n const search = useSelector(store, $search);\n\n return children({ search });\n}\n\n/**\n * Only renders when no emoji is found for the current search. The content is\n * rendered without any surrounding DOM element.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.Empty>No emoji found.</EmojiPicker.Empty>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n *\n * It can also expose the current search via a render callback to build\n * a more detailed empty state.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Empty>\n * {({ search }) => <>No emoji found for \"{search}\"</>}\n * </EmojiPicker.Empty>\n * ```\n */\nfunction EmojiPickerEmpty({ children, ...props }: EmojiPickerEmptyProps) {\n const store = useEmojiPickerStore();\n const isEmpty = useSelector(store, $isEmpty);\n\n if (!isEmpty) {\n return null;\n }\n\n return (\n <span frimousse-empty=\"\" {...props}>\n {typeof children === \"function\" ? (\n <EmojiPickerEmptyWithSearch>{children}</EmojiPickerEmptyWithSearch>\n ) : (\n children\n )}\n </span>\n );\n}\n\nexport { EmojiPickerEmpty, EmojiPickerLoading };\n","export function capitalize(string: string) {\n return string.charAt(0).toUpperCase() + string.slice(1);\n}\n","import { EMOJI_FONT_FAMILY } from \"../constants\";\n\nconst CANVAS_SIZE = 2;\n\nlet context: CanvasRenderingContext2D | null = null;\n\nexport function isEmojiSupported(emoji: string): boolean {\n try {\n context ??= document\n .createElement(\"canvas\")\n .getContext(\"2d\", { willReadFrequently: true });\n /* v8 ignore next */\n } catch {}\n\n // Non-browser environments are not supported\n if (!context) {\n return false;\n }\n\n // Schedule to dispose of the context\n queueMicrotask(() => {\n if (context) {\n context = null;\n }\n });\n\n context.canvas.width = CANVAS_SIZE;\n context.canvas.height = CANVAS_SIZE;\n context.font = `2px ${EMOJI_FONT_FAMILY}`;\n context.textBaseline = \"middle\";\n\n // Unsupported ZWJ sequence emojis show up as separate emojis\n if (context.measureText(emoji).width >= CANVAS_SIZE * 2) {\n return false;\n }\n\n context.fillStyle = \"#00f\";\n context.fillText(emoji, 0, 0);\n\n const blue = context.getImageData(0, 0, CANVAS_SIZE, CANVAS_SIZE).data;\n\n context.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);\n\n context.fillStyle = \"#f00\";\n context.fillText(emoji, 0, 0);\n\n const red = context.getImageData(0, 0, CANVAS_SIZE, CANVAS_SIZE).data;\n\n // Emojis have an immutable color so they should look the same regardless of the text color\n for (let i = 0; i < CANVAS_SIZE * CANVAS_SIZE * 4; i += 4) {\n if (\n blue[i] !== red[i] || // R\n blue[i + 1] !== red[i + 1] || // G\n blue[i + 2] !== red[i + 2] // B\n ) {\n return false;\n }\n }\n\n return true;\n}\n","export function getStorage<T>(\n storage: Storage,\n key: string,\n validate: (value: unknown) => T,\n) {\n try {\n const item = storage.getItem(key);\n\n if (!item) {\n throw new Error(`No value found for \"${key}\".`);\n }\n\n const value = JSON.parse(item) as T;\n\n return validate(value);\n } catch {\n return null;\n }\n}\n\nexport function setStorage<T>(storage: Storage, key: string, value: T) {\n storage.setItem(key, JSON.stringify(value));\n}\n","// A suite of tiny validators.\n// They are only meant to be used internally, so they don't throw human-friendly errors.\n\nexport type Validator<T> = (data: unknown) => T;\n\nexport function optional<T>(\n validate: Validator<T>,\n): (data: unknown) => T | undefined {\n return (data: unknown): T | undefined =>\n data === undefined ? undefined : validate(data);\n}\n\nexport function nullable<T>(validate: Validator<T>): Validator<T | null> {\n return (data: unknown): T | null => (data === null ? null : validate(data));\n}\n\nexport function string(data: unknown): string {\n if (typeof data !== \"string\") {\n throw new Error();\n }\n\n return data;\n}\n\nexport function number(data: unknown): number {\n if (typeof data !== \"number\") {\n throw new Error();\n }\n\n return data;\n}\n\nexport function boolean(data: unknown): boolean {\n if (typeof data !== \"boolean\") {\n throw new Error();\n }\n\n return data;\n}\n\nexport function object<T>(\n keys: {\n [K in keyof T]: Validator<T[K]>;\n },\n): (data: unknown) => T {\n return (data: unknown): T => {\n if (typeof data !== \"object\" || data === null) {\n throw new Error();\n }\n\n const result = {} as T;\n\n for (const key in keys) {\n const value = (data as Record<keyof T, unknown>)[key];\n\n if (value === undefined) {\n // If the value is undefined, verify whether the validator allows it\n keys[key](undefined);\n }\n\n result[key as keyof T] = keys[key](value);\n }\n\n return result;\n };\n}\n\nexport function naiveArray<T>(validate: Validator<T>): Validator<T[]> {\n return (data: unknown): T[] => {\n if (!Array.isArray(data)) {\n throw new Error();\n }\n\n // Naively only validate the first item\n if (data.length > 0) {\n validate(data[0]);\n }\n\n return data;\n };\n}\n","import { SKIN_TONES } from \"../constants\";\nimport type {\n EmojibaseEmoji,\n EmojibaseEmojiWithGroup,\n EmojibaseMessagesDataset,\n EmojiData,\n EmojiDataEmoji,\n Locale,\n SkinTone,\n} from \"../types\";\nimport { capitalize } from \"../utils/capitalize\";\nimport { isEmojiSupported } from \"../utils/is-emoji-supported\";\nimport { getStorage, setStorage } from \"../utils/storage\";\nimport * as $ from \"../utils/validate\";\n\nconst EMOJIBASE_EMOJIS_URL = (baseUrl: string, locale: Locale) =>\n `${baseUrl}/${locale}/data.json`;\nconst EMOJIBASE_MESSAGES_URL = (baseUrl: string, locale: Locale) =>\n `${baseUrl}/${locale}/messages.json`;\n\nconst EMOJIBASE_LOCALES = [\n \"bn\",\n \"da\",\n \"de\",\n \"en-gb\",\n \"en\",\n \"es-mx\",\n \"es\",\n \"et\",\n \"fi\",\n \"fr\",\n \"hi\",\n \"hu\",\n \"it\",\n \"ja\",\n \"ko\",\n \"lt\",\n \"ms\",\n \"nb\",\n \"nl\",\n \"pl\",\n \"pt\",\n \"ru\",\n \"sv\",\n \"th\",\n \"uk\",\n \"vi\",\n \"zh-hant\",\n \"zh\",\n] satisfies Locale[];\nconst EMOJIBASE_DEFAULT_LOCALE: Locale = \"en\";\n\nexport const LOCAL_DATA_KEY = (locale: string) => `frimousse/data/${locale}`;\nexport const SESSION_METADATA_KEY = \"frimousse/metadata\";\n\n// Prevent EMOJIBASE_LOCALES to be out of sync with Locale\n{\n type MissingLocales = Exclude<Locale, (typeof EMOJIBASE_LOCALES)[number]>;\n type AllLocalesPresent = MissingLocales extends never\n ? true\n : `Missing locales: ${MissingLocales}`;\n const _allLocalesPresent: AllLocalesPresent = true;\n _allLocalesPresent;\n}\n\ntype GetEmojiDataOptions = {\n locale: Locale;\n emojiVersion?: number;\n emojibaseUrl?: string;\n signal?: AbortSignal;\n};\n\ntype LocalData = {\n data: EmojiData;\n metadata: {\n emojisEtag: string | null;\n messagesEtag: string | null;\n };\n};\n\ntype SessionMetadata = {\n emojiVersion: number;\n countryFlags: boolean;\n};\n\nasync function fetchEtag(url: string, signal?: AbortSignal) {\n try {\n const response = await fetch(url, { method: \"HEAD\", signal });\n\n return response.headers.get(\"etag\");\n } catch (_) {\n return null;\n }\n}\n\nasync function fetchEmojibaseData(\n baseUrl: string,\n locale: Locale,\n signal?: AbortSignal,\n) {\n const [{ emojis, emojisEtag }, { messages, messagesEtag }] =\n await Promise.all([\n fetch(EMOJIBASE_EMOJIS_URL(baseUrl, locale), { signal }).then(\n async (response) => {\n return {\n emojis: (await response.json()) as EmojibaseEmoji[],\n emojisEtag: response.headers.get(\"etag\"),\n };\n },\n ),\n fetch(EMOJIBASE_MESSAGES_URL(baseUrl, locale), { signal }).then(\n async (response) => {\n return {\n messages: (await response.json()) as EmojibaseMessagesDataset,\n messagesEtag: response.headers.get(\"etag\"),\n };\n },\n ),\n ]);\n\n return {\n emojis,\n messages,\n emojisEtag,\n messagesEtag,\n };\n}\n\nasync function fetchEmojibaseEtags(\n baseUrl: string,\n locale: Locale,\n signal?: AbortSignal,\n) {\n const [emojisEtag, messagesEtag] = await Promise.all([\n fetchEtag(EMOJIBASE_EMOJIS_URL(baseUrl, locale), signal),\n fetchEtag(EMOJIBASE_MESSAGES_URL(baseUrl, locale), signal),\n ]);\n\n return {\n emojisEtag,\n messagesEtag,\n };\n}\n\nexport function getEmojibaseSkinToneVariations(\n emoji: EmojibaseEmojiWithGroup,\n): Record<Exclude<SkinTone, \"none\">, string> | undefined {\n if (!emoji.skins) {\n return;\n }\n\n const skinToneVariations = emoji.skins.filter(\n (emoji) => typeof emoji.tone === \"number\",\n );\n\n return skinToneVariations.reduce(\n (result, emoji) => {\n const skinTone = SKIN_TONES[emoji.tone as number]!;\n\n result[skinTone as Exclude<SkinTone, \"none\">] = emoji.emoji;\n\n return result;\n },\n {} as Record<Exclude<SkinTone, \"none\">, string>,\n );\n}\n\nasync function fetchEmojiData(\n baseUrl: string,\n locale: Locale,\n signal?: AbortSignal,\n): Promise<EmojiData> {\n const { emojis, emojisEtag, messages, messagesEtag } =\n await fetchEmojibaseData(baseUrl, locale, signal);\n const countryFlagsSubgroup = messages.subgroups.find(\n (subgroup) =>\n subgroup.key === \"country-flag\" || subgroup.key === \"subdivision-flag\",\n );\n\n // Filter out the component/modifier category and its emojis\n const filteredGroups = messages.groups.filter(\n (group) => group.key !== \"component\",\n );\n const filteredEmojis = emojis.filter((emoji) => {\n return \"group\" in emoji;\n }) as EmojibaseEmojiWithGroup[];\n\n const categories = filteredGroups.map((group) => ({\n index: group.order,\n label: capitalize(group.message),\n }));\n const skinTones = messages.skinTones.reduce(\n (skinTones, skinTone) => {\n skinTones[skinTone.key] = capitalize(skinTone.message);\n\n return skinTones;\n },\n {} as Record<SkinTone, string>,\n );\n\n const formattedEmojis = filteredEmojis.map((emoji) => {\n return {\n emoji: emoji.emoji,\n category: emoji.group,\n version: emoji.version,\n label: capitalize(emoji.label),\n tags: emoji.tags ?? [],\n countryFlag:\n (countryFlagsSubgroup &&\n emoji.subgroup === countryFlagsSubgroup.order) ||\n undefined,\n skins: getEmojibaseSkinToneVariations(emoji),\n } satisfies EmojiDataEmoji;\n });\n\n const emojiData: EmojiData = {\n locale,\n emojis: formattedEmojis,\n categories,\n skinTones,\n };\n\n setStorage(localStorage, LOCAL_DATA_KEY(locale), {\n data: emojiData,\n metadata: {\n emojisEtag,\n messagesEtag,\n },\n });\n\n return emojiData;\n}\n\nfunction getSessionMetadata(\n emojis: EmojiDataEmoji[],\n emojiVersion?: number,\n): SessionMetadata {\n const versionEmojis = new Map<number, string>();\n\n for (const emoji of emojis) {\n if (!versionEmojis.has(emoji.version)) {\n versionEmojis.set(emoji.version, emoji.emoji);\n }\n }\n\n const descendingVersions = [...versionEmojis.keys()].sort((a, b) => b - a);\n const highestVersion = descendingVersions[0] ?? 0;\n\n const supportsCountryFlags = isEmojiSupported(\"πŸ‡ͺπŸ‡Ί\");\n\n if (typeof emojiVersion === \"number\") {\n return {\n emojiVersion,\n countryFlags: supportsCountryFlags,\n };\n }\n\n for (const version of descendingVersions) {\n const emoji = versionEmojis.get(version)!;\n\n if (isEmojiSupported(emoji)) {\n return {\n emojiVersion: version,\n countryFlags: supportsCountryFlags,\n };\n }\n }\n\n return {\n emojiVersion: highestVersion,\n countryFlags: supportsCountryFlags,\n };\n}\n\nconst validateSessionMetadata = $.object<SessionMetadata>({\n emojiVersion: $.number,\n countryFlags: $.boolean,\n});\n\nconst validateLocalData = $.object<LocalData>({\n data: $.object({\n locale: $.string as $.Validator<Locale>,\n emojis: $.naiveArray(\n $.object({\n emoji: $.string,\n category: $.number,\n label: $.string,\n version: $.number,\n tags: $.naiveArray($.string),\n countryFlag: $.optional($.boolean as $.Validator<true>),\n skins: $.optional(\n $.object({\n light: $.string,\n \"medium-light\": $.string,\n medium: $.string,\n \"medium-dark\": $.string,\n dark: $.string,\n }),\n ),\n }),\n ),\n categories: $.naiveArray(\n $.object({\n index: $.number,\n label: $.string,\n }),\n ),\n skinTones: $.object({\n light: $.string,\n \"medium-light\": $.string,\n medium: $.string,\n \"medium-dark\": $.string,\n dark: $.string,\n }),\n }),\n metadata: $.object({\n emojisEtag: $.nullable($.string),\n messagesEtag: $.nullable($.string),\n }),\n});\n\nexport async function getEmojiData({\n locale,\n emojiVersion,\n emojibaseUrl,\n signal,\n}: GetEmojiDataOptions): Promise<EmojiData> {\n const baseUrl =\n typeof emojibaseUrl === \"string\"\n ? emojibaseUrl\n : `https://cdn.jsdelivr.net/npm/emojibase-data@${typeof emojiVersion === \"number\" ? Math.floor(emojiVersion) : \"latest\"}`;\n let sessionMetadata = getStorage<SessionMetadata>(\n sessionStorage,\n SESSION_METADATA_KEY,\n validateSessionMetadata,\n );\n const localData = getStorage<LocalData>(\n localStorage,\n LOCAL_DATA_KEY(locale),\n validateLocalData,\n );\n\n let data: EmojiData;\n\n if (!localData) {\n // No local data\n data = await fetchEmojiData(baseUrl, locale, signal);\n } else if (sessionMetadata) {\n // ETags are used to check if the data is up-to-date but only\n // once per session, so if the session metadata is already set,\n // the local data can be used\n data = localData.data;\n } else {\n // Check ETags to see if the local data is up-to-date,\n // but if that fails, the possibly-stale local data is used\n try {\n const { emojisEtag, messagesEtag } = await fetchEmojibaseEtags(\n baseUrl,\n locale,\n signal,\n );\n\n data =\n !emojisEtag ||\n !messagesEtag ||\n emojisEtag !== localData.metadata.emojisEtag ||\n messagesEtag !== localData.metadata.messagesEtag\n ? await fetchEmojiData(baseUrl, locale, signal)\n : localData.data;\n } catch {\n data = localData.data;\n }\n }\n\n // Set the session metadata if needed\n sessionMetadata ??= getSessionMetadata(data.emojis, emojiVersion);\n setStorage(sessionStorage, SESSION_METADATA_KEY, sessionMetadata);\n\n // Filter out unsupported emojis\n const filteredEmojis = data.emojis.filter((emoji) => {\n const isSupportedVersion = emoji.version <= sessionMetadata.emojiVersion;\n\n return emoji.countryFlag\n ? isSupportedVersion && sessionMetadata.countryFlags\n : isSupportedVersion;\n });\n\n return {\n locale,\n emojis: filteredEmojis,\n categories: data.categories,\n skinTones: data.skinTones,\n };\n}\n\nexport function validateLocale(locale: string): Locale {\n if (!EMOJIBASE_LOCALES.includes(locale as Locale)) {\n console.warn(\n `Locale \"${locale}\" is not supported, using \"${EMOJIBASE_DEFAULT_LOCALE}\" instead.`,\n );\n\n return EMOJIBASE_DEFAULT_LOCALE;\n }\n\n return locale as Locale;\n}\n\nexport function validateSkinTone(skinTone: string): SkinTone {\n if (!SKIN_TONES.includes(skinTone as SkinTone)) {\n console.warn(`Skin tone \"${skinTone}\" is not valid, using \"none\" instead.`);\n\n return \"none\";\n }\n\n return skinTone as SkinTone;\n}\n","export function chunk<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n\n if (size <= 0) {\n return chunks;\n }\n\n for (let i = 0, j = array.length; i < j; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n\n return chunks;\n}\n","import type {\n Emoji,\n EmojiData,\n EmojiDataEmoji,\n EmojiPickerData,\n EmojiPickerDataCategory,\n EmojiPickerDataRow,\n EmojiPickerEmoji,\n SkinTone,\n} from \"../types\";\nimport { chunk } from \"../utils/chunk\";\n\nexport function searchEmojis(emojis: EmojiDataEmoji[], search?: string) {\n if (!search) {\n return emojis;\n }\n\n const searchText = search.toLowerCase().trim();\n const scores = new WeakMap<Emoji, number>();\n\n return emojis\n .filter((emoji) => {\n let score = 0;\n\n if (emoji.label.toLowerCase().includes(searchText)) {\n score += 10;\n }\n\n for (const tag of emoji.tags) {\n if (tag.toLowerCase().includes(searchText)) {\n score += 1;\n }\n }\n\n if (score > 0) {\n scores.set(emoji, score);\n\n return true;\n }\n\n return false;\n })\n .sort((a, b) => (scores.get(b) ?? 0) - (scores.get(a) ?? 0));\n}\n\nexport function getEmojiPickerData(\n data: EmojiData,\n columns: number,\n skinTone: SkinTone | undefined,\n search: string,\n): EmojiPickerData {\n const emojis = searchEmojis(data.emojis, search);\n const rows: EmojiPickerDataRow[] = [];\n const categories: EmojiPickerDataCategory[] = [];\n const categoriesStartRowIndices: number[] = [];\n const emojisByCategory: Record<number, EmojiPickerEmoji[]> = {};\n let categoryIndex = 0;\n let startRowIndex = 0;\n\n for (const emoji of emojis) {\n if (!emojisByCategory[emoji.category]) {\n emojisByCategory[emoji.category] = [];\n }\n\n emojisByCategory[emoji.category]!.push({\n emoji:\n skinTone && skinTone !== \"none\" && emoji.skins\n ? emoji.skins[skinTone]\n : emoji.emoji,\n label: emoji.label,\n isCustom: emoji.isCustom,\n });\n }\n\n for (const category of data.categories) {\n const categoryEmojis = emojisByCategory[category.index];\n\n if (!categoryEmojis || categoryEmojis.length === 0) {\n continue;\n }\n\n const categoryRows = chunk(Array.from(categoryEmojis), columns).map(\n (emojis) => ({\n categoryIndex,\n emojis,\n }),\n );\n\n rows.push(...categoryRows);\n categories.push({\n label: category.label,\n rowsCount: categoryRows.length,\n startRowIndex,\n icon: category.icon,\n isCustomIcon: category.isCustomIcon,\n });\n\n categoriesStartRowIndices.push(startRowIndex);\n\n categoryIndex++;\n startRowIndex += categoryRows.length;\n }\n\n return {\n count: emojis.length,\n categories,\n categoriesStartRowIndices,\n rows,\n skinTones: data.skinTones,\n };\n}\n","export function noop(..._: unknown[]) {\n return;\n}\n\nexport async function noopAsync(..._: unknown[]) {\n return;\n}\n","const DEFAULT_TIMEOUT = 10;\nconst DEFAULT_MAX_TIMEOUT = 50;\n\n// Safari doesn't support requestIdleCallback yet\nexport function requestIdleCallback(\n callback: IdleRequestCallback,\n options?: IdleRequestOptions,\n) {\n let id: ReturnType<typeof window.requestIdleCallback> | null = null;\n\n if (typeof window.requestIdleCallback === \"function\") {\n id = window.requestIdleCallback(callback, options);\n } else {\n const start = Date.now();\n\n id = window.setTimeout(() => {\n callback({\n didTimeout: false,\n timeRemaining: () =>\n Math.max(\n 0,\n (options?.timeout ?? DEFAULT_MAX_TIMEOUT) - (Date.now() - start),\n ),\n });\n }, DEFAULT_TIMEOUT);\n }\n\n return () => {\n if (typeof window.cancelIdleCallback === \"function\") {\n window.cancelIdleCallback(id);\n } else {\n window.clearTimeout(id);\n }\n };\n}\n","import { useCallback, useRef } from \"react\";\nimport { useLayoutEffect } from \"./use-layout-effect\";\n\nexport function useStableCallback<A extends unknown[], R>(\n callback: (...args: A) => R,\n): (...args: A) => R {\n const callbackRef = useRef(callback);\n\n useLayoutEffect(() => {\n callbackRef.current = callback;\n });\n\n return useCallback((...args: A): R => {\n return callbackRef.current(...args);\n }, []);\n}\n","import {\n type CSSProperties,\n forwardRef,\n type FocusEvent as ReactFocusEvent,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { EMOJI_FONT_FAMILY } from \"../../constants\";\nimport { getEmojiData, validateLocale, validateSkinTone } from \"../../data/emoji\";\nimport { getEmojiPickerData } from \"../../data/emoji-picker\";\nimport {\n $activeEmoji,\n createEmojiPickerStore,\n type EmojiPickerStore,\n EmojiPickerStoreProvider,\n useEmojiPickerStore,\n} from \"../../store\";\nimport type {\n EmojiData,\n EmojiDataCategory,\n EmojiDataEmoji,\n EmojiPickerRootProps,\n} from \"../../types\";\nimport { noop } from \"../../utils/noop\";\nimport { requestIdleCallback } from \"../../utils/request-idle-callback\";\nimport { useCreateStore, useSelectorKey } from \"../../utils/store\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\nimport { useStableCallback } from \"../../utils/use-stable-callback\";\n\nfunction EmojiPickerDataHandler({\n emojiVersion,\n emojibaseUrl,\n customEmojis,\n customCategories,\n}: Pick<\n EmojiPickerRootProps,\n \"emojiVersion\" | \"emojibaseUrl\" | \"customEmojis\" | \"customCategories\"\n>) {\n const [emojiData, setEmojiData] = useState<EmojiData | undefined>(undefined);\n const store = useEmojiPickerStore();\n const locale = useSelectorKey(store, \"locale\");\n const columns = useSelectorKey(store, \"columns\");\n const skinTone = useSelectorKey(store, \"skinTone\");\n const search = useSelectorKey(store, \"search\");\n\n useEffect(() => {\n const controller = new AbortController();\n const signal = controller.signal;\n\n getEmojiData({ locale, emojiVersion, emojibaseUrl, signal })\n .then((data) => {\n let categories = [...data.categories];\n let customCategoryIndex =\n Math.max(...data.categories.map((category) => category.index)) + 1;\n\n if (customCategories && customCategories.length > 0) {\n const sortedCustomCategories = [...customCategories].sort(\n (a, b) => a.index - b.index,\n );\n const customCategoryData: EmojiDataCategory[] =\n sortedCustomCategories.map((customCategory) => ({\n index: customCategory.index,\n label: customCategory.label,\n icon: customCategory.icon,\n isCustomIcon: customCategory.isCustomIcon,\n }));\n\n categories = [...customCategoryData, ...categories];\n customCategoryIndex =\n Math.max(...categories.map((category) => category.index)) + 1;\n }\n\n if (customEmojis && customEmojis.length > 0) {\n const categoryMapping = new Map<number, number>();\n\n if (customCategories) {\n customCategories.forEach((customCategory) => {\n categoryMapping.set(\n customCategory.id ?? customCategory.index,\n customCategory.index,\n );\n });\n }\n\n const customEmojisData: EmojiData[\"emojis\"] = customEmojis.map(\n (customEmoji) => {\n const categoryIndex =\n categoryMapping.get(customEmoji.category ?? 0) ??\n customEmoji.category ??\n customCategoryIndex;\n\n return {\n emoji: customEmoji.emoji,\n category: categoryIndex,\n label: customEmoji.label,\n version: emojiVersion ?? 99,\n tags: customEmoji.tags ?? [],\n countryFlag: undefined,\n skins: undefined,\n isCustom: customEmoji.isCustom,\n } satisfies EmojiDataEmoji;\n },\n );\n\n const hasCustomCategory = customEmojis.some(\n (emoji) => emoji.category === undefined,\n );\n\n if (hasCustomCategory) {\n categories = [\n ...categories,\n { index: customCategoryIndex, label: \"Custom\" },\n ];\n }\n\n setEmojiData({\n ...data,\n emojis: [...data.emojis, ...customEmojisData],\n categories,\n });\n } else {\n setEmojiData({\n ...data,\n categories,\n });\n }\n })\n .catch((error) => {\n if (!signal.aborted) {\n console.error(error);\n }\n });\n\n return () => {\n controller.abort();\n };\n }, [emojiVersion, emojibaseUrl, locale, customEmojis, customCategories]);\n\n useEffect(() => {\n if (!emojiData) {\n return;\n }\n\n return requestIdleCallback(\n () => {\n store\n .get()\n .onDataChange(\n getEmojiPickerData(emojiData, columns, skinTone, search),\n );\n },\n { timeout: 100 },\n );\n }, [emojiData, columns, skinTone, search]);\n\n return null;\n}\n\n/**\n * Surrounds all the emoji picker parts.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root onEmojiSelect={({ emoji }) => console.log(emoji)}>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n *\n * Options affecting the entire emoji picker are available on this\n * component as props.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root locale=\"fr\" columns={10} skinTone=\"medium\">\n * {\\/* ... *\\/}\n * </EmojiPicker.Root>\n * ```\n */\nconst EmojiPickerRoot = forwardRef<HTMLDivElement, EmojiPickerRootProps>(\n (\n {\n locale = \"en\",\n columns = 9,\n skinTone = \"none\",\n onEmojiSelect = noop,\n emojiVersion,\n emojibaseUrl,\n customEmojis,\n customCategories,\n onFocusCapture,\n onBlurCapture,\n children,\n style,\n sticky = true,\n ...props\n },\n forwardedRef,\n ) => {\n const stableOnEmojiSelect = useStableCallback(onEmojiSelect);\n const store = useCreateStore(() =>\n createEmojiPickerStore(\n stableOnEmojiSelect,\n validateLocale(locale),\n columns,\n sticky,\n validateSkinTone(skinTone),\n ),\n );\n const [isFocusedWithin, setFocusedWithin] = useState(false);\n const ref = useRef<HTMLDivElement>(null!);\n const callbackRef = useCallback((element: HTMLDivElement | null) => {\n if (element) {\n ref.current = element;\n store.set({ rootRef: ref });\n }\n }, []);\n\n useLayoutEffect(() => {\n store.set({ locale: validateLocale(locale) });\n }, [locale]);\n\n useLayoutEffect(() => {\n store.set({ columns });\n }, [columns]);\n\n useLayoutEffect(() => {\n store.set({ sticky });\n }, [sticky]);\n\n useLayoutEffect(() => {\n store.set({ skinTone: validateSkinTone(skinTone) });\n }, [skinTone]);\n\n const handleFocusCapture = useCallback(\n (event: ReactFocusEvent<HTMLDivElement>) => {\n onFocusCapture?.(event);\n\n const { searchRef, viewportRef } = store.get();\n\n const isSearch =\n event.target === searchRef?.current ||\n event.target.hasAttribute(\"frimousse-search\");\n\n const isViewport =\n event.target === viewportRef?.current ||\n event.target.hasAttribute(\"frimousse-viewport\");\n\n if (!event.isDefaultPrevented()) {\n setFocusedWithin(isSearch || isViewport);\n\n if (!event.isDefaultPrevented()) {\n setFocusedWithin(isSearch || isViewport);\n\n if (isViewport) {\n store.get().onActiveEmojiChange(\"keyboard\", 0, 0);\n } else if (isSearch && store.get().search === \"\") {\n store.set({ interaction: \"none\" });\n }\n }\n }\n },\n [onFocusCapture],\n );\n\n const handleBlurCapture = useCallback(\n (event: ReactFocusEvent<HTMLDivElement>) => {\n onBlurCapture?.(event);\n\n if (\n !event.isDefaultPrevented() &&\n !event.currentTarget.contains(event.relatedTarget)\n ) {\n setFocusedWithin(false);\n }\n },\n [onBlurCapture],\n );\n\n useLayoutEffect(() => {\n if (!isFocusedWithin) {\n store.get().onActiveEmojiReset();\n }\n }, [isFocusedWithin]);\n\n useImperativeHandle(forwardedRef, () => ref.current);\n\n useEffect(() => {\n if (!isFocusedWithin) {\n return;\n }\n\n function handleKeyDown(event: KeyboardEvent) {\n if (\n event.defaultPrevented ||\n (!event.key.startsWith(\"Arrow\") && event.key !== \"Enter\")\n ) {\n return;\n }\n\n const {\n data,\n onEmojiSelect,\n onActiveEmojiChange,\n interaction,\n activeColumnIndex,\n activeRowIndex,\n } = store.get();\n\n // Select the active emoji with enter if it exists\n if (event.key === \"Enter\") {\n const activeEmoji = $activeEmoji(store.get());\n\n if (activeEmoji) {\n event.preventDefault();\n\n onEmojiSelect(activeEmoji);\n }\n }\n\n // Move the active emoji with arrow keys\n if (event.key.startsWith(\"Arrow\")) {\n let columnIndex = activeColumnIndex;\n let rowIndex = activeRowIndex;\n\n event.preventDefault();\n\n if (interaction !== \"none\") {\n if (data?.rows && data.rows.length > 0) {\n switch (event.key) {\n case \"ArrowLeft\": {\n if (columnIndex === 0) {\n const previousRowIndex = rowIndex - 1;\n const previousRow = data.rows[previousRowIndex];\n\n // If first column, move to last column of previous row (if available)\n if (previousRow) {\n rowIndex = previousRowIndex;\n columnIndex = previousRow.emojis.length - 1;\n }\n } else {\n // Otherwise, move to previous column\n columnIndex -= 1;\n }\n\n break;\n }\n\n case \"ArrowRight\": {\n if (columnIndex === data.rows[rowIndex]!.emojis.length - 1) {\n const nextRowIndex = rowIndex + 1;\n const nextRow = data.rows[nextRowIndex];\n\n // If last column, move to first column of next row (if available)\n if (nextRow) {\n rowIndex = nextRowIndex;\n columnIndex = 0;\n }\n } else {\n // Otherwise, move to next column\n columnIndex += 1;\n }\n\n break;\n }\n\n case \"ArrowUp\": {\n const previousRow = data.rows[rowIndex - 1];\n\n // If not first row, move to previous row\n if (previousRow) {\n rowIndex -= 1;\n\n // If previous row doesn't have the same column, move to last column of previous row\n if (!previousRow.emojis[columnIndex]) {\n columnIndex = previousRow.emojis.length - 1;\n }\n }\n\n break;\n }\n\n case \"ArrowDown\": {\n const nextRow = data.rows[rowIndex + 1];\n\n // If not last row, move to next row\n if (nextRow) {\n rowIndex += 1;\n\n // If next row doesn't have the same column, move to last column of next row\n if (!nextRow.emojis[columnIndex]) {\n columnIndex = nextRow.emojis.length - 1;\n }\n }\n\n break;\n }\n }\n }\n\n onActiveEmojiChange(\"keyboard\", columnIndex, rowIndex);\n } else {\n onActiveEmojiChange(\"keyboard\", 0, 0);\n }\n }\n }\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [isFocusedWithin]);\n\n useLayoutEffect(() => {\n let previousViewportWidth: EmojiPickerStore[\"viewportWidth\"] = null;\n let previousViewportHeight: EmojiPickerStore[\"viewportHeight\"] = null;\n let previousRowHeight: EmojiPickerStore[\"rowHeight\"] = null;\n let previousCategoryHeaderHeight: EmojiPickerStore[\"categoryHeaderHeight\"] =\n null;\n\n const unsubscribe = store.subscribe((state) => {\n /* v8 ignore next 3 */\n if (!ref.current) {\n return;\n }\n\n if (previousViewportWidth !== state.viewportWidth) {\n previousViewportWidth = state.viewportWidth;\n\n ref.current.style.setProperty(\n \"--frimousse-viewport-width\",\n `${state.viewportWidth}px`,\n );\n }\n\n if (previousViewportHeight !== state.viewportHeight) {\n previousViewportHeight = state.viewportHeight;\n\n ref.current.style.setProperty(\n \"--frimousse-viewport-height\",\n `${state.viewportHeight}px`,\n );\n }\n\n if (previousRowHeight !== state.rowHeight) {\n previousRowHeight = state.rowHeight;\n\n ref.current.style.setProperty(\n \"--frimousse-row-height\",\n `${state.rowHeight}px`,\n );\n }\n\n if (previousCategoryHeaderHeight !== state.categoryHeaderHeight) {\n previousCategoryHeaderHeight = state.categoryHeaderHeight;\n\n ref.current.style.setProperty(\n \"--frimousse-category-header-height\",\n `${state.categoryHeaderHeight}px`,\n );\n }\n });\n\n const { viewportWidth, viewportHeight, rowHeight, categoryHeaderHeight } =\n store.get();\n\n if (viewportWidth) {\n ref.current.style.setProperty(\n \"--frimousse-viewport-width\",\n `${viewportWidth}px`,\n );\n }\n\n if (viewportHeight) {\n ref.current.style.setProperty(\n \"--frimousse-viewport-height\",\n `${viewportHeight}px`,\n );\n }\n\n if (rowHeight) {\n ref.current.style.setProperty(\n \"--frimousse-row-height\",\n `${rowHeight}px`,\n );\n }\n\n if (categoryHeaderHeight) {\n ref.current.style.setProperty(\n \"--frimousse-category-header-height\",\n `${categoryHeaderHeight}px`,\n );\n }\n\n return unsubscribe;\n }, []);\n\n return (\n <div\n data-focused={isFocusedWithin ? \"\" : undefined}\n frimousse-root=\"\"\n onBlurCapture={handleBlurCapture}\n onFocusCapture={handleFocusCapture}\n {...props}\n ref={callbackRef}\n style={\n {\n \"--frimousse-emoji-font\": EMOJI_FONT_FAMILY,\n ...style,\n } as CSSProperties\n }\n >\n <EmojiPickerStoreProvider store={store}>\n <EmojiPickerDataHandler\n customCategories={customCategories}\n customEmojis={customEmojis}\n emojibaseUrl={emojibaseUrl}\n emojiVersion={emojiVersion}\n />\n {children}\n </EmojiPickerStoreProvider>\n </div>\n );\n },\n);\n\nexport { EmojiPickerRoot };\n","import {\n forwardRef,\n type ChangeEvent as ReactChangeEvent,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n} from \"react\";\nimport { useEmojiPickerStore } from \"../../store\";\nimport type { EmojiPickerSearchProps } from \"../../types\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\n\n/**\n * A search input to filter the list of emojis.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n *\n * It can be controlled or uncontrolled.\n *\n * @example\n * ```tsx\n * const [search, setSearch] = useState(\"\");\n *\n * return (\n * <EmojiPicker.Root>\n * <EmojiPicker.Search\n * value={search}\n * onChange={(event) => setSearch(event.target.value)}\n * />\n * {\\/* ... *\\/}\n * </EmojiPicker.Root>\n * );\n * ```\n */\nconst EmojiPickerSearch = forwardRef<HTMLInputElement, EmojiPickerSearchProps>(\n ({ value, defaultValue, onChange, ...props }, forwardedRef) => {\n const store = useEmojiPickerStore();\n const ref = useRef<HTMLInputElement>(null!);\n const callbackRef = useCallback((element: HTMLInputElement | null) => {\n if (element) {\n ref.current = element;\n store.set({ searchRef: ref });\n }\n }, []);\n const isControlled = typeof value === \"string\";\n const wasControlled = useRef(isControlled);\n\n useEffect(() => {\n if (\n process.env.NODE_ENV !== \"production\" &&\n wasControlled.current !== isControlled\n ) {\n console.warn(\n `EmojiPicker.Search is changing from ${\n wasControlled ? \"controlled\" : \"uncontrolled\"\n } to ${isControlled ? \"controlled\" : \"uncontrolled\"}.`,\n );\n }\n\n wasControlled.current = isControlled;\n }, [isControlled]);\n\n // Initialize search with a controlled or uncontrolled value\n useLayoutEffect(() => {\n store.set({\n search:\n typeof value === \"string\"\n ? value\n : typeof defaultValue === \"string\"\n ? defaultValue\n : \"\",\n });\n }, []);\n\n // Handle controlled value changes\n useLayoutEffect(() => {\n if (typeof value === \"string\") {\n store.get().onSearchChange(value);\n }\n }, [value]);\n\n const handleChange = useCallback(\n (event: ReactChangeEvent<HTMLInputElement>) => {\n onChange?.(event);\n\n if (!event.isDefaultPrevented()) {\n store.get().onSearchChange(event.target.value);\n }\n },\n [onChange],\n );\n\n useImperativeHandle(forwardedRef, () => ref.current);\n\n return (\n <input\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n enterKeyHint=\"done\"\n frimousse-search=\"\"\n placeholder=\"Search…\"\n spellCheck={false}\n type=\"search\"\n {...props}\n defaultValue={defaultValue}\n onChange={handleChange}\n ref={callbackRef}\n value={value}\n />\n );\n },\n);\n\nexport { EmojiPickerSearch };\n","import {\n forwardRef,\n type MouseEvent as ReactMouseEvent,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useSkinTone } from \"../../hooks\";\nimport { $skinTones, useEmojiPickerStore } from \"../../store\";\nimport type {\n EmojiPickerSkinToneProps,\n EmojiPickerSkinToneSelectorProps,\n} from \"../../types\";\nimport { shallow } from \"../../utils/compare\";\nimport { useSelector } from \"../../utils/store\";\n\n/**\n * A button to change the current skin tone by cycling through the\n * available skin tones.\n *\n * @example\n * ```tsx\n * <EmojiPicker.SkinToneSelector />\n * ```\n *\n * The emoji used as visual can be customized (by default, βœ‹).\n *\n * @example\n * ```tsx\n * <EmojiPicker.SkinToneSelector emoji=\"πŸ‘‹\" />\n * ```\n *\n * @see\n * If you want to build a custom skin tone selector, you can use the\n * {@link EmojiPickerSkinTone|`<EmojiPicker.SkinTone />`} component or\n * {@link useSkinTone|`useSkinTone`} hook.\n */\nconst EmojiPickerSkinToneSelector = forwardRef<\n HTMLButtonElement,\n EmojiPickerSkinToneSelectorProps\n>(\n (\n { emoji, onClick, \"aria-label\": ariaLabel = \"Change skin tone\", ...props },\n forwardedRef,\n ) => {\n const store = useEmojiPickerStore();\n const skinTones = useSelector(store, $skinTones, shallow);\n const [skinTone, setSkinTone, skinToneVariations] = useSkinTone(emoji);\n\n const skinToneVariationIndex = useMemo(\n () =>\n Math.max(\n 0,\n skinToneVariations.findIndex(\n (variation) => variation.skinTone === skinTone,\n ),\n ),\n [skinTone, skinToneVariations],\n );\n\n const skinToneVariation = skinToneVariations[skinToneVariationIndex]!;\n const nextSkinToneVariation =\n skinToneVariations[\n (skinToneVariationIndex + 1) % skinToneVariations.length\n ]!;\n const nextSkinTone = nextSkinToneVariation.skinTone;\n\n const nextSkinToneLabel =\n nextSkinTone === \"none\" ? undefined : skinTones?.[nextSkinTone];\n\n const handleClick = useCallback(\n (event: ReactMouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSkinTone(nextSkinTone);\n }\n },\n [onClick, setSkinTone, nextSkinTone],\n );\n\n return (\n <button\n type=\"button\"\n {...props}\n aria-label={\n ariaLabel + (nextSkinToneLabel ? ` (${nextSkinToneLabel})` : \"\")\n }\n aria-live=\"polite\"\n frimousse-skin-tone-selector=\"\"\n onClick={handleClick}\n ref={forwardedRef}\n >\n {skinToneVariation.emoji}\n </button>\n );\n },\n);\n\n/**\n * Exposes the current skin tone and a function to change it via a render\n * callback.\n *\n * @example\n * ```tsx\n * <EmojiPicker.SkinTone>\n * {({ skinTone, setSkinTone }) => (\n * <div>\n * <span>{skinTone}</span>\n * <button onClick={() => setSkinTone(\"none\")}>Reset skin tone</button>\n * </div>\n * )}\n * </EmojiPicker.SkinTone>\n * ```\n *\n * It can be used to build a custom skin tone selector: pass an emoji\n * you want to use as visual (by default, βœ‹) and it will return its skin tone\n * variations.\n *\n * @example\n * ```tsx\n * const [skinTone, setSkinTone, skinToneVariations] = useSkinTone(\"πŸ‘‹\");\n *\n * // (πŸ‘‹) (πŸ‘‹πŸ») (πŸ‘‹πŸΌ) (πŸ‘‹πŸ½) (πŸ‘‹πŸΎ) (πŸ‘‹πŸΏ)\n * <EmojiPicker.SkinTone emoji=\"πŸ‘‹\">\n * {({ skinTone, setSkinTone, skinToneVariations }) => (\n * skinToneVariations.map(({ skinTone, emoji }) => (\n * <button key={skinTone} onClick={() => setSkinTone(skinTone)}>\n * {emoji}\n * </button>\n * ))\n * )}\n * </EmojiPicker.SkinTone>\n * ```\n *\n * @see\n * If you prefer to use a hook rather than a component,\n * {@link useSkinTone} is also available.\n *\n * @see\n * An already-built skin tone selector is also available,\n * {@link EmojiPicker.SkinToneSelector|`<EmojiPicker.SkinToneSelector />`}.\n */\nfunction EmojiPickerSkinTone({ children, emoji }: EmojiPickerSkinToneProps) {\n const [skinTone, setSkinTone, skinToneVariations] = useSkinTone(emoji);\n\n return children({ skinTone, setSkinTone, skinToneVariations });\n}\n\nexport { EmojiPickerSkinTone, EmojiPickerSkinToneSelector };\n","import {\n forwardRef,\n memo,\n type UIEvent as ReactUIEvent,\n useCallback,\n useImperativeHandle,\n useRef,\n} from \"react\";\nimport { useActiveEmoji } from \"../../hooks\";\nimport { $categoriesCount, $rowsCount, useEmojiPickerStore } from \"../../store\";\nimport type { EmojiPickerViewportProps } from \"../../types\";\nimport { useSelector } from \"../../utils/store\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\n\nconst ActiveEmojiAnnouncer = memo(() => {\n const activeEmoji = useActiveEmoji();\n\n if (!activeEmoji) {\n return null;\n }\n\n return (\n <div\n aria-live=\"polite\"\n style={{\n border: 0,\n clip: \"rect(0, 0, 0, 0)\",\n height: 1,\n margin: -1,\n overflow: \"hidden\",\n padding: 0,\n position: \"absolute\",\n whiteSpace: \"nowrap\",\n width: 1,\n wordWrap: \"normal\",\n }}\n >\n {activeEmoji.label}\n </div>\n );\n});\n\n/**\n * The scrolling container of the emoji picker.\n *\n * @example\n * ```tsx\n * <EmojiPicker.Root>\n * <EmojiPicker.Search />\n * <EmojiPicker.Viewport>\n * <EmojiPicker.Loading>Loading…</EmojiPicker.Loading>\n * <EmojiPicker.Empty>No emoji found.</EmojiPicker.Empty>\n * <EmojiPicker.List />\n * </EmojiPicker.Viewport>\n * </EmojiPicker.Root>\n * ```\n */\nconst EmojiPickerViewport = forwardRef<\n HTMLDivElement,\n EmojiPickerViewportProps\n>(({ children, onScroll, onKeyDown, style, ...props }, forwardedRef) => {\n const store = useEmojiPickerStore();\n const ref = useRef<HTMLDivElement>(null!);\n const callbackRef = useCallback((element: HTMLDivElement | null) => {\n if (element) {\n ref.current = element;\n store.set({ viewportRef: ref });\n }\n }, []);\n const rowsCount = useSelector(store, $rowsCount);\n const categoriesCount = useSelector(store, $categoriesCount);\n\n const handleScroll = useCallback(\n (event: ReactUIEvent<HTMLDivElement>) => {\n onScroll?.(event);\n\n store.get().onViewportScroll(event.currentTarget.scrollTop);\n },\n [onScroll],\n );\n\n useLayoutEffect(() => {\n /* v8 ignore next 3 */\n if (!ref.current) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(([entry]) => {\n const width = entry?.borderBoxSize[0]?.inlineSize ?? 0;\n const height = entry?.borderBoxSize[0]?.blockSize ?? 0;\n\n const { onViewportSizeChange, viewportHeight, viewportWidth } =\n store.get();\n\n if (viewportHeight !== height || viewportWidth !== width) {\n onViewportSizeChange(width, height);\n }\n });\n\n resizeObserver.observe(ref.current);\n\n store\n .get()\n .onViewportSizeChange(ref.current.offsetWidth, ref.current.clientHeight);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n useImperativeHandle(forwardedRef, () => ref.current);\n\n return (\n <div\n frimousse-viewport=\"\"\n {...props}\n onScroll={handleScroll}\n ref={callbackRef}\n style={{\n position: \"relative\",\n boxSizing: \"border-box\",\n contain: \"layout paint\",\n containIntrinsicSize:\n typeof rowsCount === \"number\" && typeof categoriesCount === \"number\"\n ? `var(--frimousse-viewport-width, auto) calc(${rowsCount} * var(--frimousse-row-height) + ${categoriesCount} * var(--frimousse-category-header-height))`\n : undefined,\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n scrollbarGutter: \"stable\",\n willChange: \"scroll-position\",\n ...style,\n }}\n >\n <ActiveEmojiAnnouncer />\n {children}\n </div>\n );\n});\n\nexport { EmojiPickerViewport };\n","import { EmojiPickerActiveEmoji } from \"./emoji-picker/active-emoji\";\nimport { EmojiPickerCategoryNav } from \"./emoji-picker/category-nav\";\nimport { EmojiPickerList } from \"./emoji-picker/list\";\nimport { EmojiPickerEmpty, EmojiPickerLoading } from \"./emoji-picker/status\";\nimport { EmojiPickerRoot } from \"./emoji-picker/root\";\nimport { EmojiPickerSearch } from \"./emoji-picker/search\";\nimport {\n EmojiPickerSkinTone,\n EmojiPickerSkinToneSelector,\n} from \"./emoji-picker/skin-tone\";\nimport { EmojiPickerViewport } from \"./emoji-picker/viewport\";\n\nEmojiPickerRoot.displayName = \"EmojiPicker.Root\";\nEmojiPickerSearch.displayName = \"EmojiPicker.Search\";\nEmojiPickerViewport.displayName = \"EmojiPicker.Viewport\";\nEmojiPickerList.displayName = \"EmojiPicker.List\";\n(\n EmojiPickerLoading as typeof EmojiPickerLoading & { displayName?: string }\n).displayName = \"EmojiPicker.Loading\";\n(\n EmojiPickerEmpty as typeof EmojiPickerEmpty & { displayName?: string }\n).displayName = \"EmojiPicker.Empty\";\nEmojiPickerSkinToneSelector.displayName = \"EmojiPicker.SkinToneSelector\";\n(\n EmojiPickerActiveEmoji as typeof EmojiPickerActiveEmoji & {\n displayName?: string;\n }\n).displayName = \"EmojiPicker.ActiveEmoji\";\n(\n EmojiPickerCategoryNav as typeof EmojiPickerCategoryNav & {\n displayName?: string;\n }\n).displayName = \"EmojiPicker.CategoryNav\";\n(\n EmojiPickerSkinTone as typeof EmojiPickerSkinTone & { displayName?: string }\n).displayName = \"EmojiPicker.SkinTone\";\n\nexport {\n EmojiPickerRoot as Root, // <EmojiPicker.Root />\n EmojiPickerSearch as Search, // <EmojiPicker.Search />\n EmojiPickerViewport as Viewport, // <EmojiPicker.Viewport />\n EmojiPickerList as List, // <EmojiPicker.List />\n EmojiPickerLoading as Loading, // <EmojiPicker.Loading />\n EmojiPickerEmpty as Empty, // <EmojiPicker.Empty />\n EmojiPickerSkinToneSelector as SkinToneSelector, // <EmojiPicker.SkinToneSelector />\n EmojiPickerActiveEmoji as ActiveEmoji, // <EmojiPicker.ActiveEmoji />\n EmojiPickerCategoryNav as CategoryNav, // <EmojiPicker.CategoryNav />\n EmojiPickerSkinTone as SkinTone, // <EmojiPicker.SkinTone />\n};\n"],"mappings":"uVAkBA,SAAgB,EACd,EACU,CACV,IAAI,EAAQ,EAAE,CACV,EAAoB,KACpB,EAAyB,KACvB,EAAc,IAAI,IAElB,MAAc,CAClB,GAAI,EAAS,CACX,EAAQ,EACR,EAAU,KAEV,IAAK,IAAM,KAAc,EACvB,EAAW,EAAM,CAIrB,EAAU,MAGN,MAAY,GAAW,EAEvB,EAAwB,GAAY,CACxC,IAAY,EACZ,OAAO,OACL,EACA,OAAO,GAAY,WACd,EAAqC,GAAK,CAAC,CAC5C,EACL,CAED,AACE,IAAU,sBAAsB,EAAM,EAY1C,MAFA,GAAQ,EAAmB,EAAK,EAAI,CAE7B,CAAE,MAAK,MAAK,UARA,IACjB,EAAY,IAAI,EAAW,KAEd,EAAY,OAAO,EAAW,EAKf,CAGhC,SAAgB,EAAkB,EAA6B,CAC7D,GAAM,CAAC,GAAS,EAAS,EAAY,CAErC,OAAO,EAGT,SAAgB,EAAsB,EAA+B,CACnE,IAAM,EAAU,EAA+B,KAAK,CAmBpD,MAAO,CAAE,aAjBc,CACrB,IAAM,EAAQ,EAAW,EAAQ,CAEjC,GAAI,CAAC,EACH,MAAU,MAAM,EAAqB,CAGvC,OAAO,GAUU,UAPD,CAChB,QACA,cAEO,EAAC,EAAQ,SAAA,CAAS,MAAO,EAAQ,YAA4B,CAGzC,CAG/B,SAAgB,EACd,EACA,EACA,EAAmC,OAAO,GAC1C,CACA,GAAM,CAAC,EAAO,GAAY,MAAe,EAAS,EAAM,KAAK,CAAC,CAAC,CAc/D,OAZA,MACS,EAAM,cAAgB,CAC3B,IAAM,EAAY,EAAS,EAAM,KAAK,CAAC,CAEvC,EAAU,GACR,EAAQ,EAAe,EAAU,CAAG,EAAgB,EACrD,EACD,CACD,CAAC,EAAO,EAAU,EAAQ,CAAC,CAE9B,EAAc,EAAM,CAEb,EAGT,SAAgB,EACd,EACA,EACA,EACA,CAGA,OAAO,EAAY,EAFF,EAAa,GAAa,EAAM,GAAM,CAAC,EAAI,CAAC,CAEzB,EAAQ,CChE9C,SAAgB,EACd,EACA,EACA,EACA,EACA,EACA,CACA,IAAI,EAAkB,EAEtB,OAAO,GAA+B,EAAK,KAAS,CAClD,OAAQ,EACR,QAAS,EACT,OAAQ,EACR,SAAU,EACV,gBAEA,KAAM,KACN,OAAQ,GACR,YAAa,OACb,kBAAmB,EACnB,eAAgB,EAEhB,UAAW,KACX,qBAAsB,KACtB,cAAe,KACf,eAAgB,KAEhB,6BAA8B,EAC9B,2BAA4B,EAC5B,sBAAuB,EACvB,oBAAqB,EAErB,QAAS,KACT,UAAW,KACX,YAAa,KACb,QAAS,KAET,oBAAsB,GAAwC,CAC5D,IAAM,EAAQ,GAAK,CAEb,EAAO,GAAS,MAAQ,EAAM,KAC9B,EACJ,GAAS,sBAAwB,EAAM,qBACnC,EAAY,GAAS,WAAa,EAAM,UACxC,EAAiB,GAAS,gBAAkB,EAAM,eAExD,GACE,CAAC,GACD,EAAK,KAAK,SAAW,GACrB,CAAC,GACD,CAAC,GACD,CAAC,EAED,OAAO,EAAI,CACT,GAAG,EACH,6BAA8B,EAC9B,2BAA4B,EAC5B,sBAAuB,EACvB,oBAAqB,EACtB,CAAC,CAGJ,IAAI,EAAgC,EAChC,EAA+B,EAC7B,EAAmB,EAAkB,EAE3C,IACE,IAAI,EAAgB,EACpB,EAAgB,EAAK,WAAW,OAChC,IACA,CACA,IAAM,EAAW,EAAK,WAAW,GAC3B,EACJ,EAAgB,EAChB,EAAS,cAAgB,EAM3B,GAJI,GAAa,IACf,EAA+B,GAG7B,EAAY,EACd,GAAiC,OAEjC,MAIJ,IAAM,EACJ,EAAK,WAAW,OAAS,EACzB,EAAK,KAAK,OAAS,EAEf,EAAgB,KAAK,MAAO,EAAoB,EAAa,EAAE,CAC/D,EAAc,KAAK,KAAM,EAAoB,EAAa,EAAE,CAG5D,EAAiB,KAAK,IAC1B,EAAkB,EAAgC,EAClD,EAAc,EACf,CAEK,EAAe,EAAiB,EAAiB,EAEjD,EAAwB,KAAK,IACjC,EACA,KAAK,MAAM,EAAiB,EAAU,CACvC,CACK,EAAsB,KAAK,IAC/B,EAAK,KAAK,OAAS,EACnB,KAAK,KAAK,EAAe,EAAU,CACpC,CACK,EACJ,EAAK,KAAK,IAAwB,cAMpC,OAHS,EADL,IAA+B,IAAA,IAAa,EACnC,CAAE,GAAG,EAAS,+BAA8B,CAG9C,CACT,GAAG,EACH,+BACA,6BACA,wBACA,sBACD,CATyD,EAY5D,aAAe,GAA0B,CACvC,GAAK,CAAC,oBAAoB,CACxB,OAGA,kBAAmB,EACnB,eAAgB,EACjB,CAAC,EAEJ,eAAiB,GAAmB,CAClC,EAAI,CAAE,SAAQ,YAAa,EAAS,WAAa,OAAQ,CAAC,EAE5D,qBACE,EACA,EACA,IACG,CAOH,GANA,EAAI,CACF,cACA,oBACA,iBACD,CAAC,CAEE,IAAgB,WAClB,OAGF,GAAM,CACJ,UACA,cACA,SACA,YACA,iBACA,wBACE,GAAK,CAEH,EAAO,GAAS,QAChB,EAAW,GAAa,QAE9B,GACE,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,EAED,OAGF,IAAM,EAAW,EAEb,IAAa,GACf,EAAS,SAAS,CAChB,IAAK,EACL,SAAU,UACX,CAAC,CAGJ,IAAM,EAAM,EAAK,cAAc,mBAAmB,EAAS,IAAI,CAE/D,GAAI,EAAE,aAAe,aACnB,OAGF,IAAM,EAAO,EAAI,UACX,EAAmB,iBAAiB,EAAI,CACxC,EAAqB,OAAO,WAChC,EAAiB,gBAClB,CACK,EAAwB,OAAO,WACnC,EAAiB,mBAClB,CAEG,EAAiB,EAAkB,EAGnC,GAAU,EAAO,EAAkB,EAAiB,IACtD,GAAkB,GAGpB,IAAM,EACJ,EAAiB,EAAiB,GAEhC,EAAO,GAAkB,EAAO,EAAY,IAC9C,EAAS,SAAS,CAEhB,IAAK,KAAK,IACR,EAAO,EAAiB,EACpB,EACE,KAAK,IACH,EAAS,EAAuB,EAChC,EACD,CACH,EAAO,EAAiB,EAAY,EACxC,EACD,CACD,SAAU,UACX,CAAC,EAGN,uBAA0B,CACxB,EAAI,CACF,YAAa,OAGb,kBAAmB,EACnB,eAAgB,EACjB,CAAC,EAEJ,kBAAoB,GAAsB,CACxC,GAAK,CAAC,oBAAoB,CAAE,YAAW,CAAC,EAE1C,6BAA+B,GAAiC,CAC9D,GAAK,CAAC,oBAAoB,CAAE,uBAAsB,CAAC,EAErD,sBAAuB,EAAuB,IAA2B,CACvE,GAAK,CAAC,oBAAoB,CAAE,gBAAe,iBAAgB,CAAC,EAE9D,iBAAmB,GAAoB,CACrC,EAAkB,EAElB,GAAK,CAAC,qBAAqB,EAE9B,EAAE,CAGL,KAAa,CACX,SAAU,EACV,SAAU,GACR,EAAqC,+BAA+B,CAExE,SAAgB,EAAQ,EAAyB,CAC/C,OAAO,EAAM,OAGf,SAAgB,EACd,EAC8B,CAC1B,KAAM,cAAgB,OAO1B,OAFE,EAAM,MAAM,KAAK,EAAM,iBAAiB,OAAO,EAAM,mBAKzD,SAAgB,EAAS,EAAyB,CAChD,OACE,EAAM,OAAS,IAAA,IACd,OAAO,EAAM,MAAM,OAAU,UAAY,EAAM,KAAK,QAAU,EAInE,SAAgB,EAAW,EAAyB,CAClD,OACE,EAAM,OAAS,MACf,EAAM,iBAAmB,MACzB,EAAM,YAAc,MACpB,EAAM,uBAAyB,KAInC,SAAgB,EAAW,EAAyB,CAClD,OAAO,EAAM,MAAM,KAAK,OAG1B,SAAgB,EAAiB,EAAyB,CACxD,OAAO,EAAM,MAAM,WAAW,OAGhC,SAAgB,GAA4B,EAAyB,CACnE,OAAO,EAAM,MAAM,0BAGrB,SAAgB,EAAW,EAAyB,CAClD,OAAO,EAAM,MAAM,UAGrB,SAAgB,EACd,EACA,EACA,CACA,OAAO,GAAG,QAAU,GAAG,MAGzB,SAAgB,GACd,EACA,EACA,CASA,OARI,GAAG,gBAAkB,GAAG,eAIxB,GAAG,OAAO,SAAW,GAAG,OAAO,OAC1B,GAGF,EACL,GAAG,OAAO,OAAO,EAAO,IACtB,EAAqB,EAAO,GAAG,OAAO,GAAO,CAC9C,CClYL,MAAa,EACX,6IAEW,EAAyB,CACpC,OACA,QACA,eACA,SACA,cACA,OACD,CCRK,EAAsB,2BACtB,GAA4B,UAC5B,GACJ,sDAEI,GAA+D,CACnE,MAAO,KACP,eAAgB,KAChB,OAAQ,KACR,cAAe,KACf,KAAM,KACP,CAED,SAAgB,GAAqB,EAAe,EAAoB,CAEtE,GAAI,CAAC,EAAM,MAAM,IAAI,CAAC,KAAM,GAAY,EAAoB,KAAK,EAAQ,CAAC,CACxE,OAAO,EAGT,IAAM,EAAY,EACf,MAAM,IAAI,CACV,IAAK,GAAY,EAAQ,QAAQ,GAAqB,GAAG,CAAC,CAC1D,KAAK,IAAI,CAMZ,OAJI,IAAa,OACR,EAGF,EACJ,MAAM,IAAI,CACV,KAAK,EAAS,EAAG,IAAa,CAC7B,IAAM,EAAgB,EAAS,OAAS,EAUxC,MAPE,CAAC,EAAoB,KAAK,EAAQ,EAEjC,GAAiB,IAAY,KAEvB,EAIP,EAAQ,QAAQ,GAA2B,GAAG,CAC9C,GAAkB,IAEpB,CACD,KAAK,IAAI,CAGd,SAAgB,GAAsB,EAAoC,CACxE,OAAO,EAAW,IAAK,IAAc,CACnC,WACA,MAAO,GAAqB,EAAO,EAAS,CAC7C,EAAE,CClBL,SAAgB,GAAoC,CAIlD,OAAO,EAFa,EADN,GAAqB,CACI,EAAc,EAAqB,CAEtC,CAmCtC,SAAgB,EACd,EAAQ,IACuD,CAC/D,IAAM,EAAQ,GAAqB,CAC7B,EAAW,EAAe,EAAO,WAAW,CAC5C,EAAqB,MACnB,GAAsB,EAAM,CAClC,CAAC,EAAM,CACR,CAMD,MAAO,CAAC,EAJY,EAAa,GAAuB,CACtD,EAAM,IAAI,CAAE,WAAU,CAAC,EACtB,EAAE,CAAC,CAEyB,EAAmB,CCzDpD,SAAS,EAAuB,CAAE,YAAyC,CAGzE,OAAO,EAAS,CAAE,MAFE,GAAgB,CAEE,CAAC,CCXzC,SAAS,EAAuB,CAC9B,YAC8B,CAC9B,IAAM,EAAQ,GAAqB,CAC7B,EAAO,EAAe,EAAO,OAAO,CACpC,EAAc,EAAe,EAAO,cAAc,CAClD,EAAY,EAAe,EAAO,YAAY,CAC9C,EAAuB,EAAe,EAAO,uBAAuB,CACpE,EAAmB,EAAe,EAAO,mBAAmB,CAC5D,EAA+B,EACnC,EACA,+BACD,CA+CD,OAAO,EAAS,CAAE,WA7CC,MAEf,CAAC,GAAM,YACP,CAAC,GACD,CAAC,GACD,CAAC,EAEM,EAAE,CAGJ,EAAK,WAAW,KAAK,EAAU,KAAW,CAC/C,SAAU,CACR,MAAO,EAAS,MAChB,KAAM,EAAS,KACf,aAAc,EAAS,aACxB,CACD,SAAU,IAAU,EACpB,aAAgB,CACd,IAAM,EAAW,EAAY,QAE7B,GAAI,CAAC,EACH,OAGF,IAAM,EACJ,EAAQ,EAAuB,EAAS,cAAgB,EAE1D,EAAS,SAAS,CAChB,IAAK,EACN,CAAC,CAEF,0BAA4B,CAC1B,EAAiB,EAAS,UAAU,EACpC,EAEL,EAAE,CACF,CACD,GAAM,WACN,EACA,EACA,EACA,EACA,EACD,CAAC,CAE4B,CAAC,CCtFjC,SAAgB,EAAQ,EAAY,EAAqB,CACvD,GAAI,OAAO,GAAG,EAAG,EAAE,CACjB,MAAO,GAYT,GARE,OAAO,GAAM,UACb,OAAO,GAAM,UACb,IAAM,MACN,IAAM,MAKJ,MAAM,QAAQ,EAAE,GAAK,MAAM,QAAQ,EAAE,CACvC,MAAO,GAGT,IAAM,EAAQ,OAAO,KAAK,EAAE,CACtB,EAAQ,OAAO,KAAK,EAAE,CAM5B,OAJI,EAAM,SAAW,EAAM,OAIpB,EAAM,MACV,GAAQ,KAAO,GAAK,EAAE,KAA2B,EAAE,GACrD,CALQ,GCnBX,MAAaA,EAEX,OAAO,OAAW,IAAcC,EAA0B,EC6B5D,SAAS,EACP,EACA,EACA,EAC2C,CAC3C,MAAO,CACL,MAAO,CAAE,GAAG,EAAO,WAAU,CAC7B,KAAM,WACN,gBAAiB,EACjB,gBAAiB,GAAY,IAAA,GAC7B,aAAc,EAAM,MACpB,cAAe,EAAW,GAAK,IAAA,GAC/B,kBAAmB,GACnB,MAAO,CACL,WAAY,8BACb,CACD,SAAU,GACX,CAGH,SAAS,EACP,EACA,EAAQ,GACiC,CACzC,MAAO,CACL,KAAO,EAAgB,IAAA,GAAR,MACf,gBAAkB,EAAmB,IAAA,GAAX,EAC1B,gBAAiB,GACjB,MAAO,CACL,QAAU,EAAoB,IAAA,GAAZ,UAClB,OAAS,EAAwC,IAAA,GAAhC,8BACjB,QAAS,OACV,CACF,CAGH,SAAS,EACP,EACA,EACuC,CACvC,MAAO,CACL,qBAAsB,GACtB,MAAO,CACL,QAAS,UACT,IAAK,EACD,QAAQ,EAAc,+CAA+C,EAAS,cAAc,iCAC5F,IAAA,GACJ,OAAQ,EACJ,kDAAkD,EAAS,UAAU,iCACrE,IAAA,GACJ,MAAO,OACP,cAAe,OACf,SAAU,WACX,CACF,CAGH,SAAS,EACP,EACA,EAAQ,GACR,EAAS,GAC2C,CACpD,MAAO,CACL,WACA,4BAA6B,GAC7B,MAAO,CACL,QAAU,EAAyB,IAAA,GAAjB,eAClB,OAAS,EAAoD,IAAA,GAA5C,0CACjB,cAAe,OACf,SAAU,EAAS,SAAW,IAAA,GAC9B,IAAK,EACN,CACF,CAGH,SAAS,EACP,EACA,EACA,EACA,EACuC,CACvC,MAAO,CACL,uBAAwB,GACxB,MAAO,CACL,SAAU,WACV,UAAW,aACX,OAAQ,QAAQ,EAAU,mCAAmC,EAAgB,6CAC7E,WAAY,QAAQ,EAAsB,mCAAmC,EAAqB,6CACnG,CACF,CAGH,SAAS,EACP,EACA,EACA,EACsC,CACtC,MAAO,CACL,gBAAiB,EACjB,gBAAiB,EACjB,iBAAkB,GAClB,MAAO,CACL,2BAA4B,EAC5B,GAAG,EACJ,CACD,KAAM,OACP,CAGH,SAAS,GAAe,EAA4B,CAClD,EAAM,gBAAgB,CAGxB,MAAM,GAAuB,GAC1B,CACC,QACA,QACA,cACA,cAK+C,CAC/C,IAAM,EAAQ,GAAqB,CAC7B,EAAW,EACf,EACC,GAAU,EAAa,EAAM,EAAE,QAAU,EAAM,MACjD,CAEK,EAAe,MAAkB,CACrC,EAAM,KAAK,CAAC,cAAc,EAAM,EAC/B,CAAC,EAAM,CAAC,CAEL,EAAqB,MAAkB,CAC3C,EAAM,KAAK,CAAC,oBAAoB,UAAW,EAAa,EAAS,EAChE,CAAC,EAAa,EAAS,CAAC,CAErB,EAAqB,MAAkB,CAC3C,EAAM,KAAK,CAAC,oBAAoB,EAC/B,EAAE,CAAC,CAEN,OACE,EAAC,EAAA,CACC,GAAI,EAAe,EAAO,EAAa,EAAS,CAChD,QAAS,EACT,cAAe,GACf,eAAgB,EAChB,eAAgB,GAChB,EAGP,CAEK,GAAqB,GACxB,CACC,MACA,QACA,cAII,CAEJ,IAAM,EAAM,EADE,GAAqB,CAGhC,GAAU,EAAM,MAAM,KAAK,GAC5B,GACD,CAOD,OAJK,EAKH,EAAC,EAAA,CAAI,GAAI,EAAa,EAAS,UAC5B,EAAI,OAAO,KAAK,EAAO,IACtB,EAAC,GAAA,CACc,cACN,QACA,QAEG,YADL,EAAM,MAEX,CACF,EACE,CAdC,MAiBZ,CAEK,GAA0B,GAC7B,CACC,iBACA,mBAII,CACJ,IAAM,EAAQ,GAAqB,CAC7B,EAAW,EACf,EACC,GAAU,EAAM,MAAM,WAAW,GAClC,EACD,CACK,EAAS,EAAe,EAAO,SAAS,CAO9C,OAJK,EAKH,EAAC,MAAA,CAAI,GAAI,EAAkB,EAAe,EAAS,UACjD,EAAC,EAAA,CACC,GAAI,EACF,CACE,MAAO,EAAS,MAChB,KAAM,EAAS,KACf,aAAc,EAAS,aACxB,CACD,GACA,EACD,CAAA,CACD,EACE,CAhBC,MAmBZ,CAEK,GAAwB,GAC3B,CACC,iBACA,MACA,WACyE,CACzE,IAAM,EAAM,EAAuB,KAAM,CACnC,EAAQ,GAAqB,CAC7B,EAAU,EAAe,EAAO,UAAU,CAC1C,EAAS,MAEX,MAAwB,EAAQ,CAAC,KAAK,CACpC,MAAO,KACP,MAAO,GACR,CAAC,CACJ,CAAC,EAAQ,CACV,CACK,EAAgC,OAC7B,CACL,MAAO,WACP,KAAM,KACP,EACD,EAAE,CACH,CACK,EAAS,EAAuB,KAAM,CACtC,EAAoB,EAAuB,KAAM,CAgDvD,OA9CA,MAAsB,CACpB,IAAM,EAAO,EAAI,SAAS,eAAe,cAGzC,GAAI,CAAC,GAAQ,CAAC,EAAO,SAAW,CAAC,EAAkB,QACjD,OAGF,IAAM,EAAiB,IAAI,eAAgB,GAAY,CACrD,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAS,EAAM,YAAY,OAE3B,CACJ,oBACA,+BACA,YACA,wBACE,EAAM,KAAK,CAEX,EAAM,SAAW,EAAO,SAAW,IAAc,GACnD,EAAkB,EAAO,CAIzB,EAAM,SAAW,EAAkB,SACnC,IAAyB,GAEzB,EAA6B,EAAO,GAGxC,CAEF,EAAe,QAAQ,EAAK,CAC5B,EAAe,QAAQ,EAAO,QAAQ,CACtC,EAAe,QAAQ,EAAkB,QAAQ,CAEjD,GAAM,CAAE,oBAAmB,gCAAiC,EAAM,KAAK,CAKvE,OAHA,EAAkB,EAAO,QAAQ,aAAa,CAC9C,EAA6B,EAAkB,QAAQ,aAAa,KAEvD,CACX,EAAe,YAAY,GAE5B,EAAE,CAAC,CAGJ,EAAC,MAAA,CACC,cAAA,GACK,MACL,MAAO,CACL,OAAQ,EACR,WAAY,SACb,WAED,EAAC,MAAA,CAAI,sBAAoB,GAAG,IAAK,WAC/B,EAAC,EAAA,CAAI,GAAI,EAAa,GAAI,GAAK,UAC5B,EAAO,KAAK,EAAO,IAClB,EAAC,EAAA,CAAkB,GAAI,EAAe,EAAO,EAAO,GAAM,CAAA,CAA9C,EAAkD,CAC9D,EACE,EACF,CACN,EAAC,MAAA,CAAI,GAAI,EAAkB,GAAG,UAC5B,EAAC,MAAA,CAAI,kCAAgC,GAAG,IAAK,WAC3C,EAAC,EAAA,CAAe,GAAI,EAAwB,EAAU,GAAK,CAAA,CAAI,EAC3D,EACF,CAAA,EACF,EAGX,CAED,SAAS,GAAqC,CAC5C,WACA,GAAG,GACkC,CACrC,OACE,EAAC,MAAA,CAAI,GAAI,YACN,EAAS,MACR,EAAC,OAAA,CAAK,MAAO,CAAE,YAAa,QAAS,UAClC,EAAS,aACR,EAAC,MAAA,CACC,IAAI,GACJ,IAAK,EAAS,KACd,MAAO,CACL,MAAO,MACP,OAAQ,MACR,UAAW,UACX,cAAe,SAChB,EACD,CAEF,EAAS,MAEN,CAER,EAAS,MAAA,EACN,CAIV,SAAS,GAA4B,CACnC,QACA,GAAG,GACyB,CAC5B,OACE,EAAC,SAAA,CAAO,KAAK,SAAS,GAAI,WACvB,EAAM,SACL,EAAC,MAAA,CACC,IAAK,EAAM,MACX,IAAK,EAAM,MACX,MAAO,CACL,MAAO,MACP,OAAQ,MACR,UAAW,UACX,cAAe,SAChB,EACD,CAEF,EAAM,OAED,CAIb,SAAS,GAA0B,CAAE,GAAG,GAAkC,CACxE,OAAO,EAAC,MAAA,CAAI,GAAI,EAAA,CAAS,CAmC3B,MAAM,GAAkB,GACrB,CAAE,QAAO,aAAY,GAAG,GAAS,IAAiB,CACjD,IAAM,EAAQ,GAAqB,CAC7B,EAAM,EAAuB,KAAM,CACnC,EAAc,EAAa,GAAmC,CAC9D,IACF,EAAI,QAAU,EACd,EAAM,IAAI,CAAE,QAAS,EAAK,CAAC,GAE5B,EAAE,CAAC,CACA,EACJ,GAAY,gBAAkB,GAC1B,EAAQ,GAAY,OAAS,GAC7B,EAAM,GAAY,KAAO,GACzB,EAAU,EAAe,EAAO,UAAU,CAC1C,EAAwB,EAC5B,EACA,wBACD,CACK,EAAsB,EAAe,EAAO,sBAAsB,CAClE,EAAY,EAAY,EAAO,EAAW,CAC1C,EAA6B,EACjC,EACA,GACA,EACD,CACK,EAAuB,MAEzB,GAA4B,OACzB,GAAU,EAAQ,EACpB,CAAC,QAAU,EAEb,CAAC,EAA4B,EAAsB,CAAC,CACjD,EAAkB,GAA4B,QAAU,EAkB9D,OAhBA,EAAoB,MAAoB,EAAI,QAAQ,CAEhD,CAAC,GAAa,CAAC,GAA8B,IAAoB,EAEjE,EAAC,MAAA,CAAI,GAAI,EAAU,EAAS,EAAG,EAAM,CAAE,GAAI,WACzC,EAAC,MAAA,CAAI,GAAI,EAAe,EAAG,EAAG,EAAG,EAAE,UACjC,EAAC,GAAA,CACiB,iBACT,QACF,OACL,EACE,EACF,CAKR,EAAC,MAAA,CACC,GAAI,EAAU,EAAS,EAAW,EAAM,CACxC,GAAI,EACJ,IAAK,WAEL,EAAC,MAAA,CACC,GAAI,EACF,EACA,EACA,EACA,EACD,WAED,EAAC,GAAA,CACiB,iBACT,QACF,OACL,CACD,MAAM,KACL,CAAE,OAAQ,EAAsB,EAAwB,EAAG,EAC1D,EAAG,IAAU,CACZ,IAAM,EAAW,EAAwB,EAIzC,OACE,EAAC,EAAA,CAAA,SAAA,CAHD,EAA2B,QAAQ,EAAS,EAIxB,GAChB,EAAC,MAAA,CACC,MAAO,CACL,OAAQ,0CACT,CAAA,CACD,CAEJ,EAAC,GAAA,CACQ,QACF,MACK,YACV,CAAA,CAAA,CAZW,EAaJ,EAGhB,CACA,MAAM,KAAK,CAAE,OAAQ,EAAiB,EAAG,EAAG,IAC3C,EAAC,GAAA,CACiB,iBAChB,cAAe,GACV,EACL,CACF,GACE,EACF,EAGX,CC3hBD,SAAS,GAAmB,CAAE,WAAU,GAAG,GAAkC,CAQ3E,OANkB,EADJ,GAAqB,CACE,EAAW,CAO9C,EAAC,OAAA,CAAK,oBAAkB,GAAG,GAAI,EAC5B,YACI,CANA,KAUX,SAAS,GAA2B,CAClC,YAGC,CAID,OAAO,EAAS,CAAE,OAFH,EADD,GAAqB,CACD,EAAQ,CAEhB,CAAC,CA4B7B,SAAS,GAAiB,CAAE,WAAU,GAAG,GAAgC,CAQvE,OANgB,EADF,GAAqB,CACA,EAAS,CAO1C,EAAC,OAAA,CAAK,kBAAgB,GAAG,GAAI,WAC1B,OAAO,GAAa,WACnB,EAAC,GAAA,CAA4B,WAAA,CAAsC,CAEnE,GAEG,CAVA,KC3EX,SAAgB,EAAW,EAAgB,CACzC,OAAO,EAAO,OAAO,EAAE,CAAC,aAAa,CAAG,EAAO,MAAM,EAAE,CCGzD,IAAI,EAA2C,KAE/C,SAAgB,GAAiB,EAAwB,CACvD,GAAI,CACF,IAAY,SACT,cAAc,SAAS,CACvB,WAAW,KAAM,CAAE,mBAAoB,GAAM,CAAC,MAE3C,EAoBR,GAjBI,CAAC,IAKL,mBAAqB,CACnB,AACE,IAAU,MAEZ,CAEF,EAAQ,OAAO,MAAQ,EACvB,EAAQ,OAAO,OAAS,EACxB,EAAQ,KAAO,OAAO,IACtB,EAAQ,aAAe,SAGnB,EAAQ,YAAY,EAAM,CAAC,OAAS,GACtC,MAAO,GAGT,EAAQ,UAAY,OACpB,EAAQ,SAAS,EAAO,EAAG,EAAE,CAE7B,IAAM,EAAO,EAAQ,aAAa,EAAG,EAAG,EAAa,EAAY,CAAC,KAElE,EAAQ,UAAU,EAAG,EAAG,EAAa,EAAY,CAEjD,EAAQ,UAAY,OACpB,EAAQ,SAAS,EAAO,EAAG,EAAE,CAE7B,IAAM,EAAM,EAAQ,aAAa,EAAG,EAAG,EAAa,EAAY,CAAC,KAGjE,IAAK,IAAI,EAAI,EAAG,EAAI,GAA+B,GAAK,EACtD,GACE,EAAK,KAAO,EAAI,IAChB,EAAK,EAAI,KAAO,EAAI,EAAI,IACxB,EAAK,EAAI,KAAO,EAAI,EAAI,GAExB,MAAO,GAIX,MAAO,GC3DT,SAAgB,GACd,EACA,EACA,EACA,CACA,GAAI,CACF,IAAM,EAAO,EAAQ,QAAQ,EAAI,CAEjC,GAAI,CAAC,EACH,MAAU,MAAM,uBAAuB,EAAI,IAAI,CAKjD,OAAO,EAFO,KAAK,MAAM,EAAK,CAER,MAChB,CACN,OAAO,MAIX,SAAgB,GAAc,EAAkB,EAAa,EAAU,CACrE,EAAQ,QAAQ,EAAK,KAAK,UAAU,EAAM,CAAC,CChB7C,SAAgB,GACd,EACkC,CAClC,MAAQ,IACN,IAAS,IAAA,GAAY,IAAA,GAAY,EAAS,EAAK,CAGnD,SAAgB,GAAY,EAA6C,CACvE,MAAQ,IAA6B,IAAS,KAAO,KAAO,EAAS,EAAK,CAG5E,SAAgB,EAAO,EAAuB,CAC5C,GAAI,OAAO,GAAS,SAClB,MAAU,OAAO,CAGnB,OAAO,EAGT,SAAgB,EAAO,EAAuB,CAC5C,GAAI,OAAO,GAAS,SAClB,MAAU,OAAO,CAGnB,OAAO,EAGT,SAAgB,GAAQ,EAAwB,CAC9C,GAAI,OAAO,GAAS,UAClB,MAAU,OAAO,CAGnB,OAAO,EAGT,SAAgB,EACd,EAGsB,CACtB,MAAQ,IAAqB,CAC3B,GAAI,OAAO,GAAS,WAAY,EAC9B,MAAU,OAAO,CAGnB,IAAM,EAAS,EAAE,CAEjB,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAS,EAAkC,GAE7C,IAAU,IAAA,IAEZ,EAAK,GAAK,IAAA,GAAU,CAGtB,EAAO,GAAkB,EAAK,GAAK,EAAM,CAG3C,OAAO,GAIX,SAAgB,EAAc,EAAwC,CACpE,MAAQ,IAAuB,CAC7B,GAAI,CAAC,MAAM,QAAQ,EAAK,CACtB,MAAU,OAAO,CAQnB,OAJI,EAAK,OAAS,GAChB,EAAS,EAAK,GAAG,CAGZ,GC/DX,MAAM,IAAwB,EAAiB,IAC7C,GAAG,EAAQ,GAAG,EAAO,YACjB,IAA0B,EAAiB,IAC/C,GAAG,EAAQ,GAAG,EAAO,gBAEjB,GAAoB,2GA6BzB,CAGY,GAAkB,GAAmB,kBAAkB,IACvD,GAAuB,qBAgCpC,eAAe,GAAU,EAAa,EAAsB,CAC1D,GAAI,CAGF,OAFiB,MAAM,MAAM,EAAK,CAAE,OAAQ,OAAQ,SAAQ,CAAC,EAE7C,QAAQ,IAAI,OAAO,MACzB,CACV,OAAO,MAIX,eAAe,GACb,EACA,EACA,EACA,CACA,GAAM,CAAC,CAAE,SAAQ,cAAc,CAAE,WAAU,iBACzC,MAAM,QAAQ,IAAI,CAChB,MAAM,GAAqB,EAAS,EAAO,CAAE,CAAE,SAAQ,CAAC,CAAC,KACvD,KAAO,KACE,CACL,OAAS,MAAM,EAAS,MAAM,CAC9B,WAAY,EAAS,QAAQ,IAAI,OAAO,CACzC,EAEJ,CACD,MAAM,GAAuB,EAAS,EAAO,CAAE,CAAE,SAAQ,CAAC,CAAC,KACzD,KAAO,KACE,CACL,SAAW,MAAM,EAAS,MAAM,CAChC,aAAc,EAAS,QAAQ,IAAI,OAAO,CAC3C,EAEJ,CACF,CAAC,CAEJ,MAAO,CACL,SACA,WACA,aACA,eACD,CAGH,eAAe,GACb,EACA,EACA,EACA,CACA,GAAM,CAAC,EAAY,GAAgB,MAAM,QAAQ,IAAI,CACnD,GAAU,GAAqB,EAAS,EAAO,CAAE,EAAO,CACxD,GAAU,GAAuB,EAAS,EAAO,CAAE,EAAO,CAC3D,CAAC,CAEF,MAAO,CACL,aACA,eACD,CAGH,SAAgB,GACd,EACuD,CAClD,KAAM,MAQX,OAJ2B,EAAM,MAAM,OACpC,GAAU,OAAO,EAAM,MAAS,SAClC,CAEyB,QACvB,EAAQ,IAAU,CACjB,IAAM,EAAW,EAAW,EAAM,MAIlC,MAFA,GAAO,GAAyC,EAAM,MAE/C,GAET,EAAE,CACH,CAGH,eAAe,GACb,EACA,EACA,EACoB,CACpB,GAAM,CAAE,SAAQ,aAAY,WAAU,gBACpC,MAAM,GAAmB,EAAS,EAAQ,EAAO,CAC7C,EAAuB,EAAS,UAAU,KAC7C,GACC,EAAS,MAAQ,gBAAkB,EAAS,MAAQ,mBACvD,CAGK,EAAiB,EAAS,OAAO,OACpC,GAAU,EAAM,MAAQ,YAC1B,CACK,EAAiB,EAAO,OAAQ,GAC7B,UAAW,EAClB,CAEI,EAAa,EAAe,IAAK,IAAW,CAChD,MAAO,EAAM,MACb,MAAO,EAAW,EAAM,QAAQ,CACjC,EAAE,CACG,EAAY,EAAS,UAAU,QAClC,EAAW,KACV,EAAU,EAAS,KAAO,EAAW,EAAS,QAAQ,CAE/C,GAET,EAAE,CACH,CAiBK,EAAuB,CAC3B,SACA,OAjBsB,EAAe,IAAK,IACnC,CACL,MAAO,EAAM,MACb,SAAU,EAAM,MAChB,QAAS,EAAM,QACf,MAAO,EAAW,EAAM,MAAM,CAC9B,KAAM,EAAM,MAAQ,EAAE,CACtB,YACG,GACC,EAAM,WAAa,EAAqB,OAC1C,IAAA,GACF,MAAO,GAA+B,EAAM,CAC7C,EACD,CAKA,aACA,YACD,CAUD,OARA,GAAW,aAAc,GAAe,EAAO,CAAE,CAC/C,KAAM,EACN,SAAU,CACR,aACA,eACD,CACF,CAAC,CAEK,EAGT,SAAS,GACP,EACA,EACiB,CACjB,IAAM,EAAgB,IAAI,IAE1B,IAAK,IAAM,KAAS,EACb,EAAc,IAAI,EAAM,QAAQ,EACnC,EAAc,IAAI,EAAM,QAAS,EAAM,MAAM,CAIjD,IAAM,EAAqB,CAAC,GAAG,EAAc,MAAM,CAAC,CAAC,MAAM,EAAG,IAAM,EAAI,EAAE,CACpE,EAAiB,EAAmB,IAAM,EAE1C,EAAuB,GAAiB,OAAO,CAErD,GAAI,OAAO,GAAiB,SAC1B,MAAO,CACL,eACA,aAAc,EACf,CAGH,IAAK,IAAM,KAAW,EAGpB,GAAI,GAFU,EAAc,IAAI,EAAQ,CAEb,CACzB,MAAO,CACL,aAAc,EACd,aAAc,EACf,CAIL,MAAO,CACL,aAAc,EACd,aAAc,EACf,CAGH,MAAM,GAA0BC,EAA0B,CACxD,aAAcC,EACd,aAAcC,GACf,CAAC,CAEI,GAAoBF,EAAoB,CAC5C,KAAMA,EAAS,CACb,OAAQG,EACR,OAAQC,EACNJ,EAAS,CACP,MAAOG,EACP,SAAUF,EACV,MAAOE,EACP,QAASF,EACT,KAAMG,EAAaD,EAAS,CAC5B,YAAaE,GAAWH,GAA+B,CACvD,MAAOG,GACLL,EAAS,CACP,MAAOG,EACP,eAAgBA,EAChB,OAAQA,EACR,cAAeA,EACf,KAAMA,EACP,CAAC,CACH,CACF,CAAC,CACH,CACD,WAAYC,EACVJ,EAAS,CACP,MAAOC,EACP,MAAOE,EACR,CAAC,CACH,CACD,UAAWH,EAAS,CAClB,MAAOG,EACP,eAAgBA,EAChB,OAAQA,EACR,cAAeA,EACf,KAAMA,EACP,CAAC,CACH,CAAC,CACF,SAAUH,EAAS,CACjB,WAAYM,GAAWH,EAAS,CAChC,aAAcG,GAAWH,EAAS,CACnC,CAAC,CACH,CAAC,CAEF,eAAsB,GAAa,CACjC,SACA,eACA,eACA,UAC0C,CAC1C,IAAM,EACJ,OAAO,GAAiB,SACpB,EACA,+CAA+C,OAAO,GAAiB,SAAW,KAAK,MAAM,EAAa,CAAG,WAC/G,EAAkB,GACpB,eACA,GACA,GACD,CACK,EAAY,GAChB,aACA,GAAe,EAAO,CACtB,GACD,CAEG,EAEJ,GAAI,CAAC,EAEH,EAAO,MAAM,GAAe,EAAS,EAAQ,EAAO,SAC3C,EAIT,EAAO,EAAU,UAIjB,GAAI,CACF,GAAM,CAAE,aAAY,gBAAiB,MAAM,GACzC,EACA,EACA,EACD,CAED,EACE,CAAC,GACD,CAAC,GACD,IAAe,EAAU,SAAS,YAClC,IAAiB,EAAU,SAAS,aAChC,MAAM,GAAe,EAAS,EAAQ,EAAO,CAC7C,EAAU,UACV,CACN,EAAO,EAAU,KAiBrB,MAZA,KAAoB,GAAmB,EAAK,OAAQ,EAAa,CACjE,GAAW,eAAgB,GAAsB,EAAgB,CAW1D,CACL,SACA,OAVqB,EAAK,OAAO,OAAQ,GAAU,CACnD,IAAM,EAAqB,EAAM,SAAW,EAAgB,aAE5D,OAAO,EAAM,YACT,GAAsB,EAAgB,aACtC,GACJ,CAKA,WAAY,EAAK,WACjB,UAAW,EAAK,UACjB,CAGH,SAAgB,GAAe,EAAwB,CASrD,OARK,GAAkB,SAAS,EAAiB,CAQ1C,GAPL,QAAQ,KACN,WAAW,EAAO,yCACnB,CAEM,MAMX,SAAgB,GAAiB,EAA4B,CAO3D,OANK,EAAW,SAAS,EAAqB,CAMvC,GALL,QAAQ,KAAK,cAAc,EAAS,uCAAuC,CAEpE,QC3ZX,SAAgB,GAAS,EAAY,EAAqB,CACxD,IAAM,EAAgB,EAAE,CAExB,GAAI,GAAQ,EACV,OAAO,EAGT,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,EAAI,EAAG,GAAK,EAC5C,EAAO,KAAK,EAAM,MAAM,EAAG,EAAI,EAAK,CAAC,CAGvC,OAAO,ECCT,SAAgB,GAAa,EAA0B,EAAiB,CACtE,GAAI,CAAC,EACH,OAAO,EAGT,IAAM,EAAa,EAAO,aAAa,CAAC,MAAM,CACxC,EAAS,IAAI,QAEnB,OAAO,EACJ,OAAQ,GAAU,CACjB,IAAI,EAAQ,EAER,EAAM,MAAM,aAAa,CAAC,SAAS,EAAW,GAChD,GAAS,IAGX,IAAK,IAAM,KAAO,EAAM,KAClB,EAAI,aAAa,CAAC,SAAS,EAAW,GACxC,GAAS,GAUb,OANI,EAAQ,GACV,EAAO,IAAI,EAAO,EAAM,CAEjB,IAGF,IACP,CACD,MAAM,EAAG,KAAO,EAAO,IAAI,EAAE,EAAI,IAAM,EAAO,IAAI,EAAE,EAAI,GAAG,CAGhE,SAAgB,GACd,EACA,EACA,EACA,EACiB,CACjB,IAAM,EAAS,GAAa,EAAK,OAAQ,EAAO,CAC1C,EAA6B,EAAE,CAC/B,EAAwC,EAAE,CAC1C,EAAsC,EAAE,CACxC,EAAuD,EAAE,CAC3D,EAAgB,EAChB,EAAgB,EAEpB,IAAK,IAAM,KAAS,EACb,EAAiB,EAAM,YAC1B,EAAiB,EAAM,UAAY,EAAE,EAGvC,EAAiB,EAAM,UAAW,KAAK,CACrC,MACE,GAAY,IAAa,QAAU,EAAM,MACrC,EAAM,MAAM,GACZ,EAAM,MACZ,MAAO,EAAM,MACb,SAAU,EAAM,SACjB,CAAC,CAGJ,IAAK,IAAM,KAAY,EAAK,WAAY,CACtC,IAAM,EAAiB,EAAiB,EAAS,OAEjD,GAAI,CAAC,GAAkB,EAAe,SAAW,EAC/C,SAGF,IAAM,EAAe,GAAM,MAAM,KAAK,EAAe,CAAE,EAAQ,CAAC,IAC7D,IAAY,CACX,gBACA,SACD,EACF,CAED,EAAK,KAAK,GAAG,EAAa,CAC1B,EAAW,KAAK,CACd,MAAO,EAAS,MAChB,UAAW,EAAa,OACxB,gBACA,KAAM,EAAS,KACf,aAAc,EAAS,aACxB,CAAC,CAEF,EAA0B,KAAK,EAAc,CAE7C,IACA,GAAiB,EAAa,OAGhC,MAAO,CACL,MAAO,EAAO,OACd,aACA,4BACA,OACA,UAAW,EAAK,UACjB,CC7GH,SAAgB,GAAK,GAAG,EAAc,ECItC,SAAgB,GACd,EACA,EACA,CACA,IAAI,EAA2D,KAE/D,GAAI,OAAO,OAAO,qBAAwB,WACxC,EAAK,OAAO,oBAAoB,EAAU,EAAQ,KAC7C,CACL,IAAM,EAAQ,KAAK,KAAK,CAExB,EAAK,OAAO,eAAiB,CAC3B,EAAS,CACP,WAAY,GACZ,kBACE,KAAK,IACH,GACC,GAAS,SAAW,KAAwB,KAAK,KAAK,CAAG,GAC3D,CACJ,CAAC,EACD,GAAgB,CAGrB,UAAa,CACP,OAAO,OAAO,oBAAuB,WACvC,OAAO,mBAAmB,EAAG,CAE7B,OAAO,aAAa,EAAG,EC5B7B,SAAgB,GACd,EACmB,CACnB,IAAM,EAAc,EAAO,EAAS,CAMpC,OAJA,MAAsB,CACpB,EAAY,QAAU,GACtB,CAEK,GAAa,GAAG,IACd,EAAY,QAAQ,GAAG,EAAK,CAClC,EAAE,CAAC,CCkBR,SAAS,GAAuB,CAC9B,eACA,eACA,eACA,oBAIC,CACD,GAAM,CAAC,EAAW,GAAgB,EAAgC,IAAA,GAAU,CACtE,EAAQ,GAAqB,CAC7B,EAAS,EAAe,EAAO,SAAS,CACxC,EAAU,EAAe,EAAO,UAAU,CAC1C,EAAW,EAAe,EAAO,WAAW,CAC5C,EAAS,EAAe,EAAO,SAAS,CAgH9C,OA9GA,MAAgB,CACd,IAAM,EAAa,IAAI,gBACjB,EAAS,EAAW,OAsF1B,OApFA,GAAa,CAAE,SAAQ,eAAc,eAAc,SAAQ,CAAC,CACzD,KAAM,GAAS,CACd,IAAI,EAAa,CAAC,GAAG,EAAK,WAAW,CACjC,EACF,KAAK,IAAI,GAAG,EAAK,WAAW,IAAK,GAAa,EAAS,MAAM,CAAC,CAAG,EAmBnE,GAjBI,GAAoB,EAAiB,OAAS,IAYhD,EAAa,CAAC,GAXiB,CAAC,GAAG,EAAiB,CAAC,MAClD,EAAG,IAAM,EAAE,MAAQ,EAAE,MACvB,CAEwB,IAAK,IAAoB,CAC9C,MAAO,EAAe,MACtB,MAAO,EAAe,MACtB,KAAM,EAAe,KACrB,aAAc,EAAe,aAC9B,EAAE,CAEgC,GAAG,EAAW,CACnD,EACE,KAAK,IAAI,GAAG,EAAW,IAAK,GAAa,EAAS,MAAM,CAAC,CAAG,GAG5D,GAAgB,EAAa,OAAS,EAAG,CAC3C,IAAM,EAAkB,IAAI,IAExB,GACF,EAAiB,QAAS,GAAmB,CAC3C,EAAgB,IACd,EAAe,IAAM,EAAe,MACpC,EAAe,MAChB,EACD,CAGJ,IAAM,EAAwC,EAAa,IACxD,GAAgB,CACf,IAAM,EACJ,EAAgB,IAAI,EAAY,UAAY,EAAE,EAC9C,EAAY,UACZ,EAEF,MAAO,CACL,MAAO,EAAY,MACnB,SAAU,EACV,MAAO,EAAY,MACnB,QAAS,GAAgB,GACzB,KAAM,EAAY,MAAQ,EAAE,CAC5B,YAAa,IAAA,GACb,MAAO,IAAA,GACP,SAAU,EAAY,SACvB,EAEJ,CAEyB,EAAa,KACpC,GAAU,EAAM,WAAa,IAAA,GAC/B,GAGC,EAAa,CACX,GAAG,EACH,CAAE,MAAO,EAAqB,MAAO,SAAU,CAChD,EAGH,EAAa,CACX,GAAG,EACH,OAAQ,CAAC,GAAG,EAAK,OAAQ,GAAG,EAAiB,CAC7C,aACD,CAAC,MAEF,EAAa,CACX,GAAG,EACH,aACD,CAAC,EAEJ,CACD,MAAO,GAAU,CACX,EAAO,SACV,QAAQ,MAAM,EAAM,EAEtB,KAES,CACX,EAAW,OAAO,GAEnB,CAAC,EAAc,EAAc,EAAQ,EAAc,EAAiB,CAAC,CAExE,MAAgB,CACT,KAIL,OAAO,OACC,CACJ,EACG,KAAK,CACL,aACC,GAAmB,EAAW,EAAS,EAAU,EAAO,CACzD,EAEL,CAAE,QAAS,IAAK,CACjB,EACA,CAAC,EAAW,EAAS,EAAU,EAAO,CAAC,CAEnC,KA0BT,MAAM,GAAkB,GAEpB,CACE,SAAS,KACT,UAAU,EACV,WAAW,OACX,gBAAgB,GAChB,eACA,eACA,eACA,mBACA,iBACA,gBACA,WACA,QACA,SAAS,GACT,GAAG,GAEL,IACG,CACH,IAAM,EAAsB,GAAkB,EAAc,CACtD,EAAQ,MACZ,EACE,EACA,GAAe,EAAO,CACtB,EACA,EACA,GAAiB,EAAS,CAC3B,CACF,CACK,CAAC,EAAiB,GAAoB,EAAS,GAAM,CACrD,EAAM,EAAuB,KAAM,CACnC,GAAc,EAAa,GAAmC,CAC9D,IACF,EAAI,QAAU,EACd,EAAM,IAAI,CAAE,QAAS,EAAK,CAAC,GAE5B,EAAE,CAAC,CAEN,MAAsB,CACpB,EAAM,IAAI,CAAE,OAAQ,GAAe,EAAO,CAAE,CAAC,EAC5C,CAAC,EAAO,CAAC,CAEZ,MAAsB,CACpB,EAAM,IAAI,CAAE,UAAS,CAAC,EACrB,CAAC,EAAQ,CAAC,CAEb,MAAsB,CACpB,EAAM,IAAI,CAAE,SAAQ,CAAC,EACpB,CAAC,EAAO,CAAC,CAEZ,MAAsB,CACpB,EAAM,IAAI,CAAE,SAAU,GAAiB,EAAS,CAAE,CAAC,EAClD,CAAC,EAAS,CAAC,CAEd,IAAM,EAAqB,EACxB,GAA2C,CAC1C,IAAiB,EAAM,CAEvB,GAAM,CAAE,YAAW,eAAgB,EAAM,KAAK,CAExC,EACJ,EAAM,SAAW,GAAW,SAC5B,EAAM,OAAO,aAAa,mBAAmB,CAEzC,EACJ,EAAM,SAAW,GAAa,SAC9B,EAAM,OAAO,aAAa,qBAAqB,CAE5C,EAAM,oBAAoB,GAC7B,EAAiB,GAAY,EAAW,CAEnC,EAAM,oBAAoB,GAC7B,EAAiB,GAAY,EAAW,CAEpC,EACF,EAAM,KAAK,CAAC,oBAAoB,WAAY,EAAG,EAAE,CACxC,GAAY,EAAM,KAAK,CAAC,SAAW,IAC5C,EAAM,IAAI,CAAE,YAAa,OAAQ,CAAC,IAK1C,CAAC,EAAe,CACjB,CAEK,EAAoB,EACvB,GAA2C,CAC1C,IAAgB,EAAM,CAGpB,CAAC,EAAM,oBAAoB,EAC3B,CAAC,EAAM,cAAc,SAAS,EAAM,cAAc,EAElD,EAAiB,GAAM,EAG3B,CAAC,EAAc,CAChB,CA6ND,OA3NA,MAAsB,CACf,GACH,EAAM,KAAK,CAAC,oBAAoB,EAEjC,CAAC,EAAgB,CAAC,CAErB,EAAoB,MAAoB,EAAI,QAAQ,CAEpD,MAAgB,CACd,GAAI,CAAC,EACH,OAGF,SAAS,EAAc,EAAsB,CAC3C,GACE,EAAM,kBACL,CAAC,EAAM,IAAI,WAAW,QAAQ,EAAI,EAAM,MAAQ,QAEjD,OAGF,GAAM,CACJ,OACA,gBACA,sBACA,cACA,oBACA,kBACE,EAAM,KAAK,CAGf,GAAI,EAAM,MAAQ,QAAS,CACzB,IAAM,EAAc,EAAa,EAAM,KAAK,CAAC,CAEzC,IACF,EAAM,gBAAgB,CAEtB,EAAc,EAAY,EAK9B,GAAI,EAAM,IAAI,WAAW,QAAQ,CAAE,CACjC,IAAI,EAAc,EACd,EAAW,EAIf,GAFA,EAAM,gBAAgB,CAElB,IAAgB,OAAQ,CAC1B,GAAI,GAAM,MAAQ,EAAK,KAAK,OAAS,EACnC,OAAQ,EAAM,IAAd,CACE,IAAK,YACH,GAAI,IAAgB,EAAG,CACrB,IAAM,EAAmB,EAAW,EAC9B,EAAc,EAAK,KAAK,GAG1B,IACF,EAAW,EACX,EAAc,EAAY,OAAO,OAAS,QAI5C,IAGF,MAGF,IAAK,aACH,GAAI,IAAgB,EAAK,KAAK,GAAW,OAAO,OAAS,EAAG,CAC1D,IAAM,EAAe,EAAW,EAChB,EAAK,KAAK,KAIxB,EAAW,EACX,EAAc,QAIhB,GAAe,EAGjB,MAGF,IAAK,UAAW,CACd,IAAM,EAAc,EAAK,KAAK,EAAW,GAGrC,IACF,IAGK,EAAY,OAAO,KACtB,EAAc,EAAY,OAAO,OAAS,IAI9C,MAGF,IAAK,YAAa,CAChB,IAAM,EAAU,EAAK,KAAK,EAAW,GAGjC,IACF,GAAY,EAGP,EAAQ,OAAO,KAClB,EAAc,EAAQ,OAAO,OAAS,IAI1C,OAKN,EAAoB,WAAY,EAAa,EAAS,MAEtD,EAAoB,WAAY,EAAG,EAAE,EAO3C,OAFA,SAAS,iBAAiB,UAAW,EAAc,KAEtC,CACX,SAAS,oBAAoB,UAAW,EAAc,GAEvD,CAAC,EAAgB,CAAC,CAErB,MAAsB,CACpB,IAAI,EAA2D,KAC3D,EAA6D,KAC7D,EAAmD,KACnD,EACF,KAEI,EAAc,EAAM,UAAW,GAAU,CAExC,EAAI,UAIL,IAA0B,EAAM,gBAClC,EAAwB,EAAM,cAE9B,EAAI,QAAQ,MAAM,YAChB,6BACA,GAAG,EAAM,cAAc,IACxB,EAGC,IAA2B,EAAM,iBACnC,EAAyB,EAAM,eAE/B,EAAI,QAAQ,MAAM,YAChB,8BACA,GAAG,EAAM,eAAe,IACzB,EAGC,IAAsB,EAAM,YAC9B,EAAoB,EAAM,UAE1B,EAAI,QAAQ,MAAM,YAChB,yBACA,GAAG,EAAM,UAAU,IACpB,EAGC,IAAiC,EAAM,uBACzC,EAA+B,EAAM,qBAErC,EAAI,QAAQ,MAAM,YAChB,qCACA,GAAG,EAAM,qBAAqB,IAC/B,IAEH,CAEI,CAAE,gBAAe,iBAAgB,YAAW,wBAChD,EAAM,KAAK,CA8Bb,OA5BI,GACF,EAAI,QAAQ,MAAM,YAChB,6BACA,GAAG,EAAc,IAClB,CAGC,GACF,EAAI,QAAQ,MAAM,YAChB,8BACA,GAAG,EAAe,IACnB,CAGC,GACF,EAAI,QAAQ,MAAM,YAChB,yBACA,GAAG,EAAU,IACd,CAGC,GACF,EAAI,QAAQ,MAAM,YAChB,qCACA,GAAG,EAAqB,IACzB,CAGI,GACN,EAAE,CAAC,CAGJ,EAAC,MAAA,CACC,eAAc,EAAkB,GAAK,IAAA,GACrC,iBAAe,GACf,cAAe,EACf,eAAgB,EAChB,GAAI,EACJ,IAAK,GACL,MACE,CACE,yBAA0B,EAC1B,GAAG,EACJ,UAGH,EAAC,EAAA,CAAgC,kBAC/B,EAAC,GAAA,CACmB,mBACJ,eACA,eACA,gBACd,CACD,EAAA,EACwB,EACvB,EAGX,CCxeK,GAAoB,GACvB,CAAE,QAAO,eAAc,WAAU,GAAG,GAAS,IAAiB,CAC7D,IAAM,EAAQ,GAAqB,CAC7B,EAAM,EAAyB,KAAM,CACrC,EAAc,EAAa,GAAqC,CAChE,IACF,EAAI,QAAU,EACd,EAAM,IAAI,CAAE,UAAW,EAAK,CAAC,GAE9B,EAAE,CAAC,CACA,EAAe,OAAO,GAAU,SAChC,EAAgB,EAAO,EAAa,CAE1C,MAAgB,CAEZ,QAAQ,IAAI,WAAa,cACzB,EAAc,UAAY,GAE1B,QAAQ,KACN,uCACE,EAAgB,aAAe,eAChC,MAAM,EAAe,aAAe,eAAe,GACrD,CAGH,EAAc,QAAU,GACvB,CAAC,EAAa,CAAC,CAGlB,MAAsB,CACpB,EAAM,IAAI,CACR,OACE,OAAO,GAAU,SACb,EACA,OAAO,GAAiB,SACtB,EACA,GACT,CAAC,EACD,EAAE,CAAC,CAGN,MAAsB,CAChB,OAAO,GAAU,UACnB,EAAM,KAAK,CAAC,eAAe,EAAM,EAElC,CAAC,EAAM,CAAC,CAEX,IAAM,EAAe,EAClB,GAA8C,CAC7C,IAAW,EAAM,CAEZ,EAAM,oBAAoB,EAC7B,EAAM,KAAK,CAAC,eAAe,EAAM,OAAO,MAAM,EAGlD,CAAC,EAAS,CACX,CAID,OAFA,EAAoB,MAAoB,EAAI,QAAQ,CAGlD,EAAC,QAAA,CACC,eAAe,MACf,aAAa,MACb,YAAY,MACZ,aAAa,OACb,mBAAiB,GACjB,YAAY,UACZ,WAAY,GACZ,KAAK,SACL,GAAI,EACU,eACd,SAAU,EACV,IAAK,EACE,SACP,EAGP,CCpFK,GAA8B,GAKhC,CAAE,QAAO,UAAS,aAAc,EAAY,mBAAoB,GAAG,GACnE,IACG,CAEH,IAAM,EAAY,EADJ,GAAqB,CACE,EAAY,EAAQ,CACnD,CAAC,EAAU,EAAa,GAAsB,EAAY,EAAM,CAEhE,EAAyB,MAE3B,KAAK,IACH,EACA,EAAmB,UAChB,GAAc,EAAU,WAAa,EACvC,CACF,CACH,CAAC,EAAU,EAAmB,CAC/B,CAEK,EAAoB,EAAmB,GAKvC,EAHJ,GACG,EAAyB,GAAK,EAAmB,QAEX,SAErC,EACJ,IAAiB,OAAS,IAAA,GAAY,IAAY,GAE9C,EAAc,EACjB,GAA8C,CAC7C,IAAU,EAAM,CAEX,EAAM,oBAAoB,EAC7B,EAAY,EAAa,EAG7B,CAAC,EAAS,EAAa,EAAa,CACrC,CAED,OACE,EAAC,SAAA,CACC,KAAK,SACL,GAAI,EACJ,aACE,GAAa,EAAoB,KAAK,EAAkB,GAAK,IAE/D,YAAU,SACV,+BAA6B,GAC7B,QAAS,EACT,IAAK,WAEJ,EAAkB,OACZ,EAGd,CA8CD,SAAS,GAAoB,CAAE,WAAU,SAAmC,CAC1E,GAAM,CAAC,EAAU,EAAa,GAAsB,EAAY,EAAM,CAEtE,OAAO,EAAS,CAAE,WAAU,cAAa,qBAAoB,CAAC,CCnIhE,MAAM,GAAuB,MAAW,CACtC,IAAM,EAAc,GAAgB,CAMpC,OAJK,EAKH,EAAC,MAAA,CACC,YAAU,SACV,MAAO,CACL,OAAQ,EACR,KAAM,mBACN,OAAQ,EACR,OAAQ,GACR,SAAU,SACV,QAAS,EACT,SAAU,WACV,WAAY,SACZ,MAAO,EACP,SAAU,SACX,UAEA,EAAY,OACT,CApBC,MAsBT,CAiBI,GAAsB,GAGzB,CAAE,WAAU,WAAU,YAAW,QAAO,GAAG,GAAS,IAAiB,CACtE,IAAM,EAAQ,GAAqB,CAC7B,EAAM,EAAuB,KAAM,CACnC,EAAc,EAAa,GAAmC,CAC9D,IACF,EAAI,QAAU,EACd,EAAM,IAAI,CAAE,YAAa,EAAK,CAAC,GAEhC,EAAE,CAAC,CACA,EAAY,EAAY,EAAO,EAAW,CAC1C,EAAkB,EAAY,EAAO,EAAiB,CAEtD,EAAe,EAClB,GAAwC,CACvC,IAAW,EAAM,CAEjB,EAAM,KAAK,CAAC,iBAAiB,EAAM,cAAc,UAAU,EAE7D,CAAC,EAAS,CACX,CAiCD,OA/BA,MAAsB,CAEpB,GAAI,CAAC,EAAI,QACP,OAGF,IAAM,EAAiB,IAAI,gBAAgB,CAAC,KAAW,CACrD,IAAM,EAAQ,GAAO,cAAc,IAAI,YAAc,EAC/C,EAAS,GAAO,cAAc,IAAI,WAAa,EAE/C,CAAE,uBAAsB,iBAAgB,iBAC5C,EAAM,KAAK,EAET,IAAmB,GAAU,IAAkB,IACjD,EAAqB,EAAO,EAAO,EAErC,CAQF,OANA,EAAe,QAAQ,EAAI,QAAQ,CAEnC,EACG,KAAK,CACL,qBAAqB,EAAI,QAAQ,YAAa,EAAI,QAAQ,aAAa,KAE7D,CACX,EAAe,YAAY,GAE5B,EAAE,CAAC,CAEN,EAAoB,MAAoB,EAAI,QAAQ,CAGlD,EAAC,MAAA,CACC,qBAAmB,GACnB,GAAI,EACJ,SAAU,EACV,IAAK,EACL,MAAO,CACL,SAAU,WACV,UAAW,aACX,QAAS,eACT,qBACE,OAAO,GAAc,UAAY,OAAO,GAAoB,SACxD,8CAA8C,EAAU,mCAAmC,EAAgB,6CAC3G,IAAA,GACN,UAAW,OACX,mBAAoB,UACpB,gBAAiB,SACjB,WAAY,kBACZ,GAAG,EACJ,WAED,EAAC,GAAA,EAAA,CAAuB,CACvB,EAAA,EACG,EAER,2KC7HF,GAAgB,YAAc,mBAC9B,GAAkB,YAAc,qBAChC,GAAoB,YAAc,uBAClC,GAAgB,YAAc,mBAC9B,GAEE,YAAc,sBAChB,GAEE,YAAc,oBAChB,GAA4B,YAAc,+BAC1C,EAIE,YAAc,0BAChB,EAIE,YAAc,0BAChB,GAEE,YAAc"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@devoxin/emoji-picker",
3
+ "version": "1.0.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+ssh://git@github.com/exhaled/emoji-picker.git"
7
+ },
8
+ "main": "./dist/index.cjs",
9
+ "peerDependencies": {
10
+ "react": "^18 || ^19",
11
+ "typescript": ">=5.1.0"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "import": {
16
+ "types": "./dist/index.d.mts",
17
+ "default": "./dist/index.mjs"
18
+ },
19
+ "require": {
20
+ "types": "./dist/index.d.cts",
21
+ "default": "./dist/index.cjs"
22
+ }
23
+ }
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/exhaled/emoji-picker/issues"
27
+ },
28
+ "description": "A lightweight, unstyled, and composable emoji picker for React.",
29
+ "files": [
30
+ "dist/**",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "homepage": "https://github.com/exhaled/emoji-picker",
35
+ "keywords": [
36
+ "emoji",
37
+ "emoji picker",
38
+ "react",
39
+ "unstyled",
40
+ "component",
41
+ "emojibase",
42
+ "liveblocks"
43
+ ],
44
+ "license": "MIT",
45
+ "packageManager": "bun@1.3.1",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "typescript": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "scripts": {
55
+ "dev": "tsdown --watch",
56
+ "build": "tsdown --minify",
57
+ "test": "vitest run --silent",
58
+ "test:watch": "vitest watch --silent",
59
+ "test:coverage": "bun run test -- --coverage",
60
+ "format": "biome check --write",
61
+ "lint": "bun run lint:tsc && bun run lint:biome && bun run lint:package",
62
+ "lint:tsc": "tsc --noEmit",
63
+ "lint:biome": "biome lint",
64
+ "lint:package": "publint --strict && attw --pack",
65
+ "release": "release-it --config ../../.release-it.json"
66
+ },
67
+ "sideEffects": false,
68
+ "type": "module",
69
+ "types": "./dist/index.d.mts"
70
+ }