chore: change prettier config (#19)

This commit is contained in:
Jakub Grzywacz 2021-08-11 10:13:38 +02:00 committed by GitHub
parent c4a8ed36fc
commit f68fcf720f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 688 additions and 862 deletions

View File

@ -1,3 +1,3 @@
module.exports = { module.exports = {
presets: ['module:metro-react-native-babel-preset'], presets: ['module:metro-react-native-babel-preset'],
}; }

View File

@ -1,5 +1,5 @@
const path = require('path'); const path = require('path')
const pak = require('../package.json'); const pak = require('../package.json')
module.exports = { module.exports = {
presets: ['module:metro-react-native-babel-preset'], presets: ['module:metro-react-native-babel-preset'],
@ -14,4 +14,4 @@ module.exports = {
}, },
], ],
], ],
}; }

View File

@ -1,5 +1,5 @@
import { AppRegistry } from 'react-native'; import { AppRegistry } from 'react-native'
import App from './src/App'; import App from './src/App'
import { name as appName } from './app.json'; import { name as appName } from './app.json'
AppRegistry.registerComponent(appName, () => App); AppRegistry.registerComponent(appName, () => App)

View File

@ -494,8 +494,8 @@ SPEC CHECKSUMS:
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
FlipperKit: 57764956d2f0f972c1af5075a9c8f05ca5b12349 FlipperKit: 57764956d2f0f972c1af5075a9c8f05ca5b12349
Folly: b73c3869541e86821df3c387eb0af5f65addfab4 Folly: aeb27d02cdff07ca01f8a8a5a6dd5bcaf6be6f70
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 glog: 2ad46e202fbaa5641fceb4b2af37dcd88fd8762d
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e

View File

