UNPKG

react-native-emojis-picker

Version:

A simple emoji picker for React Native and Expo. It's built with TypeScript and uses Reanimated 2 for smooth animations.

358 lines (349 loc) 9.24 kB
// src/components/modal/modal.tsx import { nanoid } from "nanoid/non-secure"; import { useMemo, useState } from "react"; import { Portal } from "@gorhom/portal"; import { FlatList, KeyboardAvoidingView, Platform, StyleSheet as StyleSheet4, View as View2, Text as Text3, SafeAreaView } from "react-native"; import { BlurView } from "expo-blur"; // src/components/modal/filterBar.tsx import { StyleSheet, View, Text, TouchableOpacity } from "react-native"; // src/helper.ts import e from "emoji-datasource"; var emojis = e; var Categories = [ { symbol: "\u{1F600}", key: "Smileys & Emotion" }, { symbol: "\u{1F9D1}", key: "People & Body" }, { symbol: "\u{1F984}", key: "Animals & Nature" }, { symbol: "\u{1F354}", key: "Food & Drink" }, { symbol: "\u26BE\uFE0F", key: "Activities" }, { symbol: "\u2708\uFE0F", key: "Travel & Places" }, { symbol: "\u{1F4A1}", key: "Objects" }, { symbol: "\u{1F523}", key: "Symbols" }, { symbol: "\u{1F1F8}\u{1F1E6}", key: "Flags" } ]; function charFromUtf16(utf16) { return String.fromCodePoint( ...utf16.split("-").map((u) => "0x" + u) ); } function charFromEmojiObject(obj) { return charFromUtf16(obj.unified); } var filteredEmojis = emojis.filter((e2) => !e2["obsoleted_by"]); // src/constants.ts import { Dimensions } from "react-native"; var { width: screenWidth, height: screenHeight } = Dimensions.get("window"); // src/components/modal/filterBar.tsx import { jsx } from "react/jsx-runtime"; function FilterBar({ onPress, selectedCategory, categories, darkMode }) { const result = Categories.filter( (item) => categories.find((el) => el === item.key) ); const size = Math.floor((screenWidth - 80) / 9); return /* @__PURE__ */ jsx( View, { style: [ styles.wrapper, { borderColor: darkMode ? "#5C5470" : "#f2f2f2" } ], children: result.map((category, index) => /* @__PURE__ */ jsx( TouchableOpacity, { onPress: () => onPress(category.key), style: { opacity: selectedCategory === category.key ? 1 : 0.3, width: size, height: size }, children: /* @__PURE__ */ jsx(Text, { style: styles.icon, children: category.symbol }) }, index )) } ); } var styles = StyleSheet.create({ wrapper: { flexDirection: "row", alignItems: "center", paddingHorizontal: 20, paddingBottom: 10, borderBottomWidth: 2 }, icon: { fontSize: screenHeight * 0.025 } }); // src/components/modal/search.tsx import { StyleSheet as StyleSheet2, TextInput } from "react-native"; import { jsx as jsx2 } from "react/jsx-runtime"; function Search({ darkMode, ...props }) { return /* @__PURE__ */ jsx2( TextInput, { style: [styles2.search, darkMode && { backgroundColor: "#5C5470" }], placeholderTextColor: "#8E8E93", ...props } ); } var styles2 = StyleSheet2.create({ search: { paddingHorizontal: 10, paddingVertical: 15, marginHorizontal: 20, marginVertical: 10, borderRadius: 10, backgroundColor: "#f2f2f2" } }); // src/components/modal/emojiCell.tsx import { Text as Text2, StyleSheet as StyleSheet3, TouchableOpacity as TouchableOpacity2 } from "react-native"; import { jsx as jsx3 } from "react/jsx-runtime"; function EmojiCell({ emoji, onPress, colSize }) { return /* @__PURE__ */ jsx3( TouchableOpacity2, { style: { ...styles3.wrapper, width: colSize, height: colSize }, onPress: () => onPress(emoji), children: /* @__PURE__ */ jsx3(Text2, { style: { fontSize: colSize - 5 }, children: charFromEmojiObject(emoji) }) } ); } var styles3 = StyleSheet3.create({ wrapper: { borderRadius: 10, justifyContent: "center", alignItems: "center" } }); // src/components/modal/modal.tsx import Animated, { FadeIn, FadeInDown, FadeOut, FadeOutDown } from "react-native-reanimated"; import { jsx as jsx4, jsxs } from "react/jsx-runtime"; var AnimatedBlurView = Animated.createAnimatedComponent(BlurView); function Modal({ intensityBlur = 20, columns = 10, onEmojiSelected, onPressOutside, categories = [ "Smileys & Emotion", "Activities", "Animals & Nature", "Flags", "Food & Drink", "Objects", "People & Body", "Symbols", "Travel & Places" ], position = "center", darkMode = false, autoFocusSearch = false }) { const [search, setSearch] = useState(""); const [selectedCategory, setSelectedCategory] = useState(categories[0]); const key = useMemo(() => `modal-${nanoid()}`, []); const colSize = Math.floor((screenWidth - 80) / columns); const positionStyle = useMemo(() => { switch (position) { case "top": return "flex-start"; case "bottom": return "flex-end"; default: return "center"; } }, [position]); return /* @__PURE__ */ jsx4(Portal, { name: key, children: /* @__PURE__ */ jsx4(SafeAreaView, { style: styles4.safeArea, children: /* @__PURE__ */ jsxs( KeyboardAvoidingView, { keyboardVerticalOffset: Platform.OS === "ios" ? 10 : 0, style: [styles4.container, { justifyContent: positionStyle }], behavior: Platform.OS === "ios" ? "padding" : "height", children: [ /* @__PURE__ */ jsx4( AnimatedBlurView, { entering: FadeIn, exiting: FadeOut, onTouchStart: onPressOutside, style: styles4.blur, intensity: intensityBlur } ), /* @__PURE__ */ jsxs( Animated.View, { entering: FadeInDown.springify(), exiting: FadeOutDown, style: [styles4.modal, darkMode && { backgroundColor: "#352F44" }], children: [ /* @__PURE__ */ jsx4( FilterBar, { darkMode, categories, onPress: (key2) => setSelectedCategory(key2), selectedCategory } ), /* @__PURE__ */ jsx4( Search, { darkMode, autoFocus: autoFocusSearch, placeholder: "Search..", value: search, onChangeText: (value) => setSearch(value) } ), !search && /* @__PURE__ */ jsx4( Text3, { style: [styles4.title, { color: darkMode ? "white" : "black" }], children: selectedCategory } ), /* @__PURE__ */ jsx4( FlatList, { numColumns: columns, showsVerticalScrollIndicator: false, ListEmptyComponent: /* @__PURE__ */ jsx4(View2, { style: styles4.noResultWrapper, children: /* @__PURE__ */ jsx4(Text3, { style: styles4.noResultText, children: "No Results" }) }), contentContainerStyle: styles4.flatlistContainer, data: emojis.filter((item) => { if (search === "") { return item.category === selectedCategory; } else { return item.name.includes(search.toLocaleUpperCase()); } }), keyExtractor: (_, index) => index.toString(), renderItem: ({ item }) => /* @__PURE__ */ jsx4( EmojiCell, { colSize, emoji: item, onPress: (emoji) => onEmojiSelected(charFromEmojiObject(emoji)) } ) } ) ] } ) ] } ) }) }, key); } var styles4 = StyleSheet4.create({ safeArea: { height: screenHeight, width: screenWidth, position: "absolute" }, container: { zIndex: 1e3, alignItems: "center", height: "100%", width: "100%" }, blur: { position: "absolute", zIndex: 1, height: screenHeight, width: screenWidth }, modal: { zIndex: 10, paddingVertical: 20, height: "auto", maxHeight: screenHeight / 2, width: screenWidth - 40, borderRadius: 20, backgroundColor: "white", // shadow shadowColor: "#000", shadowOffset: { width: 2, height: 2 }, shadowOpacity: 0.3, shadowRadius: 30 }, title: { marginLeft: 20, fontSize: 20, marginVertical: 10 }, flatlistContainer: { marginHorizontal: 20 }, noResultWrapper: { marginVertical: 10, flex: 1, justifyContent: "center", alignItems: "center" }, noResultText: { fontSize: 16, color: "#666" } }); // src/components/provider/provider.tsx import { PortalProvider } from "@gorhom/portal"; import { jsx as jsx5 } from "react/jsx-runtime"; function Provider({ children }) { return /* @__PURE__ */ jsx5(PortalProvider, { children }); } export { Modal as EmojiModal, Provider as EmojiProvider };