build: create many optional props and general improve
This commit is contained in:
parent
d20174947f
commit
5cab329d57
68
README.md
68
README.md
|
@ -2,26 +2,70 @@
|
|||
|
||||
.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
npm install react-native-emoji-keyboard
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import EmojiKeyboard from "react-native-emoji-keyboard";
|
||||
import EmojiPicker from '{package-name}';
|
||||
|
||||
// ...
|
||||
export default function App() {
|
||||
const [isOpen, setIsOpen] = React.useState<boolean>(false);
|
||||
|
||||
const result = await EmojiKeyboard.multiply(3, 7);
|
||||
const handlePick = (emojiObject: EmojiType) => {
|
||||
console.log(emojiObject);
|
||||
/* example emojiObject = { {
|
||||
"emoji": "❤️",
|
||||
"name": "red heart",
|
||||
"slug": "red_heart",
|
||||
"skin_tone_support": false,
|
||||
"unicode_version": "0.6",
|
||||
"emoji_version": "0.6"
|
||||
},
|
||||
*/
|
||||
};
|
||||
|
||||
return (
|
||||
<EmojiPicker
|
||||
onEmojiSelected={handleSelect}
|
||||
open={isOpen}
|
||||
onClose={() => setIsOpen(false)} />
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
## Installation
|
||||
```sh
|
||||
yarn add {package-name}
|
||||
```
|
||||
|
||||
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
||||
or
|
||||
|
||||
```sh
|
||||
npm install {package-name}
|
||||
```
|
||||
## Full Example
|
||||
```js
|
||||
TODO
|
||||
```
|
||||
## Accepted props (current implemented)
|
||||
| Name | Type | Default Value | Required | Description |
|
||||
|---|---|---|---|---|
|
||||
| onEmojiSelected | function | undefined | yes | Callback on emoji selected |
|
||||
| open | boolean | false | yes | Opens modal picker |
|
||||
| onClose | function | undefined | yes | Request close modal *runs when onEmojiSelected or backdrop pressed* |
|
||||
| emojiSize | number | 28 | no | Custom emoji size |
|
||||
| headerStyles | TextStyle | {} | no | Override category name styles |
|
||||
| knobStyles | ViewStyle | {} | no | Override knob styles |
|
||||
| containerStyles | ViewStyle | {} | no | Override container styles |
|
||||
| hideHeader | boolean | false | no | Hide category names |
|
||||
| expandable | boolean | true | no | Show knob and enable expand on swipe up |
|
||||
| defaultHeight | number | 0.4 | no | Specify collapsed container height (1 is full screen height) |
|
||||
| expandedHeight | number | 0.8 | no | Specify expanded container height (1 is full screen height) *only if expandable is true* |
|
||||
| backdropColor | string | "#00000055" | no | Change backdrop color and alpha |
|
||||
## License
|
||||
**MIT**
|
||||
|
||||
MIT
|
||||
<br /><br /><br />
|
||||
## TODO
|
||||
categories => Specify displayed categories
|
||||
|
||||
language => Use translation
|
|
@ -21,7 +21,7 @@ export default function App() {
|
|||
</TouchableOpacity>
|
||||
<EmojiPicker
|
||||
onEmojiSelected={handlePick}
|
||||
isOpen={isModalOpen}
|
||||
open={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
|
|
|
@ -62,7 +62,9 @@ export const EmojiKeyboard = () => {
|
|||
}, [ctx, scrollNav]);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, ctx.containerStyles]}>
|
||||
<View
|
||||
style={[styles.container, styles.containerShadow, ctx.containerStyles]}
|
||||
>
|
||||
<Animated.FlatList
|
||||
data={CATEGORIES}
|
||||
keyExtractor={(item: CategoryTypes) => item}
|
||||
|
@ -88,5 +90,14 @@ const styles = StyleSheet.create({
|
|||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 16,
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
containerShadow: {
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.15,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowRadius: 5,
|
||||
elevation: 10,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
import * as React from 'react';
|
||||
import { Animated, useWindowDimensions, StyleSheet } from 'react-native';
|
||||
import { Animated, useWindowDimensions } from 'react-native';
|
||||
import { EmojiKeyboard } from './EmojiKeyboard';
|
||||
import { Knob } from './components/Knob';
|
||||
import { KeyboardProvider } from './KeyboardProvider';
|
||||
import { defaultKeyboardContext, KeyboardProvider } from './KeyboardProvider';
|
||||
import type { KeyboardProps } from './KeyboardContext';
|
||||
import type { EmojiType } from './types';
|
||||
import { ModalWithBackdrop } from './components/ModalWithBackdrop';
|
||||
|
||||
type EmojiPickerProps = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const EmojiPicker = ({
|
||||
onEmojiSelected,
|
||||
isOpen,
|
||||
open,
|
||||
onClose,
|
||||
expandable = defaultKeyboardContext.expandable,
|
||||
defaultHeight = defaultKeyboardContext.defaultHeight,
|
||||
...props
|
||||
}: EmojiPickerProps & KeyboardProps) => {
|
||||
}: KeyboardProps) => {
|
||||
const { height: screenHeight } = useWindowDimensions();
|
||||
const offsetY = React.useRef(new Animated.Value(0)).current;
|
||||
const height = React.useRef(new Animated.Value(screenHeight * 0.4)).current;
|
||||
const height = React.useRef(
|
||||
new Animated.Value(screenHeight * defaultHeight)
|
||||
).current;
|
||||
const translateY = React.useRef(new Animated.Value(0)).current;
|
||||
|
||||
React.useEffect(() => {
|
||||
Animated.timing(translateY, {
|
||||
toValue: isOpen ? 0 : screenHeight,
|
||||
toValue: open ? 0 : screenHeight,
|
||||
useNativeDriver: true,
|
||||
duration: 500,
|
||||
}).start();
|
||||
}, [isOpen, screenHeight, translateY]);
|
||||
}, [open, screenHeight, translateY]);
|
||||
|
||||
const close = () => {
|
||||
height.setValue(screenHeight * 0.4);
|
||||
|
@ -43,18 +42,22 @@ export const EmojiPicker = ({
|
|||
onEmojiSelected(emoji);
|
||||
close();
|
||||
}}
|
||||
isOpen={isOpen}
|
||||
open={open}
|
||||
onClose={close}
|
||||
expandable={expandable}
|
||||
defaultHeight={defaultHeight}
|
||||
{...props}
|
||||
>
|
||||
<ModalWithBackdrop isOpen={isOpen} backdropPress={close}>
|
||||
<ModalWithBackdrop isOpen={open} backdropPress={close}>
|
||||
<>
|
||||
{expandable && (
|
||||
<Knob height={height} offsetY={offsetY} onClose={onClose} />
|
||||
)}
|
||||
<Animated.View
|
||||
style={[
|
||||
{
|
||||
height: Animated.subtract(height, offsetY),
|
||||
},
|
||||
styles.container,
|
||||
]}
|
||||
>
|
||||
<EmojiKeyboard />
|
||||
|
@ -64,16 +67,3 @@ export const EmojiPicker = ({
|
|||
</KeyboardProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modalContainer: { flex: 1, justifyContent: 'flex-end' },
|
||||
container: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 16,
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.15,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowRadius: 5,
|
||||
},
|
||||
backdrop: { backgroundColor: '#00000055' },
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import type { ViewStyle } from 'react-native';
|
||||
import type { TextStyle, ViewStyle } from 'react-native';
|
||||
import {
|
||||
defaultKeyboardContext,
|
||||
defaultKeyboardValues,
|
||||
|
@ -7,10 +7,18 @@ import {
|
|||
import type { EmojiType } from './types';
|
||||
|
||||
export type KeyboardProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onEmojiSelected: (emoji: EmojiType) => void;
|
||||
emojiSize?: number;
|
||||
containerStyles?: ViewStyle;
|
||||
isOpen?: boolean;
|
||||
knobStyles?: ViewStyle;
|
||||
headerStyles?: TextStyle;
|
||||
expandable?: boolean;
|
||||
hideHeader?: boolean;
|
||||
defaultHeight?: number;
|
||||
expandedHeight?: number;
|
||||
backdropColor?: string;
|
||||
};
|
||||
export type ContextValues = {
|
||||
activeCategoryIndex: number;
|
||||
|
|
|
@ -11,10 +11,18 @@ type ProviderProps = KeyboardProps & {
|
|||
};
|
||||
|
||||
export const defaultKeyboardContext: Required<KeyboardProps> = {
|
||||
open: false,
|
||||
onClose: () => {},
|
||||
onEmojiSelected: (_emoji: EmojiType) => {},
|
||||
emojiSize: 24,
|
||||
emojiSize: 28,
|
||||
containerStyles: {},
|
||||
isOpen: false,
|
||||
knobStyles: {},
|
||||
headerStyles: {},
|
||||
expandable: true,
|
||||
hideHeader: false,
|
||||
defaultHeight: 0.4,
|
||||
expandedHeight: 0.8,
|
||||
backdropColor: '#00000055',
|
||||
};
|
||||
|
||||
export const defaultKeyboardValues: ContextValues = {
|
||||
|
@ -27,8 +35,8 @@ export const KeyboardProvider: React.FC<ProviderProps> = React.memo((props) => {
|
|||
const [activeCategoryIndex, setActiveCategoryIndex] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.isOpen) setActiveCategoryIndex(0);
|
||||
}, [props.isOpen]);
|
||||
if (props.open) setActiveCategoryIndex(0);
|
||||
}, [props.open]);
|
||||
|
||||
const value: Required<KeyboardProps> & ContextValues = {
|
||||
...defaultKeyboardContext,
|
||||
|
|
|
@ -42,7 +42,9 @@ export const EmojiCategory = ({ item }: { item: CategoryTypes }) => {
|
|||
|
||||
return (
|
||||
<View style={[styles.container, { width: width }]}>
|
||||
<Text style={styles.sectionTitle}>{item}</Text>
|
||||
{!ctx.hideHeader && (
|
||||
<Text style={[styles.sectionTitle, ctx.headerStyles]}>{item}</Text>
|
||||
)}
|
||||
<FlatList
|
||||
data={data}
|
||||
keyExtractor={(emoji) => emoji.name}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
PanGestureHandlerGestureEvent,
|
||||
State,
|
||||
} from 'react-native-gesture-handler';
|
||||
import { KeyboardContext } from '../KeyboardContext';
|
||||
|
||||
type KnobProps = {
|
||||
offsetY: Animated.Value;
|
||||
|
@ -14,6 +15,7 @@ type KnobProps = {
|
|||
|
||||
export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
|
||||
const { height: screenHeight } = useWindowDimensions();
|
||||
const ctx = React.useContext(KeyboardContext);
|
||||
|
||||
const handleGesture = ({
|
||||
nativeEvent: { translationY, state },
|
||||
|
@ -31,16 +33,16 @@ export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
|
|||
if (translationY < -30) {
|
||||
Animated.spring(height, {
|
||||
useNativeDriver: false,
|
||||
toValue: screenHeight * 0.8,
|
||||
toValue: screenHeight * ctx.expandedHeight,
|
||||
}).start();
|
||||
} else if (translationY > 150) {
|
||||
height.setValue(screenHeight * 0.4);
|
||||
height.setValue(screenHeight * ctx.defaultHeight);
|
||||
offsetY.setValue(0);
|
||||
onClose();
|
||||
} else {
|
||||
Animated.spring(height, {
|
||||
useNativeDriver: false,
|
||||
toValue: screenHeight * 0.4,
|
||||
toValue: screenHeight * ctx.defaultHeight,
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +53,7 @@ export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
|
|||
onGestureEvent={handleGesture}
|
||||
onHandlerStateChange={handleGesture}
|
||||
>
|
||||
<Animated.View style={styles.knob} />
|
||||
<Animated.View style={[styles.knob, ctx.knobStyles]} />
|
||||
</PanGestureHandler>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { KeyboardContext } from '../KeyboardContext';
|
||||
|
||||
type ModalWithBackdropProps = {
|
||||
isOpen: boolean;
|
||||
|
@ -22,6 +23,7 @@ export const ModalWithBackdrop = ({
|
|||
}: ModalWithBackdropProps) => {
|
||||
const { height: screenHeight } = useWindowDimensions();
|
||||
const translateY = React.useRef(new Animated.Value(0)).current;
|
||||
const ctx = React.useContext(KeyboardContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
Animated.timing(translateY, {
|
||||
|
@ -38,7 +40,12 @@ export const ModalWithBackdrop = ({
|
|||
activeOpacity={1}
|
||||
onPress={backdropPress}
|
||||
>
|
||||
<View style={[styles.modalContainer, styles.backdrop]}>
|
||||
<View
|
||||
style={[
|
||||
styles.modalContainer,
|
||||
{ backgroundColor: ctx.backdropColor },
|
||||
]}
|
||||
>
|
||||
<SafeAreaView style={styles.modalContainer}>
|
||||
<TouchableOpacity activeOpacity={1}>
|
||||
<Animated.View
|
||||
|
@ -66,5 +73,4 @@ const styles = StyleSheet.create({
|
|||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowRadius: 5,
|
||||
},
|
||||
backdrop: { backgroundColor: '#00000055' },
|
||||
});
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export type EmojiType = {
|
||||
emoji: string;
|
||||
emoji_version: string;
|
||||
name: string;
|
||||
skin_tone_support: boolean;
|
||||
slug: string;
|
||||
skin_tone_support: boolean;
|
||||
unicode_version: string;
|
||||
emoji_version: string;
|
||||
};
|
||||
|
||||
export type CategoryTypes =
|
||||
|
|
Loading…
Reference in New Issue