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
JavaScript
// 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
};