@ -1,13 +1,13 @@
const path = require('path'); const path = require('path')
const blacklist = require('metro-config/src/defaults/blacklist'); const blacklist = require('metro-config/src/defaults/blacklist')
const escape = require('escape-string-regexp'); const escape = require('escape-string-regexp')
const pak = require('../package.json'); const pak = require('../package.json')
const root = path.resolve(__dirname, '..'); const root = path.resolve(__dirname, '..')
const modules = Object.keys({ const modules = Object.keys({
...pak.peerDependencies, ...pak.peerDependencies,
}); })
module.exports = { module.exports = {
projectRoot: __dirname, projectRoot: __dirname,
@ -17,15 +17,12 @@ module.exports = {
// So we blacklist them at the root, and alias them to the versions in example's node_modules // So we blacklist them at the root, and alias them to the versions in example's node_modules
resolver: { resolver: {
blacklistRE: blacklist( blacklistRE: blacklist(
modules.map( modules.map((m) => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`))
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
), ),
extraNodeModules: modules.reduce((acc, name) => { extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name); acc[name] = path.join(__dirname, 'node_modules', name)
return acc; return acc
}, {}), }, {}),
}, },
@ -37,4 +34,4 @@ module.exports = {
}, },
}), }),
}, },
}; }

View File

@ -1,20 +1,20 @@
import 'react-native-gesture-handler'; import 'react-native-gesture-handler'
import * as React from 'react'; import * as React from 'react'
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack'
import Examples from './Examples/Examples'; import Examples from './Examples/Examples'
import Basic from './Basic/Basic'; import Basic from './Basic/Basic'
import Dark from './Dark/Dark'; import Dark from './Dark/Dark'
import Translated from './Translated/Translated'; import Translated from './Translated/Translated'
import DisabledCategories from './DisabledCategories/DisabledCategories'; import DisabledCategories from './DisabledCategories/DisabledCategories'
import StaticModal from './StaticModal/StaticModal'; import StaticModal from './StaticModal/StaticModal'
import Static from './Static/Static'; import Static from './Static/Static'
import EnableRecently from './EnableRecently/EnableRecently'; import EnableRecently from './EnableRecently/EnableRecently'
import TopCategory from './TopCategory/TopCategory'; import TopCategory from './TopCategory/TopCategory'
import BottomCategory from './BottomCategory/BottomCategory'; import BottomCategory from './BottomCategory/BottomCategory'
import SearchBar from './SearchBar/SearchBar'; import SearchBar from './SearchBar/SearchBar'
const Stack = createStackNavigator(); const Stack = createStackNavigator()
export default () => { export default () => {
return ( return (
<NavigationContainer> <NavigationContainer>
@ -27,13 +27,10 @@ export default () => {
<Stack.Screen name="TopCategory" component={TopCategory} /> <Stack.Screen name="TopCategory" component={TopCategory} />
<Stack.Screen name="BottomCategory" component={BottomCategory} /> <Stack.Screen name="BottomCategory" component={BottomCategory} />
<Stack.Screen name="Translated" component={Translated} /> <Stack.Screen name="Translated" component={Translated} />
<Stack.Screen <Stack.Screen name="DisabledCategories" component={DisabledCategories} />
name="DisabledCategories"
component={DisabledCategories}
/>
<Stack.Screen name="StaticModal" component={StaticModal} /> <Stack.Screen name="StaticModal" component={StaticModal} />
<Stack.Screen name="Static" component={Static} /> <Stack.Screen name="Static" component={Static} />
</Stack.Navigator> </Stack.Navigator>
</NavigationContainer> </NavigationContainer>
); )
}; }

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const Basic = () => { const Basic = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -26,8 +26,8 @@ const Basic = () => {
onClose={() => setIsModalOpen(false)} onClose={() => setIsModalOpen(false)}
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -38,6 +38,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default Basic; export default Basic

View File

@ -1,17 +1,17 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const BottomCategory = () => { const BottomCategory = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -26,8 +26,8 @@ const BottomCategory = () => {
categoryPosition="bottom" categoryPosition="bottom"
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -38,6 +38,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default BottomCategory; export default BottomCategory

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const Dark = () => { const Dark = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -34,8 +34,8 @@ const Dark = () => {
headerStyles={styles.headerStyles} headerStyles={styles.headerStyles}
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -58,6 +58,6 @@ const styles = StyleSheet.create({
color: '#fff', color: '#fff',
fontSize: 16, fontSize: 16,
}, },
}); })
export default Dark; export default Dark

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const DisabledCategories = () => { const DisabledCategories = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -27,8 +27,8 @@ const DisabledCategories = () => {
disabledCategory={['activities', 'flags', 'objects', 'symbols']} disabledCategory={['activities', 'flags', 'objects', 'symbols']}
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -39,6 +39,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default DisabledCategories; export default DisabledCategories

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const EnableRecently = () => { const EnableRecently = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -27,8 +27,8 @@ const EnableRecently = () => {
enableRecentlyUsed enableRecentlyUsed
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -39,6 +39,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default EnableRecently; export default EnableRecently

View File

@ -1,23 +1,23 @@
import * as React from 'react'; import * as React from 'react'
import { View, Button } from 'react-native'; import { View, Button } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import type { StackScreenProps } from '@react-navigation/stack'; import type { StackScreenProps } from '@react-navigation/stack'
type RootStackParamList = { type RootStackParamList = {
Examples: undefined; Examples: undefined
Basic: undefined; Basic: undefined
Dark: undefined; Dark: undefined
Translated: undefined; Translated: undefined
DisabledCategories: undefined; DisabledCategories: undefined
StaticModal: undefined; StaticModal: undefined
Static: undefined; Static: undefined
EnableRecently: undefined; EnableRecently: undefined
TopCategory: undefined; TopCategory: undefined
BottomCategory: undefined; BottomCategory: undefined
SearchBar: undefined; SearchBar: undefined
}; }
type Props = StackScreenProps<RootStackParamList, 'Examples'>; type Props = StackScreenProps<RootStackParamList, 'Examples'>
const Examples = ({ navigation }: Props) => { const Examples = ({ navigation }: Props) => {
return ( return (
@ -25,10 +25,7 @@ const Examples = ({ navigation }: Props) => {
<View> <View>
<Button title="Basic" onPress={() => navigation.navigate('Basic')} /> <Button title="Basic" onPress={() => navigation.navigate('Basic')} />
<Button title="Dark" onPress={() => navigation.navigate('Dark')} /> <Button title="Dark" onPress={() => navigation.navigate('Dark')} />
<Button <Button title="Translated" onPress={() => navigation.navigate('Translated')} />
title="Translated"
onPress={() => navigation.navigate('Translated')}
/>
<Button <Button
title="Disabled Categories" title="Disabled Categories"
onPress={() => navigation.navigate('DisabledCategories')} onPress={() => navigation.navigate('DisabledCategories')}
@ -37,28 +34,16 @@ const Examples = ({ navigation }: Props) => {
title="Static Modal (wihtout knob)" title="Static Modal (wihtout knob)"
onPress={() => navigation.navigate('StaticModal')} onPress={() => navigation.navigate('StaticModal')}
/> />
<Button <Button title="Static Component" onPress={() => navigation.navigate('Static')} />
title="Static Component"
onPress={() => navigation.navigate('Static')}
/>
<Button <Button
title="Enable recently used" title="Enable recently used"
onPress={() => navigation.navigate('EnableRecently')} onPress={() => navigation.navigate('EnableRecently')}
/> />
<Button <Button title="Category Top" onPress={() => navigation.navigate('TopCategory')} />
title="Category Top" <Button title="Category Bottom" onPress={() => navigation.navigate('BottomCategory')} />
onPress={() => navigation.navigate('TopCategory')} <Button title="Search Bar" onPress={() => navigation.navigate('SearchBar')} />
/>
<Button
title="Category Bottom"
onPress={() => navigation.navigate('BottomCategory')}
/>
<Button
title="Search Bar"
onPress={() => navigation.navigate('SearchBar')}
/>
</View> </View>
</SafeAreaView> </SafeAreaView>
); )
}; }
export default Examples; export default Examples

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const SearchBar = () => { const SearchBar = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -27,8 +27,8 @@ const SearchBar = () => {
enableSearchBar enableSearchBar
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -39,6 +39,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default SearchBar; export default SearchBar

View File

@ -1,29 +1,26 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, Text, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import { EmojiKeyboard } from 'rn-emoji-keyboard'; import { EmojiKeyboard } from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const Static = () => { const Static = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
setResult(emoji.emoji); setResult(emoji.emoji)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
</View> </View>
<View style={styles.container}> <View style={styles.container}>
<EmojiKeyboard <EmojiKeyboard onEmojiSelected={handlePick} containerStyles={styles.keyboardContainer} />
onEmojiSelected={handlePick}
containerStyles={styles.keyboardContainer}
/>
</View> </View>
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -37,6 +34,6 @@ const styles = StyleSheet.create({
keyboardContainer: { keyboardContainer: {
borderRadius: 0, borderRadius: 0,
}, },
}); })
export default Static; export default Static

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const StaticModal = () => { const StaticModal = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -28,8 +28,8 @@ const StaticModal = () => {
defaultHeight="65%" defaultHeight="65%"
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -40,6 +40,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default StaticModal; export default StaticModal

View File

@ -1,17 +1,17 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker from 'rn-emoji-keyboard'; import EmojiPicker from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const TopCategory = () => { const TopCategory = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Result: {result}</Text> <Text style={styles.text}>Result: {result}</Text>
@ -26,8 +26,8 @@ const TopCategory = () => {
categoryPosition="top" categoryPosition="top"
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -38,6 +38,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default TopCategory; export default TopCategory

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, TouchableOpacity } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context'
import EmojiPicker, { pl } from 'rn-emoji-keyboard'; import EmojiPicker, { pl } from 'rn-emoji-keyboard'
import type { EmojiType } from 'src/types'; import type { EmojiType } from 'src/types'
const Translated = () => { const Translated = () => {
const [result, setResult] = React.useState<string>(); const [result, setResult] = React.useState<string>()
const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false); const [isModalOpen, setIsModalOpen] = React.useState<boolean>(false)
const handlePick = (emoji: EmojiType) => { const handlePick = (emoji: EmojiType) => {
console.log(emoji); console.log(emoji)
setResult(emoji.emoji); setResult(emoji.emoji)
setIsModalOpen((prev) => !prev); setIsModalOpen((prev) => !prev)
}; }
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<Text style={styles.text}>Emotka: {result}</Text> <Text style={styles.text}>Emotka: {result}</Text>
@ -27,8 +27,8 @@ const Translated = () => {
translation={pl} translation={pl}
/> />
</SafeAreaView> </SafeAreaView>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -39,6 +39,6 @@ const styles = StyleSheet.create({
margin: 64, margin: 64,
fontSize: 18, fontSize: 18,
}, },
}); })
export default Translated; export default Translated

View File

@ -52,6 +52,7 @@
"@commitlint/config-conventional": "^11.0.0", "@commitlint/config-conventional": "^11.0.0",
"@react-native-community/eslint-config": "^2.0.0", "@react-native-community/eslint-config": "^2.0.0",
"@release-it/conventional-changelog": "^2.0.0", "@release-it/conventional-changelog": "^2.0.0",
"@twgdev/prettier-config": "^1.0.2",
"@types/jest": "^26.0.0", "@types/jest": "^26.0.0",
"@types/react": "^16.9.19", "@types/react": "^16.9.19",
"@types/react-native": "0.62.13", "@types/react-native": "0.62.13",
@ -116,31 +117,13 @@
"extends": [ "extends": [
"@react-native-community", "@react-native-community",
"prettier" "prettier"
],
"rules": {
"prettier/prettier": [
"error",
{
"quoteProps": "consistent",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}
] ]
}
}, },
"eslintIgnore": [ "eslintIgnore": [
"node_modules/", "node_modules/",
"lib/" "lib/"
], ],
"prettier": { "prettier": "@twgdev/prettier-config",
"quoteProps": "consistent",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
},
"react-native-builder-bob": { "react-native-builder-bob": {
"source": "src", "source": "src",
"output": "lib", "output": "lib",

18
scripts/bootstrap.js vendored
View File

@ -1,24 +1,24 @@
const path = require('path'); const path = require('path')
const child_process = require('child_process'); const child_process = require('child_process')
const root = path.resolve(__dirname, '..'); const root = path.resolve(__dirname, '..')
const args = process.argv.slice(2); const args = process.argv.slice(2)
const options = { const options = {
cwd: process.cwd(), cwd: process.cwd(),
env: process.env, env: process.env,
stdio: 'inherit', stdio: 'inherit',
encoding: 'utf-8', encoding: 'utf-8',
}; }
let result; let result
if (process.cwd() !== root || args.length) { if (process.cwd() !== root || args.length) {
// We're not in the root of the project, or additional arguments were passed // We're not in the root of the project, or additional arguments were passed
// In this case, forward the command to `yarn` // In this case, forward the command to `yarn`
result = child_process.spawnSync('yarn', args, options); result = child_process.spawnSync('yarn', args, options)
} else { } else {
// If `yarn` is run without arguments, perform bootstrap // If `yarn` is run without arguments, perform bootstrap
result = child_process.spawnSync('yarn', ['bootstrap'], options); result = child_process.spawnSync('yarn', ['bootstrap'], options)
} }
process.exitCode = result.status; process.exitCode = result.status

View File

@ -1,24 +1,20 @@
const json = require('unicode-emoji-json/data-by-group.json'); const json = require('unicode-emoji-json/data-by-group.json')
const fs = require('fs'); const fs = require('fs')
const newArray = []; const newArray = []
for (const [key, value] of Object.entries(json)) { for (const [key, value] of Object.entries(json)) {
const newData = value.map((emoji) => ({ const newData = value.map((emoji) => ({
emoji: emoji.emoji, emoji: emoji.emoji,
name: emoji.name, name: emoji.name,
v: emoji.unicode_version, v: emoji.unicode_version,
})); }))
newArray.push({ newArray.push({
title: key.replace(' & ', '_').replace(' ', '_').toLocaleLowerCase(), title: key.replace(' & ', '_').replace(' ', '_').toLocaleLowerCase(),
data: newData, data: newData,
}); })
} }
fs.writeFile( fs.writeFile('./src/assets/emojis.json', JSON.stringify(newArray), function (err) {
'./src/assets/emojis.json', if (err) return console.log(err)
JSON.stringify(newArray), console.log('emojis.json successfully saved to assets folder')
function (err) { })
if (err) return console.log(err);
console.log('emojis.json successfully saved to assets folder');
}
);

View File

@ -1,19 +1,16 @@
import * as React from 'react'; import * as React from 'react'
import { EmojiStaticKeyboard } from './components/EmojiStaticKeyboard'; import { EmojiStaticKeyboard } from './components/EmojiStaticKeyboard'
import { KeyboardProvider } from './contexts/KeyboardProvider'; import { KeyboardProvider } from './contexts/KeyboardProvider'
import type { import type { KeyboardProps, OnEmojiSelected } from './contexts/KeyboardContext'
KeyboardProps,
OnEmojiSelected,
} from './contexts/KeyboardContext';
type EmojiKeyboardProps = { type EmojiKeyboardProps = {
onEmojiSelected: OnEmojiSelected; onEmojiSelected: OnEmojiSelected
} & Partial<KeyboardProps>; } & Partial<KeyboardProps>
export const EmojiKeyboard = (props: EmojiKeyboardProps) => { export const EmojiKeyboard = (props: EmojiKeyboardProps) => {
return ( return (
<KeyboardProvider {...props}> <KeyboardProvider {...props}>
<EmojiStaticKeyboard /> <EmojiStaticKeyboard />
</KeyboardProvider> </KeyboardProvider>
); )
}; }

View File

@ -1,15 +1,12 @@
import * as React from 'react'; import * as React from 'react'
import { Animated, useWindowDimensions } from 'react-native'; import { Animated, useWindowDimensions } from 'react-native'
import { EmojiStaticKeyboard } from './components/EmojiStaticKeyboard'; import { EmojiStaticKeyboard } from './components/EmojiStaticKeyboard'
import { Knob } from './components/Knob'; import { Knob } from './components/Knob'
import { import { defaultKeyboardContext, KeyboardProvider } from './contexts/KeyboardProvider'
defaultKeyboardContext, import type { KeyboardProps } from './contexts/KeyboardContext'
KeyboardProvider, import type { EmojiType } from './types'
} from './contexts/KeyboardProvider'; import { ModalWithBackdrop } from './components/ModalWithBackdrop'
import type { KeyboardProps } from './contexts/KeyboardContext'; import { getHeight } from './utils'
import type { EmojiType } from './types';
import { ModalWithBackdrop } from './components/ModalWithBackdrop';
import { getHeight } from './utils';
export const EmojiPicker = ({ export const EmojiPicker = ({
onEmojiSelected, onEmojiSelected,
@ -19,55 +16,49 @@ export const EmojiPicker = ({
defaultHeight = defaultKeyboardContext.defaultHeight, defaultHeight = defaultKeyboardContext.defaultHeight,
...props ...props
}: KeyboardProps) => { }: KeyboardProps) => {
const { height: screenHeight } = useWindowDimensions(); const { height: screenHeight } = useWindowDimensions()
const offsetY = React.useRef(new Animated.Value(0)).current; const offsetY = React.useRef(new Animated.Value(0)).current
const height = React.useRef( const height = React.useRef(new Animated.Value(getHeight(defaultHeight, screenHeight))).current
new Animated.Value(getHeight(defaultHeight, screenHeight)) const translateY = React.useRef(new Animated.Value(0)).current
).current;
const translateY = React.useRef(new Animated.Value(0)).current;
React.useEffect(() => { React.useEffect(() => {
Animated.timing(translateY, { Animated.timing(translateY, {
toValue: open ? 0 : screenHeight, toValue: open ? 0 : screenHeight,
useNativeDriver: true, useNativeDriver: true,
duration: 500, duration: 500,
}).start(); }).start()
}, [open, screenHeight, translateY]); }, [open, screenHeight, translateY])
const close = () => { const close = () => {
height.setValue(getHeight(defaultHeight, screenHeight)); height.setValue(getHeight(defaultHeight, screenHeight))
offsetY.setValue(0); offsetY.setValue(0)
onClose(); onClose()
}; }
return ( return (
<KeyboardProvider <KeyboardProvider
onEmojiSelected={(emoji: EmojiType) => { onEmojiSelected={(emoji: EmojiType) => {
onEmojiSelected(emoji); onEmojiSelected(emoji)
close(); close()
}} }}
open={open} open={open}
onClose={close} onClose={close}
expandable={expandable} expandable={expandable}
defaultHeight={defaultHeight} defaultHeight={defaultHeight}
{...props} {...props}>
>
<ModalWithBackdrop isOpen={open} backdropPress={close}> <ModalWithBackdrop isOpen={open} backdropPress={close}>
<> <>
{expandable && ( {expandable && <Knob height={height} offsetY={offsetY} onClose={onClose} />}
<Knob height={height} offsetY={offsetY} onClose={onClose} />
)}
<Animated.View <Animated.View
style={[ style={[
{ {
height: Animated.subtract(height, offsetY), height: Animated.subtract(height, offsetY),
}, },
]} ]}>
>
<EmojiStaticKeyboard /> <EmojiStaticKeyboard />
</Animated.View> </Animated.View>
</> </>
</ModalWithBackdrop> </ModalWithBackdrop>
</KeyboardProvider> </KeyboardProvider>
); )
}; }

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="23" height="23" viewBox="0 0 24 24" fill="none"> <Svg width="23" height="23" viewBox="0 0 24 24" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -10,4 +10,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="23" height="23" viewBox="0 0 24 24" fill="none"> <Svg width="23" height="23" viewBox="0 0 24 24" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react'
import Svg, { FillProps, Path } from 'react-native-svg'; import Svg, { FillProps, Path } from 'react-native-svg'
export default ({ fill }: FillProps) => ( export default ({ fill }: FillProps) => (
<Svg width="22" height="22" viewBox="0 0 22 22" fill="none"> <Svg width="22" height="22" viewBox="0 0 22 22" fill="none">
@ -8,4 +8,4 @@ export default ({ fill }: FillProps) => (
fill={fill} fill={fill}
/> />
</Svg> </Svg>
); )

View File

@ -1,20 +1,16 @@
import * as React from 'react'; import * as React from 'react'
import { Animated, FlatList, StyleSheet, View, ViewStyle } from 'react-native'; import { Animated, FlatList, StyleSheet, View, ViewStyle } from 'react-native'
import { useKeyboardStore } from '../store/useKeyboardStore'; import { useKeyboardStore } from '../store/useKeyboardStore'
import { defaultKeyboardContext } from '../contexts/KeyboardProvider'; import { defaultKeyboardContext } from '../contexts/KeyboardProvider'
import { KeyboardContext } from '../contexts/KeyboardContext'; import { KeyboardContext } from '../contexts/KeyboardContext'
import { import { CATEGORIES_NAVIGATION, CategoryNavigationItem, CategoryTypes } from '../types'
CATEGORIES_NAVIGATION, import { CategoryItem } from './CategoryItem'
CategoryNavigationItem, import { exhaustiveTypeCheck, getCategoryIndex } from '../utils'
CategoryTypes,
} from '../types';
import { CategoryItem } from './CategoryItem';
import { exhaustiveTypeCheck, getCategoryIndex } from '../utils';
type CategoriesProps = { type CategoriesProps = {
flatListRef: React.RefObject<FlatList>; flatListRef: React.RefObject<FlatList>
scrollNav: Animated.Value; scrollNav: Animated.Value
}; }
export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => { export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
const { const {
@ -26,27 +22,21 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
enableRecentlyUsed, enableRecentlyUsed,
categoryPosition, categoryPosition,
searchPhrase, searchPhrase,
} = React.useContext(KeyboardContext); } = React.useContext(KeyboardContext)
const { keyboardState } = useKeyboardStore(); const { keyboardState } = useKeyboardStore()
const handleScrollToCategory = React.useCallback( const handleScrollToCategory = React.useCallback(
(category: CategoryTypes) => { (category: CategoryTypes) => {
flatListRef?.current?.scrollToIndex( flatListRef?.current?.scrollToIndex(getCategoryIndex(disabledCategory, category))
getCategoryIndex(disabledCategory, category)
);
}, },
[disabledCategory, flatListRef] [disabledCategory, flatListRef]
); )
const renderItem = React.useCallback( const renderItem = React.useCallback(
({ item, index }: { item: CategoryNavigationItem; index: number }) => ( ({ item, index }: { item: CategoryNavigationItem; index: number }) => (
<CategoryItem <CategoryItem item={item} index={index} handleScrollToCategory={handleScrollToCategory} />
item={item}
index={index}
handleScrollToCategory={handleScrollToCategory}
/>
), ),
[handleScrollToCategory] [handleScrollToCategory]
); )
const activeIndicator = React.useCallback( const activeIndicator = React.useCallback(
() => ( () => (
@ -63,51 +53,45 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
/> />
), ),
[activeCategoryContainerColor, scrollNav] [activeCategoryContainerColor, scrollNav]
); )
const getStylesBasedOnPosition = () => { const getStylesBasedOnPosition = () => {
const style: ViewStyle[] = [styles.navigation]; const style: ViewStyle[] = [styles.navigation]
switch (categoryPosition) { switch (categoryPosition) {
case 'floating': case 'floating':
style.push(styles.navigationFloating); style.push(styles.navigationFloating)
break; break
case 'top': case 'top':
style.push(styles.navigationTop); style.push(styles.navigationTop)
break; break
case 'bottom': case 'bottom':
style.push(styles.navigationBottom); style.push(styles.navigationBottom)
break; break
default: default:
exhaustiveTypeCheck(categoryPosition); exhaustiveTypeCheck(categoryPosition)
break; break
} }
if ( if (
categoryContainerColor !== categoryContainerColor !== defaultKeyboardContext.categoryContainerColor ||
defaultKeyboardContext.categoryContainerColor ||
categoryPosition === 'floating' categoryPosition === 'floating'
) )
style.push({ style.push({
backgroundColor: categoryContainerColor, backgroundColor: categoryContainerColor,
}); })
return style; return style
}; }
const renderData = React.useMemo(() => { const renderData = React.useMemo(() => {
const isRecentlyUsedHidden = (category: CategoryTypes) => const isRecentlyUsedHidden = (category: CategoryTypes) =>
category === 'recently_used' && category === 'recently_used' &&
(keyboardState.recentlyUsed.length === 0 || !enableRecentlyUsed); (keyboardState.recentlyUsed.length === 0 || !enableRecentlyUsed)
return CATEGORIES_NAVIGATION.filter(({ category }) => { return CATEGORIES_NAVIGATION.filter(({ category }) => {
if (searchPhrase === '' && category === 'search') return false; if (searchPhrase === '' && category === 'search') return false
if (isRecentlyUsedHidden(category)) return false; if (isRecentlyUsedHidden(category)) return false
return !disabledCategory.includes(category); return !disabledCategory.includes(category)
}); })
}, [ }, [disabledCategory, enableRecentlyUsed, keyboardState.recentlyUsed.length, searchPhrase])
disabledCategory,
enableRecentlyUsed,
keyboardState.recentlyUsed.length,
searchPhrase,
]);
return ( return (
<View style={[categoryPosition === 'floating' && styles.floating]}> <View style={[categoryPosition === 'floating' && styles.floating]}>
@ -126,8 +110,8 @@ export const Categories = ({ flatListRef, scrollNav }: CategoriesProps) => {
/> />
</View> </View>
</View> </View>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
floating: { floating: {
@ -175,4 +159,4 @@ const styles = StyleSheet.create({
width: 28, width: 28,
height: 28, height: 28,
}, },
}); })

View File

@ -1,31 +1,23 @@
import * as React from 'react'; import * as React from 'react'
import { View, StyleSheet, TouchableOpacity } from 'react-native'; import { View, StyleSheet, TouchableOpacity } from 'react-native'
import { KeyboardContext } from '../contexts/KeyboardContext'; import { KeyboardContext } from '../contexts/KeyboardContext'
import type { CategoryNavigationItem, CategoryTypes } from '../types'; import type { CategoryNavigationItem, CategoryTypes } from '../types'
import { Icon } from './Icon'; import { Icon } from './Icon'
type CategoryItemProps = { type CategoryItemProps = {
item: CategoryNavigationItem; item: CategoryNavigationItem
index: number; index: number
handleScrollToCategory: (category: CategoryTypes) => void; handleScrollToCategory: (category: CategoryTypes) => void
}; }
export const CategoryItem = ({ export const CategoryItem = ({ item, index, handleScrollToCategory }: CategoryItemProps) => {
item, const { activeCategoryIndex, categoryColor, activeCategoryColor, setActiveCategoryIndex } =
index, React.useContext(KeyboardContext)
handleScrollToCategory,
}: CategoryItemProps) => {
const {
activeCategoryIndex,
categoryColor,
activeCategoryColor,
setActiveCategoryIndex,
} = React.useContext(KeyboardContext);
const handleSelect = () => { const handleSelect = () => {
handleScrollToCategory(item.category); handleScrollToCategory(item.category)
setActiveCategoryIndex(index); setActiveCategoryIndex(index)
}; }
return ( return (
<TouchableOpacity onPress={handleSelect}> <TouchableOpacity onPress={handleSelect}>
@ -38,8 +30,8 @@ export const CategoryItem = ({
/> />
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -51,4 +43,4 @@ const styles = StyleSheet.create({
borderRadius: 6, borderRadius: 6,
}, },
icon: { textAlign: 'center' }, icon: { textAlign: 'center' },
}); })

View File

@ -1,24 +1,20 @@
import * as React from 'react'; import * as React from 'react'
import { StyleSheet, View, Text, FlatList } from 'react-native'; import { StyleSheet, View, Text, FlatList } from 'react-native'
import type { EmojisByCategory, EmojiType, JsonEmoji } from '../types'; import type { EmojisByCategory, EmojiType, JsonEmoji } from '../types'
import { SingleEmoji } from './SingleEmoji'; import { SingleEmoji } from './SingleEmoji'
import { KeyboardContext } from '../contexts/KeyboardContext'; import { KeyboardContext } from '../contexts/KeyboardContext'
import { useKeyboardStore } from '../store/useKeyboardStore'; import { useKeyboardStore } from '../store/useKeyboardStore'
import { parseEmoji } from '../utils'; import { parseEmoji } from '../utils'
const emptyEmoji = { const emptyEmoji = {
emoji: '', emoji: '',
name: 'blank emoji', name: 'blank emoji',
slug: 'blank_emoji', slug: 'blank_emoji',
unicode_version: '0', unicode_version: '0',
}; }
export const EmojiCategory = ({ export const EmojiCategory = ({ item: { title, data } }: { item: EmojisByCategory }) => {
item: { title, data },
}: {
item: EmojisByCategory;
}) => {
const { const {
onEmojiSelected, onEmojiSelected,
emojiSize, emojiSize,
@ -28,55 +24,47 @@ export const EmojiCategory = ({
headerStyles, headerStyles,
translation, translation,
categoryPosition, categoryPosition,
} = React.useContext(KeyboardContext); } = React.useContext(KeyboardContext)
const { setKeyboardState } = useKeyboardStore(); const { setKeyboardState } = useKeyboardStore()
const [empty, setEmpty] = React.useState<EmojiType[]>([]); const [empty, setEmpty] = React.useState<EmojiType[]>([])
React.useEffect(() => { React.useEffect(() => {
if (data.length % numberOfColumns) { if (data.length % numberOfColumns) {
const fillWithEmpty = new Array( const fillWithEmpty = new Array(numberOfColumns - (data.length % numberOfColumns)).fill(
numberOfColumns - (data.length % numberOfColumns) emptyEmoji
).fill(emptyEmoji); )
setEmpty(fillWithEmpty); setEmpty(fillWithEmpty)
} }
}, [numberOfColumns, data]); }, [numberOfColumns, data])
const getItemLayout = (_: EmojiType[] | null | undefined, index: number) => ({ const getItemLayout = (_: EmojiType[] | null | undefined, index: number) => ({
length: emojiSize ? emojiSize : 0, length: emojiSize ? emojiSize : 0,
offset: emojiSize * Math.ceil(index / numberOfColumns), offset: emojiSize * Math.ceil(index / numberOfColumns),
index, index,
}); })
const handleEmojiPress = React.useCallback( const handleEmojiPress = React.useCallback(
(emoji: JsonEmoji) => { (emoji: JsonEmoji) => {
if (emoji.name === 'blank emoji') return; if (emoji.name === 'blank emoji') return
const parsedEmoji = parseEmoji(emoji); const parsedEmoji = parseEmoji(emoji)
onEmojiSelected(parsedEmoji); onEmojiSelected(parsedEmoji)
setKeyboardState({ type: 'RECENT_EMOJI_ADD', payload: emoji }); setKeyboardState({ type: 'RECENT_EMOJI_ADD', payload: emoji })
}, },
[onEmojiSelected, setKeyboardState] [onEmojiSelected, setKeyboardState]
); )
const renderItem = React.useCallback( const renderItem = React.useCallback(
(props) => ( (props) => (
<SingleEmoji <SingleEmoji {...props} onPress={() => handleEmojiPress(props.item)} emojiSize={emojiSize} />
{...props}
onPress={() => handleEmojiPress(props.item)}
emojiSize={emojiSize}
/>
), ),
[emojiSize, handleEmojiPress] [emojiSize, handleEmojiPress]
); )
return ( return (
<View style={[styles.container, { width: width }]}> <View style={[styles.container, { width: width }]}>
{!hideHeader && ( {!hideHeader && <Text style={[styles.sectionTitle, headerStyles]}>{translation[title]}</Text>}
<Text style={[styles.sectionTitle, headerStyles]}>
{translation[title]}
</Text>
)}
<FlatList <FlatList
data={[...data, ...empty]} data={[...data, ...empty]}
keyExtractor={(emoji) => emoji.name} keyExtractor={(emoji) => emoji.name}
@ -85,19 +73,13 @@ export const EmojiCategory = ({
removeClippedSubviews={true} removeClippedSubviews={true}
getItemLayout={getItemLayout} getItemLayout={getItemLayout}
ListFooterComponent={() => ( ListFooterComponent={() => (
<View <View style={categoryPosition === 'floating' ? styles.footerFloating : styles.footer} />
style={
categoryPosition === 'floating'
? styles.footerFloating
: styles.footer
}
/>
)} )}
windowSize={20} windowSize={20}
/> />
</View> </View>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -113,4 +95,4 @@ const styles = StyleSheet.create({
}, },
footer: { height: 8 }, footer: { height: 8 },
footerFloating: { height: 70 }, footerFloating: { height: 70 },
}); })

View File

@ -1,24 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import { import { StyleSheet, View, FlatList, useWindowDimensions, Animated } from 'react-native'
StyleSheet, import type { CategoryTypes, EmojisByCategory } from '../types'
View, import { EmojiCategory } from './EmojiCategory'
FlatList, import { KeyboardContext } from '../contexts/KeyboardContext'
useWindowDimensions, import { Categories } from './Categories'
Animated, import emojisByGroup from '../assets/emojis.json'
} from 'react-native'; import { SearchBar } from './SearchBar'
import type { CategoryTypes, EmojisByCategory } from '../types'; import { useKeyboardStore } from '../store/useKeyboardStore'
import { EmojiCategory } from './EmojiCategory';
import { KeyboardContext } from '../contexts/KeyboardContext';
import { Categories } from './Categories';
import emojisByGroup from '../assets/emojis.json';
import { SearchBar } from './SearchBar';
import { useKeyboardStore } from '../store/useKeyboardStore';
const CATEGORY_ELEMENT_WIDTH = 37; const CATEGORY_ELEMENT_WIDTH = 37
export const EmojiStaticKeyboard = () => { export const EmojiStaticKeyboard = () => {
const { width } = useWindowDimensions(); const { width } = useWindowDimensions()
const { const {
activeCategoryIndex, activeCategoryIndex,
containerStyles, containerStyles,
@ -29,41 +23,35 @@ export const EmojiStaticKeyboard = () => {
searchPhrase, searchPhrase,
setActiveCategoryIndex, setActiveCategoryIndex,
enableRecentlyUsed, enableRecentlyUsed,
} = React.useContext(KeyboardContext); } = React.useContext(KeyboardContext)
const { keyboardState } = useKeyboardStore(); const { keyboardState } = useKeyboardStore()
const flatListRef = React.useRef<FlatList>(null); const flatListRef = React.useRef<FlatList>(null)
const scrollNav = React.useRef(new Animated.Value(0)).current; const scrollNav = React.useRef(new Animated.Value(0)).current
const getItemLayout = ( const getItemLayout = (_: CategoryTypes[] | null | undefined, index: number) => ({
_: CategoryTypes[] | null | undefined,
index: number
) => ({
length: width, length: width,
offset: width * index, offset: width * index,
index, index,
}); })
const renderItem = React.useCallback( const renderItem = React.useCallback((props) => <EmojiCategory {...props} />, [])
(props) => <EmojiCategory {...props} />,
[]
);
React.useEffect(() => { React.useEffect(() => {
Animated.spring(scrollNav, { Animated.spring(scrollNav, {
toValue: activeCategoryIndex * CATEGORY_ELEMENT_WIDTH, toValue: activeCategoryIndex * CATEGORY_ELEMENT_WIDTH,
useNativeDriver: true, useNativeDriver: true,
}).start(); }).start()
}, [activeCategoryIndex, scrollNav]); }, [activeCategoryIndex, scrollNav])
const renderList = React.useMemo(() => { const renderList = React.useMemo(() => {
const data = emojisByGroup.filter((category) => { const data = emojisByGroup.filter((category) => {
const title = category.title as CategoryTypes; const title = category.title as CategoryTypes
return !disabledCategory.includes(title); return !disabledCategory.includes(title)
}); })
if (keyboardState.recentlyUsed.length && enableRecentlyUsed) { if (keyboardState.recentlyUsed.length && enableRecentlyUsed) {
data.push({ data.push({
title: 'recently_used', title: 'recently_used',
data: keyboardState.recentlyUsed, data: keyboardState.recentlyUsed,
}); })
} }
data.push({ data.push({
title: 'search', title: 'search',
@ -71,24 +59,19 @@ export const EmojiStaticKeyboard = () => {
.map((group) => group.data) .map((group) => group.data)
.flat() .flat()
.filter((emoji) => { .filter((emoji) => {
if (searchPhrase && searchPhrase.length < 2) return false; if (searchPhrase && searchPhrase.length < 2) return false
return emoji.name.toLowerCase().includes(searchPhrase.toLowerCase()); return emoji.name.toLowerCase().includes(searchPhrase.toLowerCase())
}), }),
}); })
return data; return data
}, [ }, [disabledCategory, enableRecentlyUsed, keyboardState.recentlyUsed, searchPhrase])
disabledCategory,
enableRecentlyUsed,
keyboardState.recentlyUsed,
searchPhrase,
]);
React.useEffect(() => { React.useEffect(() => {
if (searchPhrase !== '') { if (searchPhrase !== '') {
flatListRef.current?.scrollToEnd(); flatListRef.current?.scrollToEnd()
setActiveCategoryIndex(renderList.length - 1); setActiveCategoryIndex(renderList.length - 1)
} }
}, [renderList, searchPhrase, setActiveCategoryIndex]); }, [renderList, searchPhrase, setActiveCategoryIndex])
return ( return (
<View <View
@ -97,8 +80,7 @@ export const EmojiStaticKeyboard = () => {
styles.containerShadow, styles.containerShadow,
categoryPosition === 'top' && styles.containerReverse, categoryPosition === 'top' && styles.containerReverse,
containerStyles, containerStyles,
]} ]}>
>
{enableSearchBar && <SearchBar flatListRef={flatListRef} />} {enableSearchBar && <SearchBar flatListRef={flatListRef} />}
<Animated.FlatList <Animated.FlatList
extraData={[keyboardState.recentlyUsed.length, searchPhrase]} extraData={[keyboardState.recentlyUsed.length, searchPhrase]}
@ -120,8 +102,8 @@ export const EmojiStaticKeyboard = () => {
/> />
<Categories flatListRef={flatListRef} scrollNav={scrollNav} /> <Categories flatListRef={flatListRef} scrollNav={scrollNav} />
</View> </View>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -137,4 +119,4 @@ const styles = StyleSheet.create({
shadowRadius: 5, shadowRadius: 5,
elevation: 10, elevation: 10,
}, },
}); })

View File

@ -1,18 +1,18 @@
import * as React from 'react'; import * as React from 'react'
import Flag from '../assets/Flag'; import Flag from '../assets/Flag'
import Football from '../assets/Football'; import Football from '../assets/Football'
import Lightbulb from '../assets/Lightbulb'; import Lightbulb from '../assets/Lightbulb'
import Pizza from '../assets/Pizza'; import Pizza from '../assets/Pizza'
import Plane from '../assets/Plane'; import Plane from '../assets/Plane'
import Smile from '../assets/Smile'; import Smile from '../assets/Smile'
import Trees from '../assets/Trees'; import Trees from '../assets/Trees'
import Ban from '../assets/Ban'; import Ban from '../assets/Ban'
import Users from '../assets/Users'; import Users from '../assets/Users'
import Search from '../assets/Search'; import Search from '../assets/Search'
import Close from '../assets/Close'; import Close from '../assets/Close'
import Clock from '../assets/Clock'; import Clock from '../assets/Clock'
import type { IconNames } from '../types'; import type { IconNames } from '../types'
import { exhaustiveTypeCheck } from '../utils'; import { exhaustiveTypeCheck } from '../utils'
export const Icon = ({ export const Icon = ({
iconName, iconName,
@ -20,39 +20,39 @@ export const Icon = ({
normalColor, normalColor,
activeColor, activeColor,
}: { }: {
iconName: IconNames | 'Close'; iconName: IconNames | 'Close'
isActive: boolean; isActive: boolean
normalColor: string; normalColor: string
activeColor: string; activeColor: string
}) => { }) => {
const color = isActive ? activeColor : normalColor; const color = isActive ? activeColor : normalColor
switch (iconName) { switch (iconName) {
case 'Smile': case 'Smile':
return <Smile fill={color} />; return <Smile fill={color} />
case 'Trees': case 'Trees':
return <Trees fill={color} />; return <Trees fill={color} />
case 'Pizza': case 'Pizza':
return <Pizza fill={color} />; return <Pizza fill={color} />
case 'Plane': case 'Plane':
return <Plane fill={color} />; return <Plane fill={color} />
case 'Football': case 'Football':
return <Football fill={color} />; return <Football fill={color} />
case 'Lightbulb': case 'Lightbulb':
return <Lightbulb fill={color} />; return <Lightbulb fill={color} />
case 'Flag': case 'Flag':
return <Flag fill={color} />; return <Flag fill={color} />
case 'Ban': case 'Ban':
return <Ban fill={color} />; return <Ban fill={color} />
case 'Users': case 'Users':
return <Users fill={color} />; return <Users fill={color} />
case 'Search': case 'Search':
return <Search fill={color} />; return <Search fill={color} />
case 'Close': case 'Close':
return <Close fill={color} />; return <Close fill={color} />
case 'Clock': case 'Clock':
return <Clock fill={color} />; return <Clock fill={color} />
default: default:
exhaustiveTypeCheck(iconName); exhaustiveTypeCheck(iconName)
return null; return null
} }
}; }

View File

@ -1,24 +1,17 @@
import * as React from 'react'; import * as React from 'react'
import { import { Animated, useWindowDimensions, StyleSheet, View, PanResponder } from 'react-native'
Animated, import { getHeight } from '../utils'
useWindowDimensions, import { KeyboardContext } from '../contexts/KeyboardContext'
StyleSheet,
View,
PanResponder,
} from 'react-native';
import { getHeight } from '../utils';
import { KeyboardContext } from '../contexts/KeyboardContext';
type KnobProps = { type KnobProps = {
offsetY: Animated.Value; offsetY: Animated.Value
height: Animated.Value; height: Animated.Value
onClose: () => void; onClose: () => void
}; }
export const Knob = ({ offsetY, height, onClose }: KnobProps) => { export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
const { height: screenHeight } = useWindowDimensions(); const { height: screenHeight } = useWindowDimensions()
const { expandedHeight, defaultHeight, knobStyles } = const { expandedHeight, defaultHeight, knobStyles } = React.useContext(KeyboardContext)
React.useContext(KeyboardContext);
const panResponder = React.useRef( const panResponder = React.useRef(
PanResponder.create({ PanResponder.create({
@ -34,28 +27,28 @@ export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
Animated.spring(offsetY, { Animated.spring(offsetY, {
useNativeDriver: false, useNativeDriver: false,
toValue: 0, toValue: 0,
}).start(); }).start()
if (gestureState.dy < -50) { if (gestureState.dy < -50) {
Animated.spring(height, { Animated.spring(height, {
useNativeDriver: false, useNativeDriver: false,
toValue: getHeight(expandedHeight, screenHeight), toValue: getHeight(expandedHeight, screenHeight),
}).start(); }).start()
} else if (gestureState.dy > 150) { } else if (gestureState.dy > 150) {
height.setValue(getHeight(defaultHeight, screenHeight)); height.setValue(getHeight(defaultHeight, screenHeight))
offsetY.setValue(0); offsetY.setValue(0)
onClose(); onClose()
} else { } else {
Animated.spring(height, { Animated.spring(height, {
useNativeDriver: false, useNativeDriver: false,
toValue: getHeight(defaultHeight, screenHeight), toValue: getHeight(defaultHeight, screenHeight),
}).start(); }).start()
} }
}, },
onShouldBlockNativeResponder: () => { onShouldBlockNativeResponder: () => {
return true; return true
}, },
}) })
).current; ).current
return ( return (
<View {...panResponder.panHandlers}> <View {...panResponder.panHandlers}>
@ -63,8 +56,8 @@ export const Knob = ({ offsetY, height, onClose }: KnobProps) => {
<Animated.View style={[styles.knob, knobStyles]} /> <Animated.View style={[styles.knob, knobStyles]} />
</View> </View>
</View> </View>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
panContainer: { panContainer: {
@ -86,4 +79,4 @@ const styles = StyleSheet.create({
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowRadius: 5, shadowRadius: 5,
}, },
}); })

View File

@ -1,4 +1,4 @@
import * as React from 'react'; import * as React from 'react'
import { import {
SafeAreaView, SafeAreaView,
Modal, Modal,
@ -7,58 +7,47 @@ import {
StyleSheet, StyleSheet,
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native'; } from 'react-native'
import { KeyboardContext } from '../contexts/KeyboardContext'; import { KeyboardContext } from '../contexts/KeyboardContext'
import { useTimeout } from '../hooks/useTimeout'; import { useTimeout } from '../hooks/useTimeout'
type ModalWithBackdropProps = { type ModalWithBackdropProps = {
isOpen: boolean; isOpen: boolean
backdropPress: () => void; backdropPress: () => void
children: React.ReactNode; children: React.ReactNode
}; }
export const ModalWithBackdrop = ({ export const ModalWithBackdrop = ({ isOpen, backdropPress, children }: ModalWithBackdropProps) => {
isOpen, const { height: screenHeight } = useWindowDimensions()
backdropPress, const translateY = React.useRef(new Animated.Value(screenHeight)).current
children, const { backdropColor } = React.useContext(KeyboardContext)
}: ModalWithBackdropProps) => { const handleTimeout = useTimeout()
const { height: screenHeight } = useWindowDimensions();
const translateY = React.useRef(new Animated.Value(screenHeight)).current;
const { backdropColor } = React.useContext(KeyboardContext);
const handleTimeout = useTimeout();
React.useEffect(() => { React.useEffect(() => {
Animated.spring(translateY, { Animated.spring(translateY, {
toValue: isOpen ? 0 : screenHeight, toValue: isOpen ? 0 : screenHeight,
useNativeDriver: true, useNativeDriver: true,
}).start(); }).start()
}, [isOpen, screenHeight, translateY]); }, [isOpen, screenHeight, translateY])
const handleClose = () => { const handleClose = () => {
Animated.spring(translateY, { Animated.spring(translateY, {
toValue: screenHeight, toValue: screenHeight,
useNativeDriver: true, useNativeDriver: true,
}).start(); }).start()
handleTimeout(() => backdropPress(), 200); handleTimeout(() => backdropPress(), 200)
}; }
return ( return (
<Modal visible={isOpen} animationType="fade" transparent={true}> <Modal visible={isOpen} animationType="fade" transparent={true}>
<TouchableOpacity <TouchableOpacity style={styles.modalContainer} activeOpacity={1} onPress={handleClose}>
style={styles.modalContainer} <View style={[styles.modalContainer, { backgroundColor: backdropColor }]}>
activeOpacity={1}
onPress={handleClose}
>
<View
style={[styles.modalContainer, { backgroundColor: backdropColor }]}
>
<SafeAreaView style={styles.modalContainer}> <SafeAreaView style={styles.modalContainer}>
<TouchableOpacity activeOpacity={1}> <TouchableOpacity activeOpacity={1}>
<Animated.View <Animated.View
style={{ style={{
transform: [{ translateY }], transform: [{ translateY }],
}} }}>
>
{children} {children}
</Animated.View> </Animated.View>
</TouchableOpacity> </TouchableOpacity>
@ -66,8 +55,8 @@ export const ModalWithBackdrop = ({
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</Modal> </Modal>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
modalContainer: { flex: 1, justifyContent: 'flex-end' }, modalContainer: { flex: 1, justifyContent: 'flex-end' },
@ -80,4 +69,4 @@ const styles = StyleSheet.create({
shadowRadius: 5, shadowRadius: 5,
elevation: 10, elevation: 10,
}, },
}); })

View File

@ -1,17 +1,11 @@
import * as React from 'react'; import * as React from 'react'
import { import { View, StyleSheet, TextInput, TouchableOpacity, FlatList } from 'react-native'
View, import { KeyboardContext } from '../contexts/KeyboardContext'
StyleSheet, import { Icon } from './Icon'
TextInput,
TouchableOpacity,
FlatList,
} from 'react-native';
import { KeyboardContext } from '../contexts/KeyboardContext';
import { Icon } from './Icon';
type SearchBarProps = { type SearchBarProps = {
flatListRef: React.RefObject<FlatList>; flatListRef: React.RefObject<FlatList>
}; }
export const SearchBar = ({ flatListRef }: SearchBarProps) => { export const SearchBar = ({ flatListRef }: SearchBarProps) => {
const { const {
@ -23,18 +17,18 @@ export const SearchBar = ({ flatListRef }: SearchBarProps) => {
searchBarStyles, searchBarStyles,
searchBarTextStyles, searchBarTextStyles,
searchBarPlaceholderColor, searchBarPlaceholderColor,
} = React.useContext(KeyboardContext); } = React.useContext(KeyboardContext)
const inputRef = React.useRef<TextInput>(null); const inputRef = React.useRef<TextInput>(null)
const handleSearch = (text: string) => { const handleSearch = (text: string) => {
setSearchPhrase(text); setSearchPhrase(text)
}; }
const clearPhrase = () => { const clearPhrase = () => {
setSearchPhrase(''); setSearchPhrase('')
inputRef.current?.blur(); inputRef.current?.blur()
setActiveCategoryIndex(0); setActiveCategoryIndex(0)
flatListRef?.current?.scrollToIndex({ index: 0, animated: true }); flatListRef?.current?.scrollToIndex({ index: 0, animated: true })
}; }
return ( return (
<View style={[styles.container, searchBarStyles]}> <View style={[styles.container, searchBarStyles]}>
@ -57,8 +51,8 @@ export const SearchBar = ({ flatListRef }: SearchBarProps) => {
</TouchableOpacity> </TouchableOpacity>
)} )}
</View> </View>
); )
}; }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -78,4 +72,4 @@ const styles = StyleSheet.create({
button: { button: {
marginRight: 8, marginRight: 8,
}, },
}); })

View File

@ -1,28 +1,28 @@
import * as React from 'react'; import * as React from 'react'
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
import type { JsonEmoji } from '../types'; import type { JsonEmoji } from '../types'
export class SingleEmoji extends React.Component<{ export class SingleEmoji extends React.Component<{
item: JsonEmoji; item: JsonEmoji
onPress: () => void; onPress: () => void
emojiSize: number; emojiSize: number
}> { }> {
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false
} }
render() { render() {
const { item, emojiSize, onPress } = this.props; const { item, emojiSize, onPress } = this.props
return ( return (
<TouchableOpacity onPress={onPress} style={styles.container}> <TouchableOpacity onPress={onPress} style={styles.container}>
<View style={styles.iconContainer}> <View style={styles.iconContainer}>
<Text style={{ fontSize: emojiSize }}>{item.emoji}</Text> <Text style={{ fontSize: emojiSize }}>{item.emoji}</Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); )
} }
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, padding: 8 }, container: { flex: 1, padding: 8 },
iconContainer: { justifyContent: 'center', alignItems: 'center' }, iconContainer: { justifyContent: 'center', alignItems: 'center' },
}); })

View File

@ -1,62 +1,52 @@
import * as React from 'react'; import * as React from 'react'
import type { TextStyle, ViewStyle } from 'react-native'; import type { TextStyle, ViewStyle } from 'react-native'
import { import { defaultKeyboardContext, defaultKeyboardValues } from './KeyboardProvider'
defaultKeyboardContext, import type { CategoryTranslation, EmojiType, CategoryTypes, CategoryPosition } from '../types'
defaultKeyboardValues,
} from './KeyboardProvider';
import type {
CategoryTranslation,
EmojiType,
CategoryTypes,
CategoryPosition,
} from '../types';
export type OnEmojiSelected = (emoji: EmojiType) => void; export type OnEmojiSelected = (emoji: EmojiType) => void
export type KeyboardProps = { export type KeyboardProps = {
open: boolean; open: boolean
onClose: () => void; onClose: () => void
onEmojiSelected: OnEmojiSelected; onEmojiSelected: OnEmojiSelected
emojiSize?: number; emojiSize?: number
containerStyles?: ViewStyle; containerStyles?: ViewStyle
knobStyles?: ViewStyle; knobStyles?: ViewStyle
headerStyles?: TextStyle; headerStyles?: TextStyle
expandable?: boolean; expandable?: boolean
hideHeader?: boolean; hideHeader?: boolean
defaultHeight?: number | string; defaultHeight?: number | string
expandedHeight?: number | string; expandedHeight?: number | string
backdropColor?: string; backdropColor?: string
categoryColor?: string; categoryColor?: string
activeCategoryColor?: string; activeCategoryColor?: string
categoryContainerColor?: string; categoryContainerColor?: string
activeCategoryContainerColor?: string; activeCategoryContainerColor?: string
onCategoryChangeFailed?: (info: { onCategoryChangeFailed?: (info: {
index: number; index: number
highestMeasuredFrameIndex: number; highestMeasuredFrameIndex: number
averageItemLength: number; averageItemLength: number
}) => void; }) => void
translation?: CategoryTranslation; translation?: CategoryTranslation
disabledCategory?: CategoryTypes[]; disabledCategory?: CategoryTypes[]
enableRecentlyUsed?: boolean; enableRecentlyUsed?: boolean
categoryPosition?: CategoryPosition; categoryPosition?: CategoryPosition
enableSearchBar?: boolean; enableSearchBar?: boolean
closeSearchColor?: string; closeSearchColor?: string
searchBarStyles?: ViewStyle; searchBarStyles?: ViewStyle
searchBarTextStyles?: TextStyle; searchBarTextStyles?: TextStyle
searchBarPlaceholderColor?: string; searchBarPlaceholderColor?: string
}; }
export type ContextValues = { export type ContextValues = {
activeCategoryIndex: number; activeCategoryIndex: number
setActiveCategoryIndex: (index: number) => void; setActiveCategoryIndex: (index: number) => void
numberOfColumns: number; numberOfColumns: number
width: number; width: number
searchPhrase: string; searchPhrase: string
setSearchPhrase: (phrase: string) => void; setSearchPhrase: (phrase: string) => void
}; }
export const KeyboardContext = React.createContext< export const KeyboardContext = React.createContext<Required<KeyboardProps> & ContextValues>({
Required<KeyboardProps> & ContextValues
>({
...defaultKeyboardContext, ...defaultKeyboardContext,
...defaultKeyboardValues, ...defaultKeyboardValues,
}); })

View File

@ -1,18 +1,13 @@
import * as React from 'react'; import * as React from 'react'
import { useWindowDimensions } from 'react-native'; import { useWindowDimensions } from 'react-native'
import { import { KeyboardProps, ContextValues, KeyboardContext, OnEmojiSelected } from './KeyboardContext'
KeyboardProps, import en from '../translation/en'
ContextValues, import type { EmojiType } from '../types'
KeyboardContext,
OnEmojiSelected,
} from './KeyboardContext';
import en from '../translation/en';
import type { EmojiType } from '../types';
type ProviderProps = Partial<KeyboardProps> & { type ProviderProps = Partial<KeyboardProps> & {
children: React.ReactNode; children: React.ReactNode
onEmojiSelected: OnEmojiSelected; onEmojiSelected: OnEmojiSelected
}; }
export const defaultKeyboardContext: Required<KeyboardProps> = { export const defaultKeyboardContext: Required<KeyboardProps> = {
open: false, open: false,
@ -32,7 +27,7 @@ export const defaultKeyboardContext: Required<KeyboardProps> = {
categoryContainerColor: '#e3dbcd', categoryContainerColor: '#e3dbcd',
activeCategoryContainerColor: '#ffffff', activeCategoryContainerColor: '#ffffff',
onCategoryChangeFailed: (info) => { onCategoryChangeFailed: (info) => {
console.warn(info); console.warn(info)
}, },
translation: en, translation: en,
disabledCategory: [], disabledCategory: [],
@ -43,7 +38,7 @@ export const defaultKeyboardContext: Required<KeyboardProps> = {
searchBarStyles: {}, searchBarStyles: {},
searchBarTextStyles: {}, searchBarTextStyles: {},
searchBarPlaceholderColor: '#00000055', searchBarPlaceholderColor: '#00000055',
}; }
export const defaultKeyboardValues: ContextValues = { export const defaultKeyboardValues: ContextValues = {
activeCategoryIndex: 0, activeCategoryIndex: 0,
@ -52,25 +47,19 @@ export const defaultKeyboardValues: ContextValues = {
width: 0, width: 0,
searchPhrase: '', searchPhrase: '',
setSearchPhrase: (_phrase: string) => {}, setSearchPhrase: (_phrase: string) => {},
}; }
export const KeyboardProvider: React.FC<ProviderProps> = React.memo((props) => { export const KeyboardProvider: React.FC<ProviderProps> = React.memo((props) => {
const { width } = useWindowDimensions(); const { width } = useWindowDimensions()
const [activeCategoryIndex, setActiveCategoryIndex] = React.useState(0); const [activeCategoryIndex, setActiveCategoryIndex] = React.useState(0)
const [searchPhrase, setSearchPhrase] = React.useState(''); const [searchPhrase, setSearchPhrase] = React.useState('')
const numberOfColumns = React.useRef<number>( const numberOfColumns = React.useRef<number>(
Math.floor( Math.floor(width / ((props.emojiSize ? props.emojiSize : defaultKeyboardContext.emojiSize) * 2))
width /
((props.emojiSize
? props.emojiSize
: defaultKeyboardContext.emojiSize) *
2)
) )
);
React.useEffect(() => { React.useEffect(() => {
if (props.open) setActiveCategoryIndex(0); if (props.open) setActiveCategoryIndex(0)
setSearchPhrase(''); setSearchPhrase('')
}, [props.open]); }, [props.open])
const value: Required<KeyboardProps> & ContextValues = { const value: Required<KeyboardProps> & ContextValues = {
...defaultKeyboardContext, ...defaultKeyboardContext,
@ -82,12 +71,8 @@ export const KeyboardProvider: React.FC<ProviderProps> = React.memo((props) => {
width, width,
searchPhrase, searchPhrase,
setSearchPhrase, setSearchPhrase,
}; }
return ( return <KeyboardContext.Provider value={value}>{props.children}</KeyboardContext.Provider>
<KeyboardContext.Provider value={value}> })
{props.children}
</KeyboardContext.Provider>
);
});
KeyboardProvider.displayName = 'KeyboardProvider'; KeyboardProvider.displayName = 'KeyboardProvider'

View File

@ -1,19 +1,17 @@
import { MutableRefObject, useCallback, useEffect, useRef } from 'react'; import { MutableRefObject, useCallback, useEffect, useRef } from 'react'
export const useTimeout = () => { export const useTimeout = () => {
const timeoutRef = useRef(null) as MutableRefObject<ReturnType< const timeoutRef = useRef(null) as MutableRefObject<ReturnType<typeof setTimeout> | null>
typeof setTimeout
> | null>;
useEffect(() => { useEffect(() => {
return () => { return () => {
if (timeoutRef.current) { if (timeoutRef.current) {
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current)
} }
}; }
}, []); }, [])
return useCallback((callback: () => void, time: number) => { return useCallback((callback: () => void, time: number) => {
timeoutRef.current = setTimeout(callback, time); timeoutRef.current = setTimeout(callback, time)
}, []); }, [])
}; }

View File

@ -1,9 +1,9 @@
import { EmojiPicker } from './EmojiPicker'; import { EmojiPicker } from './EmojiPicker'
import { EmojiKeyboard } from './EmojiKeyboard'; import { EmojiKeyboard } from './EmojiKeyboard'
import en from './translation/en'; import en from './translation/en'
import pl from './translation/pl'; import pl from './translation/pl'
export { EmojiKeyboard }; export { EmojiKeyboard }
export { en, pl }; export { en, pl }
export default EmojiPicker; export default EmojiPicker

View File

@ -1,13 +1,13 @@
import type { JsonEmoji } from 'src/types'; import type { JsonEmoji } from 'src/types'
export type RecentEmojiState = { export type RecentEmojiState = {
recentlyUsed: JsonEmoji[]; recentlyUsed: JsonEmoji[]
}; }
export type RecentEmojiAction = export type RecentEmojiAction =
| { type: 'RECENT_EMOJI_ADD'; payload: JsonEmoji } | { type: 'RECENT_EMOJI_ADD'; payload: JsonEmoji }
| { type: 'RECENT_EMOJI_REMOVE'; payload: JsonEmoji } | { type: 'RECENT_EMOJI_REMOVE'; payload: JsonEmoji }
| { type: 'RECENT_EMOJI_CLEAR' }; | { type: 'RECENT_EMOJI_CLEAR' }
export default function recentEmojiReducer( export default function recentEmojiReducer(
state: RecentEmojiState, state: RecentEmojiState,
@ -18,18 +18,18 @@ export default function recentEmojiReducer(
return { return {
...state, ...state,
recentlyUsed: [action.payload, ...filterEmoji(state, action.payload)], recentlyUsed: [action.payload, ...filterEmoji(state, action.payload)],
}; }
case 'RECENT_EMOJI_REMOVE': case 'RECENT_EMOJI_REMOVE':
return { return {
...state, ...state,
recentlyUsed: filterEmoji(state, action.payload), recentlyUsed: filterEmoji(state, action.payload),
}; }
case 'RECENT_EMOJI_CLEAR': case 'RECENT_EMOJI_CLEAR':
return { ...state, recentlyUsed: [] }; return { ...state, recentlyUsed: [] }
default: default:
return state; return state
} }
} }
const filterEmoji = (state: RecentEmojiState, emoji: JsonEmoji) => const filterEmoji = (state: RecentEmojiState, emoji: JsonEmoji) =>
state.recentlyUsed.filter((usedEmoji) => usedEmoji.emoji !== emoji.emoji); state.recentlyUsed.filter((usedEmoji) => usedEmoji.emoji !== emoji.emoji)

View File

@ -1,11 +1,8 @@
import recentEmojiReducer, { import recentEmojiReducer, { RecentEmojiAction, RecentEmojiState } from './RecentEmojiReducer'
RecentEmojiAction,
RecentEmojiState,
} from './RecentEmojiReducer';
// TODO: // TODO:
// - combine Keyboard reducers in future // - combine Keyboard reducers in future
export type KeyboardState = RecentEmojiState; export type KeyboardState = RecentEmojiState
export type KeyboardAction = RecentEmojiAction; export type KeyboardAction = RecentEmojiAction
export default recentEmojiReducer; export default recentEmojiReducer

View File

@ -1,29 +1,29 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react'
import type { KeyboardAction, KeyboardState } from './reducers'; import type { KeyboardAction, KeyboardState } from './reducers'
import keyboardReducer from './reducers'; import keyboardReducer from './reducers'
let globalKeyboardState: KeyboardState = { let globalKeyboardState: KeyboardState = {
recentlyUsed: [], recentlyUsed: [],
}; }
type KeyboardStateSetter = () => any; type KeyboardStateSetter = () => any
const keyboardStateListeners = new Set<KeyboardStateSetter>(); const keyboardStateListeners = new Set<KeyboardStateSetter>()
const setKeyboardState = (action: KeyboardAction) => { const setKeyboardState = (action: KeyboardAction) => {
globalKeyboardState = keyboardReducer(globalKeyboardState, action); globalKeyboardState = keyboardReducer(globalKeyboardState, action)
keyboardStateListeners.forEach((listener) => listener()); keyboardStateListeners.forEach((listener) => listener())
}; }
export const useKeyboardStore = () => { export const useKeyboardStore = () => {
const [keyboardState, setState] = useState(globalKeyboardState); const [keyboardState, setState] = useState(globalKeyboardState)
useEffect(() => { useEffect(() => {
const listener = () => setState(globalKeyboardState); const listener = () => setState(globalKeyboardState)
keyboardStateListeners.add(listener); keyboardStateListeners.add(listener)
return () => { return () => {
keyboardStateListeners.delete(listener); keyboardStateListeners.delete(listener)
}; }
}, [keyboardState]); }, [keyboardState])
return { keyboardState, setKeyboardState }; return { keyboardState, setKeyboardState }
}; }

View File

@ -1,4 +1,4 @@
import type { CategoryTranslation } from '../types'; import type { CategoryTranslation } from '../types'
export const en: CategoryTranslation = { export const en: CategoryTranslation = {
recently_used: 'Recently used', recently_used: 'Recently used',
@ -12,5 +12,5 @@ export const en: CategoryTranslation = {
symbols: 'Symbols', symbols: 'Symbols',
flags: 'Flags', flags: 'Flags',
search: 'Search', search: 'Search',
}; }
export default en; export default en

View File

@ -1,4 +1,4 @@
import type { CategoryTranslation } from '../types'; import type { CategoryTranslation } from '../types'
const pl: CategoryTranslation = { const pl: CategoryTranslation = {
recently_used: 'Ostatnio używane', recently_used: 'Ostatnio używane',
@ -12,5 +12,5 @@ const pl: CategoryTranslation = {
symbols: 'Symbole', symbols: 'Symbole',
flags: 'Flagi', flags: 'Flagi',
search: 'Szukaj', search: 'Szukaj',
}; }
export default pl; export default pl

View File

@ -10,39 +10,39 @@ export const CATEGORIES_NAVIGATION = [
{ icon: 'Flag', category: 'flags' }, { icon: 'Flag', category: 'flags' },
{ icon: 'Clock', category: 'recently_used' }, { icon: 'Clock', category: 'recently_used' },
{ icon: 'Search', category: 'search' }, { icon: 'Search', category: 'search' },
] as const; ] as const
export type IconNames = typeof CATEGORIES_NAVIGATION[number]['icon']; export type IconNames = typeof CATEGORIES_NAVIGATION[number]['icon']
export type CategoryTypes = typeof CATEGORIES_NAVIGATION[number]['category']; export type CategoryTypes = typeof CATEGORIES_NAVIGATION[number]['category']
export const CATEGORIES: readonly CategoryTypes[] = CATEGORIES_NAVIGATION.map( export const CATEGORIES: readonly CategoryTypes[] = CATEGORIES_NAVIGATION.map(
({ category }) => category ({ category }) => category
); )
export type JsonEmoji = { export type JsonEmoji = {
emoji: string; emoji: string
name: string; name: string
v: string; v: string
}; }
export type EmojiType = { export type EmojiType = {
emoji: string; emoji: string
name: string; name: string
slug: string; slug: string
unicode_version: string; unicode_version: string
}; }
export type CategoryPosition = 'floating' | 'top' | 'bottom'; export type CategoryPosition = 'floating' | 'top' | 'bottom'
export type CategoryNavigationItem = { export type CategoryNavigationItem = {
icon: IconNames; icon: IconNames
category: CategoryTypes; category: CategoryTypes
}; }
export type CategoryTranslation = { export type CategoryTranslation = {
[key in CategoryTypes]: string; [key in CategoryTypes]: string
}; }
export type EmojisByCategory = { export type EmojisByCategory = {
title: CategoryTypes; title: CategoryTypes
data: EmojiType[]; data: EmojiType[]
}; }

View File

@ -1,29 +1,22 @@
import { CATEGORIES, CategoryTypes, JsonEmoji } from './types'; import { CATEGORIES, CategoryTypes, JsonEmoji } from './types'
export const getHeight = (value: string | number, screenHeight: number) => export const getHeight = (value: string | number, screenHeight: number) =>
typeof value === 'number' typeof value === 'number' ? value : (screenHeight / 100) * parseInt(value.replace('%', ''), 10)
? value
: (screenHeight / 100) * parseInt(value.replace('%', ''), 10);
export const exhaustiveTypeCheck = (arg: never, strict = true) => { export const exhaustiveTypeCheck = (arg: never, strict = true) => {
console.log(`unhandled union case for : ${arg}`); console.log(`unhandled union case for : ${arg}`)
if (strict) { if (strict) {
throw new Error(`unhandled union case for : ${arg}`); throw new Error(`unhandled union case for : ${arg}`)
} }
}; }
export const parseEmoji = (emoji: JsonEmoji) => ({ export const parseEmoji = (emoji: JsonEmoji) => ({
name: emoji.name, name: emoji.name,
emoji: emoji.emoji, emoji: emoji.emoji,
unicode_version: emoji.v, unicode_version: emoji.v,
slug: emoji.name.replace(' ', '_'), slug: emoji.name.replace(' ', '_'),
}); })
export const getCategoryIndex = ( export const getCategoryIndex = (disabledCategory: CategoryTypes[], category: CategoryTypes) => ({
disabledCategory: CategoryTypes[], index: CATEGORIES.filter((name) => !disabledCategory.includes(name)).indexOf(category),
category: CategoryTypes })
) => ({
index: CATEGORIES.filter((name) => !disabledCategory.includes(name)).indexOf(
category
),
});

View File

@ -1,4 +1,3 @@
{ {
"extends": "./tsconfig", "extends": "./tsconfig",
"exclude": ["example"] "exclude": ["example"]

View File

@ -1837,6 +1837,11 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@twgdev/prettier-config@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@twgdev/prettier-config/-/prettier-config-1.0.2.tgz#5a313df4341efb2221b5e7b4c52153e86861b1dc"
integrity sha512-79UJ8Cp+dIoE8V5g8y2SDvshhGQsGaDkTdY3YVbbMzTUW/Bdv/0AOFASNNGKXy7pfkcukeUD27cgRECAU4Swyg==
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
version "7.1.14" version "7.1.14"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402"