mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-03 04:31:46 +02:00
add searchreplace in toolbar
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {Platform} from 'react-native';
|
||||
import {ScrollView, View} from 'react-native';
|
||||
import {useTracked} from '../../../../provider';
|
||||
import {normalize} from '../../../../utils/SizeUtils';
|
||||
import React, { useEffect } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { useTracked } from '../../../../provider';
|
||||
import { useEditorStore } from '../../../../provider/stores';
|
||||
import { normalize } from '../../../../utils/SizeUtils';
|
||||
import HistoryComponent from '../../HistoryComponent';
|
||||
import {TOOLBAR_CONFIG} from './config';
|
||||
import {properties, toolbarRef} from './constants';
|
||||
import { TOOLBAR_CONFIG } from './config';
|
||||
import { properties, toolbarRef } from './constants';
|
||||
import ToolbarGroup from './group';
|
||||
import SearcReplace from './searchreplace';
|
||||
import Tooltip from './tooltip';
|
||||
|
||||
|
||||
const EditorToolbar = React.memo(
|
||||
() => {
|
||||
const [state] = useTracked();
|
||||
const {colors} = state;
|
||||
const config = TOOLBAR_CONFIG;
|
||||
const searchReplace = useEditorStore(state => state.searchReplace);
|
||||
|
||||
useEffect(() => {
|
||||
properties.selection = {};
|
||||
@@ -28,44 +31,50 @@ const EditorToolbar = React.memo(
|
||||
style={{
|
||||
width: '100%',
|
||||
minHeight: normalize(50),
|
||||
position: 'relative',
|
||||
position: 'relative'
|
||||
}}>
|
||||
<Tooltip />
|
||||
<ScrollView
|
||||
ref={toolbarRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
minHeight: normalize(50),
|
||||
backgroundColor:colors.bg,
|
||||
paddingLeft: 6,
|
||||
zIndex: 11
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignItems: 'center'
|
||||
}}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
bounces={false}>
|
||||
<HistoryComponent />
|
||||
{config.map((item, index) =>
|
||||
typeof item !== 'string' ? (
|
||||
<ToolbarGroup key={item[0].format} group={item} />
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
height: 30,
|
||||
width: 2,
|
||||
marginHorizontal:2,
|
||||
backgroundColor: colors.nav
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
{searchReplace ? (
|
||||
<SearcReplace />
|
||||
) : (
|
||||
<>
|
||||
<Tooltip />
|
||||
<ScrollView
|
||||
ref={toolbarRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '100%',
|
||||
minHeight: normalize(50),
|
||||
backgroundColor: colors.bg,
|
||||
paddingLeft: 6,
|
||||
zIndex: 11
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignItems: 'center'
|
||||
}}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
bounces={false}>
|
||||
<HistoryComponent />
|
||||
{config.map((item, index) =>
|
||||
typeof item !== 'string' ? (
|
||||
<ToolbarGroup key={item[0].format} group={item} />
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
height: 30,
|
||||
width: 2,
|
||||
marginHorizontal: 2,
|
||||
backgroundColor: colors.nav
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
276
apps/mobile/src/views/Editor/tiny/toolbar/searchreplace.js
Normal file
276
apps/mobile/src/views/Editor/tiny/toolbar/searchreplace.js
Normal file
@@ -0,0 +1,276 @@
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {Button} from '../../../../components/Button';
|
||||
import Input from '../../../../components/Input';
|
||||
import {useTracked} from '../../../../provider';
|
||||
import {useSettingStore} from '../../../../provider/stores';
|
||||
import {showTooltip, TOOLTIP_POSITIONS} from '../../../../utils';
|
||||
import {SIZE} from '../../../../utils/SizeUtils';
|
||||
import {EditorWebView} from '../../Functions';
|
||||
import tiny from '../tiny';
|
||||
import {endSearch} from './commands';
|
||||
|
||||
const SearcReplace = () => {
|
||||
const [state] = useTracked();
|
||||
const {colors} = state;
|
||||
const [focusType, setFocusType] = useState(0);
|
||||
const [enableReplace, setEnableReplace] = useState(false);
|
||||
const [menu, setMenu] = useState(false);
|
||||
const deviceMode = useSettingStore(state => state.deviceMode);
|
||||
const findRef = useRef();
|
||||
const [config, setConfig] = useState({
|
||||
matchCase: false,
|
||||
matchWholeWord: false
|
||||
});
|
||||
const values = useRef({
|
||||
find: null,
|
||||
replace: null
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
findRef.current?.focus();
|
||||
}, 300);
|
||||
}, []);
|
||||
|
||||
function find() {
|
||||
if (!values.current?.find) return;
|
||||
tiny.call(
|
||||
EditorWebView,
|
||||
`tinymce.activeEditor.plugins.searchreplace.find("${values.current?.find}",${config.matchCase},${config.matchWholeWord})`
|
||||
);
|
||||
}
|
||||
|
||||
function replace(all = false) {
|
||||
if (!values.current?.replace) return;
|
||||
tiny.call(
|
||||
EditorWebView,
|
||||
`tinymce.activeEditor.plugins.searchreplace.replace("${
|
||||
values.current?.replace
|
||||
}",true${all ? ',true' : ''})`
|
||||
);
|
||||
}
|
||||
|
||||
function next() {
|
||||
tiny.call(
|
||||
EditorWebView,
|
||||
`tinymce.activeEditor.plugins.searchreplace.next()`
|
||||
);
|
||||
}
|
||||
|
||||
function prev() {
|
||||
tiny.call(
|
||||
EditorWebView,
|
||||
`tinymce.activeEditor.plugins.searchreplace.prev()`
|
||||
);
|
||||
}
|
||||
|
||||
function done() {
|
||||
endSearch();
|
||||
}
|
||||
|
||||
function toggleMatchCase() {
|
||||
setConfig(c => {
|
||||
c.matchCase = !c.matchCase;
|
||||
return {...c};
|
||||
});
|
||||
}
|
||||
|
||||
function toggleMatchWholeWord() {
|
||||
setConfig(c => {
|
||||
c.matchWholeWord = !c.matchWholeWord;
|
||||
return {...c};
|
||||
});
|
||||
}
|
||||
|
||||
const menuButtons = [
|
||||
{
|
||||
icon: 'chevron-left',
|
||||
type: 'gray',
|
||||
press: () => setMenu(false),
|
||||
fullname: 'Go back'
|
||||
},
|
||||
{
|
||||
icon: 'format-letter-case',
|
||||
type: 'gray',
|
||||
press: toggleMatchCase,
|
||||
on: config.matchCase,
|
||||
fullname: 'Match case'
|
||||
},
|
||||
{
|
||||
icon: 'format-letter-matches',
|
||||
type: 'gray',
|
||||
press: toggleMatchWholeWord,
|
||||
on: config.matchWholeWord,
|
||||
fullname: 'Match whole word'
|
||||
}
|
||||
];
|
||||
|
||||
const searchButtons = menu
|
||||
? menuButtons
|
||||
: [
|
||||
{
|
||||
icon: 'arrow-up',
|
||||
type: 'gray',
|
||||
press: prev,
|
||||
fullname: 'Previous'
|
||||
},
|
||||
{
|
||||
icon: 'arrow-down',
|
||||
type: 'gray',
|
||||
press: next,
|
||||
fullname: 'Next'
|
||||
},
|
||||
{
|
||||
icon: 'close',
|
||||
type: 'gray',
|
||||
press: done,
|
||||
fullname: 'End search'
|
||||
},
|
||||
{
|
||||
icon: 'dots-vertical',
|
||||
type: 'gray',
|
||||
press: () => setMenu(true),
|
||||
fullname: 'Show options'
|
||||
}
|
||||
];
|
||||
|
||||
const replaceButtons = [
|
||||
{
|
||||
text: 'Replace',
|
||||
type: 'grayBg',
|
||||
press: replace
|
||||
},
|
||||
{
|
||||
text: 'Replace all',
|
||||
type: 'gray',
|
||||
press: () => replace(true)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
backgroundColor: colors.bg,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
paddingLeft: 0,
|
||||
paddingBottom: 0,
|
||||
borderTopRightRadius: 10,
|
||||
borderTopLeftRadius: 10,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Button
|
||||
height="93%"
|
||||
iconSize={SIZE.md + 4}
|
||||
icon={enableReplace ? 'chevron-down' : 'chevron-right'}
|
||||
style={{
|
||||
paddingHorizontal: 3,
|
||||
marginBottom: 10
|
||||
}}
|
||||
iconColor={enableReplace ? colors.accent : colors.icon}
|
||||
onPress={() => {
|
||||
if (enableReplace) {
|
||||
if (focusType === 2) {
|
||||
findRef.current?.focus();
|
||||
}
|
||||
setEnableReplace(false);
|
||||
} else {
|
||||
setEnableReplace(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
flexDirection: deviceMode !== 'mobile' ? 'row' : 'column',
|
||||
flexShrink: 1
|
||||
}}>
|
||||
<Input
|
||||
fwdRef={findRef}
|
||||
onChangeText={value => {
|
||||
values.current.find = value;
|
||||
}}
|
||||
onFocusInput={() => setFocusType(1)}
|
||||
onSubmit={find}
|
||||
blurOnSubmit={false}
|
||||
returnKeyType="search"
|
||||
returnKeyLabel="Search"
|
||||
buttons={
|
||||
<>
|
||||
{searchButtons.map(button => (
|
||||
<Button
|
||||
title={button.text}
|
||||
fontSize={SIZE.xs}
|
||||
height={28}
|
||||
onLongPress={event => {
|
||||
console.log(event);
|
||||
showTooltip(event, button.fullname, TOOLTIP_POSITIONS.TOP);
|
||||
}}
|
||||
iconSize={SIZE.md + 4}
|
||||
width={null}
|
||||
style={{
|
||||
paddingHorizontal: button.icon ? 6 : 10
|
||||
}}
|
||||
iconColor={button.on ? colors.accent : null}
|
||||
buttonType={{
|
||||
text: button.text && colors.accent
|
||||
}}
|
||||
onPress={button.press}
|
||||
icon={button.icon}
|
||||
type={button.type}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
placeholder={'Find'}
|
||||
fontSize={SIZE.xs + 1}
|
||||
height={40}
|
||||
marginRight={deviceMode !== 'mobile' ? 10 : 0}
|
||||
/>
|
||||
|
||||
{enableReplace ? (
|
||||
<>
|
||||
<Input
|
||||
onSubmit={replace}
|
||||
onChangeText={value => {
|
||||
values.current.replace = value;
|
||||
}}
|
||||
blurOnSubmit={false}
|
||||
buttons={
|
||||
<>
|
||||
{replaceButtons.map(button => (
|
||||
<Button
|
||||
title={button.text}
|
||||
fontSize={SIZE.xs + 1}
|
||||
height={28}
|
||||
width={null}
|
||||
buttonType={{
|
||||
text: colors.accent
|
||||
}}
|
||||
onPress={button.press}
|
||||
style={{
|
||||
paddingHorizontal: button.icon ? 6 : 12
|
||||
}}
|
||||
icon={button.icon}
|
||||
type={button.type}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
placeholder={'Replace with'}
|
||||
fontSize={SIZE.xs + 1}
|
||||
onFocusInput={() => setFocusType(2)}
|
||||
height={40}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearcReplace;
|
||||
Reference in New Issue
Block a user