2024-04-12 10:29:23 +02:00
|
|
|
import { library } from "@fortawesome/fontawesome-svg-core"
|
2024-04-11 15:26:20 +02:00
|
|
|
import { fas } from "@fortawesome/free-solid-svg-icons"
|
2024-04-12 10:29:23 +02:00
|
|
|
import React, {
|
|
|
|
memo,
|
|
|
|
useCallback,
|
|
|
|
useEffect,
|
|
|
|
useState,
|
|
|
|
useTransition,
|
|
|
|
} from "react"
|
|
|
|
import { ScrollView, StyleSheet, View } from "react-native"
|
|
|
|
import { Button, List, Modal, Portal, TextInput } from "react-native-paper"
|
2024-04-11 15:26:20 +02:00
|
|
|
|
2024-04-12 10:29:23 +02:00
|
|
|
import { HabitIconList } from "./HabitIconList"
|
2024-04-11 15:26:20 +02:00
|
|
|
export interface HabitIconSelectorModalProps {
|
|
|
|
visible?: boolean
|
|
|
|
value: string
|
|
|
|
onDismiss: () => void
|
|
|
|
onSelect: (icon: string) => void
|
|
|
|
onPress: () => void
|
|
|
|
}
|
|
|
|
|
2024-04-12 10:29:23 +02:00
|
|
|
interface SearchInputProps {
|
|
|
|
searchText: string
|
|
|
|
handleSearch: (text: string) => void
|
|
|
|
}
|
|
|
|
|
|
|
|
library.add(fas)
|
|
|
|
|
|
|
|
const iconNames = Object.keys(fas).map((key) => {
|
|
|
|
return fas[key]?.iconName ?? ""
|
|
|
|
})
|
|
|
|
|
|
|
|
const findIconsInLibrary = (icon: string): string[] => {
|
|
|
|
return iconNames
|
|
|
|
.filter((name, index, self) => {
|
|
|
|
return name.includes(icon) && self.indexOf(name) === index
|
|
|
|
})
|
|
|
|
.slice(0, 20)
|
|
|
|
}
|
|
|
|
|
|
|
|
const SearchInputWithoutMemo: React.FC<SearchInputProps> = (props) => {
|
|
|
|
const { searchText, handleSearch } = props
|
|
|
|
return (
|
|
|
|
<TextInput label="Search" value={searchText} onChangeText={handleSearch} />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const SearchInput = memo(SearchInputWithoutMemo)
|
|
|
|
|
2024-04-11 15:26:20 +02:00
|
|
|
export const HabitIconSelectorModal: React.FC<HabitIconSelectorModalProps> = ({
|
|
|
|
visible = false,
|
|
|
|
value,
|
|
|
|
onDismiss,
|
|
|
|
onSelect,
|
|
|
|
onPress,
|
|
|
|
}) => {
|
|
|
|
const [selectedIcon, setSelectedIcon] = useState<string>()
|
2024-04-12 10:29:23 +02:00
|
|
|
const [possibleIcons, setPossibleIcons] = useState<string[]>([])
|
2024-04-11 15:26:20 +02:00
|
|
|
const [searchText, setSearchText] = useState<string>(value)
|
2024-04-12 10:29:23 +02:00
|
|
|
const [isPending, startTransition] = useTransition()
|
2024-04-11 15:26:20 +02:00
|
|
|
|
2024-04-12 10:29:23 +02:00
|
|
|
const handleSearch = useCallback((text: string): void => {
|
|
|
|
setSearchText(text)
|
|
|
|
}, [])
|
2024-04-11 15:26:20 +02:00
|
|
|
|
2024-04-12 10:29:23 +02:00
|
|
|
useEffect(() => {
|
|
|
|
const delay = setTimeout(() => {
|
|
|
|
startTransition(() => {
|
|
|
|
setPossibleIcons(findIconsInLibrary(searchText))
|
|
|
|
})
|
|
|
|
}, 500)
|
|
|
|
return () => {
|
|
|
|
return clearTimeout(delay)
|
|
|
|
}
|
|
|
|
}, [searchText, isPending])
|
2024-04-11 15:26:20 +02:00
|
|
|
|
2024-04-12 10:29:23 +02:00
|
|
|
const handleIconSelect = useCallback(
|
|
|
|
(icon: string): void => {
|
|
|
|
setSelectedIcon(icon)
|
|
|
|
onSelect(icon)
|
|
|
|
},
|
|
|
|
[onSelect],
|
|
|
|
)
|
2024-04-11 15:26:20 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Button onPress={onPress}>Click to open icon list</Button>
|
|
|
|
<Modal
|
|
|
|
visible={visible}
|
|
|
|
onDismiss={onDismiss}
|
|
|
|
contentContainerStyle={styles.modalContent}
|
|
|
|
>
|
|
|
|
<Portal>
|
|
|
|
<View>
|
|
|
|
<ScrollView style={styles.scrollView}>
|
|
|
|
<List.Section title="Select an icon">
|
|
|
|
<View style={styles.iconContainer}>
|
2024-04-12 10:29:23 +02:00
|
|
|
<SearchInput
|
|
|
|
searchText={searchText}
|
|
|
|
handleSearch={handleSearch}
|
|
|
|
/>
|
|
|
|
<HabitIconList
|
|
|
|
selectedIcon={selectedIcon}
|
|
|
|
possibleIcons={possibleIcons}
|
|
|
|
handleIconSelect={handleIconSelect}
|
2024-04-11 15:26:20 +02:00
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
</List.Section>
|
|
|
|
</ScrollView>
|
|
|
|
</View>
|
|
|
|
</Portal>
|
|
|
|
</Modal>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
modalContent: {
|
|
|
|
backgroundColor: "white",
|
|
|
|
padding: 20,
|
|
|
|
margin: 20,
|
|
|
|
borderRadius: 10,
|
|
|
|
},
|
|
|
|
iconContainer: {
|
|
|
|
flexDirection: "row",
|
|
|
|
flexWrap: "wrap",
|
|
|
|
justifyContent: "space-between",
|
|
|
|
},
|
|
|
|
scrollView: {
|
|
|
|
maxHeight: 10000,
|
|
|
|
maxWidth: 5000,
|
|
|
|
},
|
|
|
|
noResults: {
|
|
|
|
marginTop: 20,
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
})
|