// Displays a find/replace dialog const React = require('react'); const { useMemo, useState, useEffect } = require('react'); const MaterialCommunityIcon = require('react-native-vector-icons/MaterialCommunityIcons').default; import { SearchControl, SearchState, EditorSettings } from './types'; import { _ } from '@joplin/lib/locale'; import { BackHandler, TextInput, View, Text, StyleSheet, ViewStyle } from 'react-native'; import { Theme } from '@joplin/lib/themes/type'; import CustomButton from '../CustomButton'; const buttonSize = 48; type OnChangeCallback = (text: string)=> void; type Callback = ()=> void; export const defaultSearchState: SearchState = { useRegex: false, caseSensitive: false, searchText: '', replaceText: '', dialogVisible: false, }; export interface SearchPanelProps { searchControl: SearchControl; searchState: SearchState; editorSettings: EditorSettings; } interface ActionButtonProps { styles: any; themeId: number; iconName: string; title: string; onPress: Callback; } const ActionButton = ( props: ActionButtonProps ) => { return ( ); }; interface ToggleButtonProps { styles: any; themeId: number; iconName: string; title: string; active: boolean; onToggle: Callback; } const ToggleButton = (props: ToggleButtonProps) => { const active = props.active; return ( ); }; const useStyles = (theme: Theme) => { return useMemo(() => { const buttonStyle: ViewStyle = { width: buttonSize, height: buttonSize, backgroundColor: theme.backgroundColor4, alignItems: 'center', justifyContent: 'center', flexShrink: 1, }; const buttonTextStyle = { color: theme.color4, fontSize: 30, }; return StyleSheet.create({ button: buttonStyle, toggleButton: { ...buttonStyle, }, toggleButtonActive: { ...buttonStyle, backgroundColor: theme.backgroundColor3, }, input: { flexGrow: 1, height: buttonSize, backgroundColor: theme.backgroundColor4, color: theme.color4, }, buttonText: buttonTextStyle, activeButtonText: { ...buttonTextStyle, color: theme.color4, }, text: { color: theme.color, }, labeledInput: { flexGrow: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginLeft: 10, }, }); }, [theme]); }; export const SearchPanel = (props: SearchPanelProps) => { const theme = props.editorSettings.themeData; const placeholderColor = theme.color3; const styles = useStyles(theme); const [showingAdvanced, setShowAdvanced] = useState(false); const state = props.searchState; const control = props.searchControl; const updateSearchState = (changedData: any) => { const newState = Object.assign({}, state, changedData); control.setSearchState(newState); }; // Creates a TextInut with the given parameters const createInput = ( placeholder: string, value: string, onChange: OnChangeCallback, autoFocus: boolean ) => { return ( ); }; // Close the search dialog on back button press useEffect(() => { // Only register the listener if the dialog is visible if (!state.dialogVisible) { return () => {}; } const backListener = BackHandler.addEventListener('hardwareBackPress', () => { control.hideSearch(); return true; }); return () => backListener.remove(); // eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied }, [state.dialogVisible]); const themeId = props.editorSettings.themeId; const closeButton = ( ); const showDetailsButton = ( setShowAdvanced(true)} title={_('Show advanced')} /> ); const hideDetailsButton = ( setShowAdvanced(false)} title={_('Hide advanced')} /> ); const searchTextInput = createInput( _('Search for...'), state.searchText, (newText: string) => { updateSearchState({ searchText: newText, }); }, // Autofocus true ); const replaceTextInput = createInput( _('Replace with...'), state.replaceText, (newText: string) => { updateSearchState({ replaceText: newText, }); }, // Don't autofocus false ); const labeledSearchInput = ( {_('Find: ')} { searchTextInput } ); const labeledReplaceInput = ( {_('Replace: ')} { replaceTextInput } ); const toNextButton = ( ); const toPrevButton = ( ); const replaceButton = ( ); const replaceAllButton = ( ); const regexpButton = ( { updateSearchState({ useRegex: !state.useRegex, }); }} active={state.useRegex} title={_('Regular expression')} /> ); const caseSensitiveButton = ( { updateSearchState({ caseSensitive: !state.caseSensitive, }); }} active={state.caseSensitive} title={_('Case sensitive')} /> ); const simpleLayout = ( { closeButton } { searchTextInput } { showDetailsButton } { toPrevButton } { toNextButton } ); const advancedLayout = ( { closeButton } { labeledSearchInput } { hideDetailsButton } { toPrevButton } { toNextButton } { regexpButton } { caseSensitiveButton } { labeledReplaceInput } { replaceButton } { replaceAllButton } ); if (!state.dialogVisible) { return null; } return showingAdvanced ? advancedLayout : simpleLayout; }; export default SearchPanel;