Feature / categories position (#12)
* feat: create category position prop * docs: update props * fix: fix spacing * docs: add category position to docs
This commit is contained in:
parent
f23ed09c25
commit
c0cbef73c2
|
@ -60,6 +60,7 @@ export default function App() {
|
|||
| onCategoryChangeFailed | function | warn(info) | no | Callback on category change failed (info: {index, highestMeasuredFrameIndex, averageItemLength}) |
|
||||
| translation | CategoryTranslation | en | no | Translation object *see translation section* |
|
||||
| disabledCategory | CategoryTypes[] | [] | no | Hide categories by passing their slugs |
|
||||
| categoryPosition | CategoryPosition | categoryPosition | no | Specify category container position |
|
||||
|
||||
## 📊 Comparison
|
||||
|
||||
|
@ -118,6 +119,10 @@ You can clone the repo and run `yarn example ios` or `yarn example android` to p
|
|||
![Preview](/example/assets/static-modal-preview.jpg)
|
||||
### [Static](/example/src/Static/Static.tsx)
|
||||
![Preview](/example/assets/static-preview.jpg)
|
||||
### [Categories Top](/example/src/TopCategory/TopCategory.tsx)
|
||||
![Preview](/example/assets/categories-top-preview.jpg)
|
||||
### [Categories Bottom](/example/src/BottomCategory/BottomCategory.tsx)
|
||||
![Preview](/example/assets/categories-bottom-preview.jpg)
|
||||
## 📈 Future plans
|
||||
* Skin tone palette selector.
|
||||
* Search bar.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
|
@ -9,6 +9,8 @@ import Translated from './Translated/Translated';
|
|||
import DisabledCategories from './DisabledCategories/DisabledCategories';
|
||||
import StaticModal from './StaticModal/StaticModal';
|
||||
import Static from './Static/Static';
|
||||
import TopCategory from './TopCategory/TopCategory';
|
||||
import BottomCategory from './BottomCategory/BottomCategory';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
export default () => {
|
||||
|
@ -25,6 +27,8 @@ export default () => {
|
|||
/>
|
||||
<Stack.Screen name="StaticModal" component={StaticModal} />
|
||||
<Stack.Screen name="Static" component={Static} />
|
||||
<Stack.Screen name="TopCategory" component={TopCategory} />
|
||||
<Stack.Screen name="BottomCategory" component={BottomCategory} />
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import * as React from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import EmojiPicker from 'rn-emoji-keyboard';
|
||||
import type { EmojiType } from 'src/types';
|
||||
|
||||
const BottomCategory = () => {
|
||||
const [result, setResult] = React.useState<string>();
|
||||
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false);
|
||||
|
||||
const handlePick = (emoji: EmojiType) => {
|
||||
setResult(emoji.emoji);
|
||||
setIsModalOpen((prev) => !prev);
|
||||
};
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Text style={styles.text}>Result: {result}</Text>
|
||||
<TouchableOpacity onPress={() => setIsModalOpen(true)}>
|
||||
<Text style={styles.text}>Open</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<EmojiPicker
|
||||
onEmojiSelected={handlePick}
|
||||
open={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
categoryPosition="bottom"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center',
|
||||
margin: 64,
|
||||
fontSize: 18,
|
||||
},
|
||||
});
|
||||
|
||||
export default BottomCategory;
|
|
@ -11,6 +11,8 @@ type RootStackParamList = {
|
|||
DisabledCategories: undefined;
|
||||
StaticModal: undefined;
|
||||
Static: undefined;
|
||||
TopCategory: undefined;
|
||||
BottomCategory: undefined;
|
||||
};
|
||||
|
||||
type Props = StackScreenProps<RootStackParamList, 'Examples'>;
|
||||
|
@ -30,10 +32,21 @@ const Examples = ({ navigation }: Props) => {
|
|||
onPress={() => navigation.navigate('DisabledCategories')}
|
||||
/>
|
||||
<Button
|
||||
title="StaticModal"
|
||||
title="Static Modal (wihtout knob)"
|
||||
onPress={() => navigation.navigate('StaticModal')}
|
||||
/>
|
||||
<Button title="Static" onPress={() => navigation.navigate('Static')} />
|
||||
<Button
|
||||
title="Static Component"
|
||||
onPress={() => navigation.navigate('Static')}
|
||||
/>
|
||||
<Button
|
||||
title="Category Top"
|
||||
onPress={() => navigation.navigate('TopCategory')}
|
||||
/>
|
||||
<Button
|
||||
title="Category Bottom"
|
||||
onPress={() => navigation.navigate('BottomCategory')}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import * as React from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import EmojiPicker from 'rn-emoji-keyboard';
|
||||
import type { EmojiType } from 'src/types';
|
||||
|
||||
const TopCategory = () => {
|
||||
const [result, setResult] = React.useState<string>();
|
||||
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false);
|
||||
|
||||
const handlePick = (emoji: EmojiType) => {
|
||||
setResult(emoji.emoji);
|
||||
setIsModalOpen((prev) => !prev);
|
||||
};
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Text style={styles.text}>Result: {result}</Text>
|
||||
<TouchableOpacity onPress={() => setIsModalOpen(true)}>
|
||||
<Text style={styles.text}>Open</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<EmojiPicker
|
||||
onEmojiSelected={handlePick}
|
||||
open={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
categoryPosition="top"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center',
|
||||
margin: 64,
|
||||
fontSize: 18,
|
||||
},
|
||||
});
|
||||
|
||||
export default TopCategory;
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { View, Animated, StyleSheet, FlatList } from 'react-native';
|
||||
import { View, Animated, StyleSheet, FlatList, ViewStyle } from 'react-native';
|
||||
import { defaultKeyboardContext } from '../contexts/KeyboardProvider';
|
||||
import { KeyboardContext } from '../contexts/KeyboardContext';
|
||||
import {
|
||||
CATEGORIES,
|
||||
|
@ -21,6 +22,7 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
|
|||
onCategoryChangeFailed,
|
||||
disabledCategory,
|
||||
activeCategoryContainerColor,
|
||||
categoryPosition,
|
||||
} = React.useContext(KeyboardContext);
|
||||
|
||||
const handleScrollToCategory = React.useCallback(
|
||||
|
@ -62,11 +64,35 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
|
|||
[activeCategoryContainerColor, scrollNav]
|
||||
);
|
||||
|
||||
const getStylesBasedOnPosition = () => {
|
||||
const style: ViewStyle[] = [styles.navigation];
|
||||
switch (categoryPosition) {
|
||||
case 'floating':
|
||||
style.push(styles.navigationFloating);
|
||||
break;
|
||||
case 'top':
|
||||
style.push(styles.navigationTop);
|
||||
break;
|
||||
case 'bottom':
|
||||
style.push(styles.navigationBottom);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (
|
||||
categoryContainerColor !==
|
||||
defaultKeyboardContext.categoryContainerColor ||
|
||||
categoryPosition === 'floating'
|
||||
)
|
||||
style.push({
|
||||
backgroundColor: categoryContainerColor,
|
||||
});
|
||||
return style;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.bottomBar}>
|
||||
<View
|
||||
style={[styles.navigation, { backgroundColor: categoryContainerColor }]}
|
||||
>
|
||||
<View style={[categoryPosition === 'floating' && styles.floating]}>
|
||||
<View style={getStylesBasedOnPosition()}>
|
||||
<FlatList
|
||||
data={CATEGORIES_NAVIGATION.filter(
|
||||
({ category }) => !disabledCategory.includes(category)
|
||||
|
@ -87,7 +113,7 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
|
|||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
bottomBar: {
|
||||
floating: {
|
||||
position: 'absolute',
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
|
@ -96,8 +122,25 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
navigation: {
|
||||
padding: 3,
|
||||
alignItems: 'center',
|
||||
borderColor: '#00000011',
|
||||
},
|
||||
navigationFloating: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
navigationBottom: {
|
||||
paddingVertical: 6,
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
navigationTop: {
|
||||
paddingTop: 12,
|
||||
paddingBottom: 6,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
separator: {
|
||||
width: 1,
|
||||
height: 28,
|
||||
|
|
|
@ -26,6 +26,7 @@ export const EmojiCategory = ({
|
|||
hideHeader,
|
||||
headerStyles,
|
||||
translation,
|
||||
categoryPosition,
|
||||
} = React.useContext(KeyboardContext);
|
||||
|
||||
const { setKeyboardState } = useKeyboardStore();
|
||||
|
@ -89,7 +90,15 @@ export const EmojiCategory = ({
|
|||
renderItem={renderItem}
|
||||
removeClippedSubviews={true}
|
||||
getItemLayout={getItemLayout}
|
||||
ListFooterComponent={() => <View style={styles.footer} />}
|
||||
ListFooterComponent={() => (
|
||||
<View
|
||||
style={
|
||||
categoryPosition === 'floating'
|
||||
? styles.footerFloating
|
||||
: styles.footer
|
||||
}
|
||||
/>
|
||||
)}
|
||||
windowSize={20}
|
||||
/>
|
||||
</View>
|
||||
|
@ -104,8 +113,10 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
sectionTitle: {
|
||||
opacity: 0.6,
|
||||
marginTop: 16,
|
||||
marginBottom: 6,
|
||||
marginLeft: 12,
|
||||
},
|
||||
footer: { height: 70 },
|
||||
footer: { height: 8 },
|
||||
footerFloating: { height: 70 },
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ export const EmojiStaticKeyboard = () => {
|
|||
containerStyles,
|
||||
onCategoryChangeFailed,
|
||||
disabledCategory,
|
||||
categoryPosition,
|
||||
} = React.useContext(KeyboardContext);
|
||||
|
||||
const flatListRef = React.useRef<FlatList>(null);
|
||||
|
@ -47,7 +48,14 @@ export const EmojiStaticKeyboard = () => {
|
|||
}, [activeCategoryIndex, scrollNav]);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, styles.containerShadow, containerStyles]}>
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
styles.containerShadow,
|
||||
categoryPosition === 'top' && styles.containerReverse,
|
||||
containerStyles,
|
||||
]}
|
||||
>
|
||||
<Animated.FlatList
|
||||
data={emojisByGroup.filter((category) => {
|
||||
const title = category.title as CategoryTypes;
|
||||
|
@ -76,10 +84,10 @@ export const EmojiStaticKeyboard = () => {
|
|||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 16,
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
containerReverse: { flexDirection: 'column-reverse' },
|
||||
containerShadow: {
|
||||
shadowColor: 'black',
|
||||
shadowOpacity: 0.15,
|
||||
|
|
|
@ -4,7 +4,12 @@ import {
|
|||
defaultKeyboardContext,
|
||||
defaultKeyboardValues,
|
||||
} from './KeyboardProvider';
|
||||
import type { CategoryTranslation, EmojiType, CategoryTypes } from '../types';
|
||||
import type {
|
||||
CategoryTranslation,
|
||||
EmojiType,
|
||||
CategoryTypes,
|
||||
CategoryPosition,
|
||||
} from '../types';
|
||||
|
||||
export type OnEmojiSelected = (emoji: EmojiType) => void;
|
||||
|
||||
|
@ -32,6 +37,7 @@ export type KeyboardProps = {
|
|||
}) => void;
|
||||
translation?: CategoryTranslation;
|
||||
disabledCategory?: CategoryTypes[];
|
||||
categoryPosition?: CategoryPosition;
|
||||
};
|
||||
export type ContextValues = {
|
||||
activeCategoryIndex: number;
|
||||
|
|
|
@ -36,6 +36,7 @@ export const defaultKeyboardContext: Required<KeyboardProps> = {
|
|||
},
|
||||
translation: en,
|
||||
disabledCategory: [],
|
||||
categoryPosition: 'floating',
|
||||
};
|
||||
|
||||
export const defaultKeyboardValues: ContextValues = {
|
||||
|
|
|
@ -21,6 +21,8 @@ export type CategoryTypes =
|
|||
| 'symbols'
|
||||
| 'flags';
|
||||
|
||||
export type CategoryPosition = 'floating' | 'top' | 'bottom';
|
||||
|
||||
export const CATEGORIES: CategoryTypes[] = [
|
||||
'smileys_emotion',
|
||||
'people_body',
|
||||
|
|
Loading…
Reference in New Issue