refactor: toolOptions

This commit is contained in:
Ibrahima G. Coulibaly
2024-06-26 07:47:17 +01:00
parent 781cd16f07
commit 6e3804d2c7
9 changed files with 461 additions and 544 deletions

36
.idea/workspace.xml generated
View File

@@ -4,9 +4,16 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: loading screen">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: shuffle tools search">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/Hero.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Hero.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/change-colors-in-png/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/image/png/create-transparent/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/image/png/create-transparent/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/join/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/join/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/split/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/split/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -193,15 +200,8 @@
<workItem from="1719294110005" duration="3842000" />
<workItem from="1719339559458" duration="303000" />
<workItem from="1719340295244" duration="772000" />
<workItem from="1719363272227" duration="330000" />
</task>
<task id="LOCAL-00016" summary="ubf jn">
<option name="closed" value="true" />
<created>1719006829016</created>
<option name="number" value="00016" />
<option name="presentableId" value="LOCAL-00016" />
<option name="project" value="LOCAL" />
<updated>1719006829016</updated>
<workItem from="1719363272227" duration="390000" />
<workItem from="1719379971872" duration="4208000" />
</task>
<task id="LOCAL-00017" summary="ubf jn">
<option name="closed" value="true" />
@@ -587,7 +587,15 @@
<option name="project" value="LOCAL" />
<updated>1719360545177</updated>
</task>
<option name="localTasksCounter" value="65" />
<task id="LOCAL-00065" summary="chore: shuffle tools search">
<option name="closed" value="true" />
<created>1719363656541</created>
<option name="number" value="00065" />
<option name="presentableId" value="LOCAL-00065" />
<option name="project" value="LOCAL" />
<updated>1719363656541</updated>
</task>
<option name="localTasksCounter" value="66" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -608,7 +616,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="fix: missing files" />
<MESSAGE value="fix: create tool" />
<MESSAGE value="fix: build" />
<MESSAGE value="chore: CODEOWNERS" />
@@ -633,7 +640,8 @@
<MESSAGE value="feat: make tool responsive" />
<MESSAGE value="chore: make tool examples responsive" />
<MESSAGE value="chore: loading screen" />
<option name="LAST_COMMIT_MESSAGE" value="chore: loading screen" />
<MESSAGE value="chore: shuffle tools search" />
<option name="LAST_COMMIT_MESSAGE" value="chore: shuffle tools search" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@@ -1,9 +1,8 @@
import Typography from '@mui/material/Typography';
import React, { ReactNode } from 'react';
import { Box, Stack } from '@mui/material';
import Grid from '@mui/material/Grid';
interface ToolOptionGroup {
export interface ToolOptionGroup {
title: string;
component: ReactNode;
}

View File

@@ -1,10 +1,44 @@
import { Box, Stack, useTheme } from '@mui/material';
import SettingsIcon from '@mui/icons-material/Settings';
import Typography from '@mui/material/Typography';
import React, { ReactNode } from 'react';
import React, { ReactNode, RefObject, useContext, useEffect } from 'react';
import { Formik, FormikProps, FormikValues, useFormikContext } from 'formik';
import ToolOptionGroups, { ToolOptionGroup } from './ToolOptionGroups';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
export default function ToolOptions({ children }: { children: ReactNode }) {
export default function ToolOptions<T extends FormikValues>({
children,
initialValues,
validationSchema,
compute,
input,
getGroups,
formRef
}: {
children?: ReactNode;
initialValues: T;
validationSchema: any | (() => any);
compute: (optionsValues: T, input: any) => void;
input: any;
getGroups: (formikProps: FormikProps<T>) => ToolOptionGroup[];
formRef?: RefObject<FormikProps<T>>;
}) {
const theme = useTheme();
const FormikListenerComponent = () => {
const { values } = useFormikContext<typeof initialValues>();
const { showSnackBar } = useContext(CustomSnackBarContext);
useEffect(() => {
try {
compute(values, input);
} catch (exception: unknown) {
if (exception instanceof Error)
showSnackBar(exception.message, 'error');
}
}, [values, showSnackBar]);
return null; // This component doesn't render anything
};
return (
<Box
sx={{
@@ -19,7 +53,22 @@ export default function ToolOptions({ children }: { children: ReactNode }) {
<SettingsIcon />
<Typography fontSize={22}>Tool options</Typography>
</Stack>
<Box mt={2}>{children}</Box>
<Box mt={2}>
<Formik
innerRef={formRef}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{(formikProps) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent />
<ToolOptionGroups groups={getGroups(formikProps)} />
{children}
</Stack>
)}
</Formik>
</Box>
</Box>
);
}

View File

@@ -1,15 +1,13 @@
import { Box } from '@mui/material';
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '../../../../components/input/ToolFileInput';
import ToolFileResult from '../../../../components/result/ToolFileResult';
import ToolOptions from '../../../../components/options/ToolOptions';
import { Formik, useFormikContext } from 'formik';
import ColorSelector from '../../../../components/options/ColorSelector';
import Color from 'color';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
import ToolInputAndResult from '../../../../components/ToolInputAndResult';
import ToolOptionGroups from '../../../../components/options/ToolOptionGroups';
const initialValues = {
fromColor: 'white',
@@ -23,11 +21,8 @@ export default function ChangeColorsInPng() {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const FormikListenerComponent = ({ input }: { input: File }) => {
const { values } = useFormikContext<typeof initialValues>();
const { fromColor, toColor, similarity } = values;
useEffect(() => {
const compute = (optionsValues: typeof initialValues, input: any) => {
const { fromColor, toColor, similarity } = optionsValues;
let fromRgb: [number, number, number];
let toRgb: [number, number, number];
try {
@@ -91,18 +86,16 @@ export default function ChangeColorsInPng() {
canvas.toBlob((blob) => {
if (blob) {
const newFile = new File([blob], file.name, { type: 'image/png' });
const newFile = new File([blob], file.name, {
type: 'image/png'
});
setResult(newFile);
}
}, 'image/png');
};
processImage(input, fromRgb, toRgb, Number(similarity));
}, [input, fromColor, toColor]);
return null;
};
return (
<Box>
<ToolInputAndResult
@@ -122,17 +115,9 @@ export default function ChangeColorsInPng() {
/>
}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Box>
{input && <FormikListenerComponent input={input} />}
<ToolOptionGroups
groups={[
<ToolOptions
compute={compute}
getGroups={({ values, setFieldValue }) => [
{
title: 'From color and to color',
component: (
@@ -158,11 +143,10 @@ export default function ChangeColorsInPng() {
)
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Box>
)}
</Formik>
</ToolOptions>
</Box>
);
}

View File

@@ -1,15 +1,13 @@
import { Box } from '@mui/material';
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '../../../../components/input/ToolFileInput';
import ToolFileResult from '../../../../components/result/ToolFileResult';
import ToolOptions from '../../../../components/options/ToolOptions';
import { Formik, useFormikContext } from 'formik';
import ColorSelector from '../../../../components/options/ColorSelector';
import Color from 'color';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
import ToolInputAndResult from '../../../../components/ToolInputAndResult';
import ToolOptionGroups from '../../../../components/options/ToolOptionGroups';
const initialValues = {
fromColor: 'white',
@@ -22,11 +20,9 @@ export default function ChangeColorsInPng() {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const FormikListenerComponent = ({ input }: { input: File }) => {
const { values } = useFormikContext<typeof initialValues>();
const { fromColor, similarity } = values;
const compute = (optionsValues: typeof initialValues, input: any) => {
const { fromColor, similarity } = optionsValues;
useEffect(() => {
let fromRgb: [number, number, number];
try {
//@ts-ignore
@@ -91,9 +87,6 @@ export default function ChangeColorsInPng() {
};
processImage(input, fromRgb, Number(similarity));
}, [input, fromColor]);
return null;
};
return (
@@ -115,17 +108,9 @@ export default function ChangeColorsInPng() {
/>
}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Box>
{input && <FormikListenerComponent input={input} />}
<ToolOptionGroups
groups={[
<ToolOptions
compute={compute}
getGroups={({ values, setFieldValue }) => [
{
title: 'From color and similarity',
component: (
@@ -146,11 +131,10 @@ export default function ChangeColorsInPng() {
)
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Box>
)}
</Formik>
</ToolOptions>
</Box>
);
}

View File

@@ -1,14 +1,11 @@
import { Box, Stack } from '@mui/material';
import React, { useContext, useEffect, useState } from 'react';
import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolTextInput from '../../../components/input/ToolTextInput';
import ToolTextResult from '../../../components/result/ToolTextResult';
import { Formik, useFormikContext } from 'formik';
import * as Yup from 'yup';
import ToolOptions from '../../../components/options/ToolOptions';
import { compute, NumberExtractionType } from './service';
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
import RadioWithTextField from '../../../components/options/RadioWithTextField';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
import SimpleRadio from '../../../components/options/SimpleRadio';
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
@@ -44,25 +41,7 @@ const extractionTypes: {
export default function SplitText() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
const { showSnackBar } = useContext(CustomSnackBarContext);
const FormikListenerComponent = () => {
const { values } = useFormikContext<typeof initialValues>();
useEffect(() => {
try {
const { extractionType, printRunningSum, separator } = values;
setResult(compute(input, extractionType, printRunningSum, separator));
} catch (exception: unknown) {
if (exception instanceof Error)
showSnackBar(exception.message, 'error');
}
}, [values, input]);
return null; // This component doesn't render anything
};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
@@ -73,17 +52,8 @@ export default function SplitText() {
input={<ToolTextInput value={input} onChange={setInput} />}
result={<ToolTextResult title={'Total'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent />
<ToolOptionGroups
groups={[
<ToolOptions
getGroups={({ values, setFieldValue }) => [
{
title: 'Number extraction',
component: extractionTypes.map(
@@ -110,15 +80,15 @@ export default function SplitText() {
setFieldValue('extractionType', type)
}
onTextChange={(val) =>
setFieldValue(textValueAccessor ?? '', val)
textValueAccessor
? setFieldValue(textValueAccessor, val)
: null
}
/>
) : (
<SimpleRadio
key={title}
onChange={() =>
setFieldValue('extractionType', type)
}
onChange={() => setFieldValue('extractionType', type)}
fieldName={'extractionType'}
value={values.extractionType}
description={description}
@@ -132,22 +102,21 @@ export default function SplitText() {
component: (
<CheckboxWithDesc
title={'Print Running Sum'}
description={
"Display the sum as it's calculated step by step."
}
description={"Display the sum as it's calculated step by step."}
checked={values.printRunningSum}
onChange={(value) =>
setFieldValue('printRunningSum', value)
}
onChange={(value) => setFieldValue('printRunningSum', value)}
/>
)
}
]}
compute={(optionsValues, input) => {
const { extractionType, printRunningSum, separator } = optionsValues;
setResult(compute(input, extractionType, printRunningSum, separator));
}}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Stack>
)}
</Formik>
</ToolOptions>
</Box>
);
}

View File

@@ -16,6 +16,7 @@ import Info from './Info';
import Separator from '../../../tools/Separator';
import AllTools from '../../../components/allTools/AllTools';
import Examples from '../../../components/examples/Examples';
import ColorSelector from '../../../components/options/ColorSelector';
const initialValues = {
joinCharacter: '',
@@ -115,23 +116,11 @@ s
export default function JoinText() {
const [input, setInput] = useState<string>('');
const { showSnackBar } = useContext(CustomSnackBarContext);
const [result, setResult] = useState<string>('');
const FormikListenerComponent = ({ input }: { input: string }) => {
const { values } = useFormikContext<typeof initialValues>();
const { joinCharacter, deleteBlank, deleteTrailing } = values;
useEffect(() => {
try {
const compute = (optionsValues: typeof initialValues, input: any) => {
const { joinCharacter, deleteBlank, deleteTrailing } = optionsValues;
setResult(mergeText(input, deleteBlank, deleteTrailing, joinCharacter));
} catch (exception: unknown) {
if (exception instanceof Error)
showSnackBar(exception.message, 'error');
}
}, [values, input]);
return null;
};
function changeInputResult(input: string, result: string) {
@@ -156,17 +145,9 @@ export default function JoinText() {
}
result={<ToolTextResult title={'Joined Text'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent input={input} />
<ToolOptionGroups
groups={[
<ToolOptions
compute={compute}
getGroups={({ values, setFieldValue }) => [
{
title: 'Text Merged Options',
component: (
@@ -187,19 +168,16 @@ export default function JoinText() {
key={option.accessor}
title={option.title}
checked={!!values[option.accessor]}
onChange={(value) =>
setFieldValue(option.accessor, value)
}
onChange={(value) => setFieldValue(option.accessor, value)}
description={option.description}
/>
))
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Stack>
)}
</Formik>
</ToolOptions>
<Info
title="What Is a Text Joiner?"
description="With this tool you can join parts of the text together. It takes a list of text values, separated by newlines, and merges them together. You can set the character that will be placed between the parts of the combined text. Also, you can ignore all empty lines and remove spaces and tabs at the end of all lines. Textabulous!"

View File

@@ -12,6 +12,7 @@ import RadioWithTextField from '../../../components/options/RadioWithTextField';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
@@ -82,13 +83,7 @@ export default function SplitText() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
const { showSnackBar } = useContext(CustomSnackBarContext);
const FormikListenerComponent = () => {
const { values } = useFormikContext<typeof initialValues>();
useEffect(() => {
try {
const computeExternal = (optionsValues: typeof initialValues, input: any) => {
const {
splitSeparatorType,
outputSeparator,
@@ -98,7 +93,7 @@ export default function SplitText() {
symbolValue,
regexValue,
lengthValue
} = values;
} = optionsValues;
setResult(
compute(
@@ -113,13 +108,6 @@ export default function SplitText() {
outputSeparator
)
);
} catch (exception: unknown) {
if (exception instanceof Error)
showSnackBar(exception.message, 'error');
}
}, [values, input]);
return null; // This component doesn't render anything
};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
@@ -131,21 +119,12 @@ export default function SplitText() {
input={<ToolTextInput value={input} onChange={setInput} />}
result={<ToolTextResult title={'Text pieces'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent />
<ToolOptionGroups
groups={[
<ToolOptions
compute={computeExternal}
getGroups={({ values, setFieldValue }) => [
{
title: 'Split separator options',
component: splitOperators.map(
({ title, description, type }) => (
component: splitOperators.map(({ title, description, type }) => (
<RadioWithTextField
key={type}
radioValue={type}
@@ -156,12 +135,9 @@ export default function SplitText() {
onRadioChange={(type) =>
setFieldValue('splitSeparatorType', type)
}
onTextChange={(val) =>
setFieldValue(`${type}Value`, val)
}
onTextChange={(val) => setFieldValue(`${type}Value`, val)}
/>
)
)
))
},
{
title: 'Output separator options',
@@ -169,19 +145,16 @@ export default function SplitText() {
<TextFieldWithDesc
key={option.accessor}
value={values[option.accessor]}
onChange={(value) =>
setFieldValue(option.accessor, value)
}
onChange={(value) => setFieldValue(option.accessor, value)}
description={option.description}
/>
))
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Stack>
)}
</Formik>
</ToolOptions>
</Box>
);
}

View File

@@ -1,15 +1,11 @@
import { Box, Stack } from '@mui/material';
import Grid from '@mui/material/Grid';
import React, { useContext, useEffect, useState } from 'react';
import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolTextInput from '../../../components/input/ToolTextInput';
import ToolTextResult from '../../../components/result/ToolTextResult';
import { Formik, useFormikContext } from 'formik';
import * as Yup from 'yup';
import ToolOptions from '../../../components/options/ToolOptions';
import { compute } from './service';
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
const initialValues = {
@@ -21,23 +17,9 @@ export default function ToMorse() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
const { showSnackBar } = useContext(CustomSnackBarContext);
const FormikListenerComponent = () => {
const { values } = useFormikContext<typeof initialValues>();
useEffect(() => {
try {
const { dotSymbol, dashSymbol } = values;
const computeOptions = (optionsValues: typeof initialValues, input: any) => {
const { dotSymbol, dashSymbol } = optionsValues;
setResult(compute(input, dotSymbol, dashSymbol));
} catch (exception: unknown) {
if (exception instanceof Error)
showSnackBar(exception.message, 'error');
}
}, [values, input]);
return null; // This component doesn't render anything
};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
@@ -49,17 +31,9 @@ export default function ToMorse() {
input={<ToolTextInput value={input} onChange={setInput} />}
result={<ToolTextResult title={'Morse code'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent />
<ToolOptionGroups
groups={[
<ToolOptions
compute={computeOptions}
getGroups={({ values, setFieldValue }) => [
{
title: 'Short Signal',
component: (
@@ -85,11 +59,10 @@ export default function ToMorse() {
)
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Stack>
)}
</Formik>
</ToolOptions>
</Box>
);
}