Merge remote-tracking branch 'origin/main' into string-join

# Conflicts:
#	src/pages/string/join/index.tsx
This commit is contained in:
Ibrahima G. Coulibaly
2024-06-25 07:55:21 +01:00
46 changed files with 1733 additions and 320 deletions

View File

@@ -1,4 +1,5 @@
import { tool as stringToMorse } from './to-morse/meta';
import { tool as stringSplit } from './split/meta';
import { tool as stringJoin } from './join/meta';
export const stringTools = [stringSplit, stringJoin];
export const stringTools = [stringSplit, stringJoin, stringToMorse];

View File

@@ -9,6 +9,8 @@ import { mergeText } from './service';
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
import Info from './Info';
import Separator from '../../../tools/Separator';
@@ -56,12 +58,12 @@ const exampleCards = [
title: 'Merge a To-Do List',
description:
"In this example, we merge a bullet point list into one sentence, separating each item by the word 'and'. We also remove all empty lines and trailing spaces. If we didn't remove the empty lines, then they'd be joined with the separator word, making the separator word appear multiple times. If we didn't remove the trailing tabs and spaces, then they'd create extra spacing in the joined text and it wouldn't look nice.",
sampleText: `clean the house
sampleText: `clean the house
go shopping
go shopping
feed the cat
make dinner
make dinner
build a rocket ship and fly away`,
sampleResult: `clean the house and go shopping and feed the cat and make dinner and build a rocket ship and fly away`,
requiredOptions: {
@@ -177,18 +179,16 @@ export default function JoinText() {
return (
<Box>
<Grid container spacing={2}>
<Grid item xs={6}>
<ToolInputAndResult
input={
<ToolTextInput
title={'Text Pieces'}
value={input}
onChange={setInput}
/>
</Grid>
<Grid item xs={6}>
<ToolTextResult title={'Joined Text'} value={result} />
</Grid>
</Grid>
}
result={<ToolTextResult title={'Joined Text'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
@@ -198,31 +198,37 @@ export default function JoinText() {
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent input={input} />
<Box>
<Typography fontSize={22}>Text Merged Options</Typography>
<TextFieldWithDesc
placeholder={mergeOptions.placeholder}
value={values['joinCharacter']}
onChange={(value) =>
setFieldValue(mergeOptions.accessor, value)
<ToolOptionGroups
groups={[
{
title: 'Text Merged Options',
component: (
<TextFieldWithDesc
placeholder={mergeOptions.placeholder}
value={values['joinCharacter']}
onChange={(value) =>
setFieldValue(mergeOptions.accessor, value)
}
description={mergeOptions.description}
/>
)
},
{
title: 'Blank Lines and Trailing Spaces',
component: blankTrailingOptions.map((option) => (
<CheckboxWithDesc
key={option.accessor}
title={option.title}
checked={!!values[option.accessor]}
onChange={(value) =>
setFieldValue(option.accessor, value)
}
description={option.description}
/>
))
}
description={mergeOptions.description}
/>
</Box>
<Box>
<Typography fontSize={22}>
Blank Lines and Trailing Spaces
</Typography>
{blankTrailingOptions.map((option, index) => (
<CheckboxWithDesc
key={index}
title={option.title}
checked={!!values[option.accessor]}
onChange={(value) => setFieldValue(option.accessor, value)}
description={option.description}
/>
))}
</Box>
]}
/>
</Stack>
)}
</Formik>

View File

@@ -5,23 +5,13 @@ export function mergeText(
joinCharacter: string = ''
): string {
let processedLines: string[] = text.split('\n');
if (deleteTrailingSpaces) {
processedLines = processedLines.map((line) => line.trimEnd());
}
if (deleteBlankLines) {
processedLines = processedLines.filter((line) => line.trim() !== '');
} else {
processedLines = processedLines.map((line) =>
line.trim() === '' ? line + '\r\n\n' : line
);
processedLines = processedLines.filter((line) => line.trim());
}
return processedLines.join(joinCharacter);
}
// Example text to use
`This is a line with trailing spaces
Another line with trailing spaces
Final line without trailing spaces`;

View File

@@ -1,16 +1,17 @@
import { Box, Stack, TextField } from '@mui/material';
import { Box, Stack } from '@mui/material';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import React, { useContext, useEffect, useRef, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import ToolTextInput from '../../../components/input/ToolTextInput';
import ToolTextResult from '../../../components/result/ToolTextResult';
import { Field, Formik, FormikProps, useFormikContext } from 'formik';
import { Formik, useFormikContext } from 'formik';
import * as Yup from 'yup';
import ToolOptions from '../../../components/options/ToolOptions';
import { compute, SplitOperatorType } from './service';
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
import RadioWithTextField from '../../../components/options/RadioWithTextField';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
@@ -126,14 +127,10 @@ export default function SplitText() {
return (
<Box>
<Grid container spacing={2}>
<Grid item xs={6}>
<ToolTextInput value={input} onChange={setInput} />
</Grid>
<Grid item xs={6}>
<ToolTextResult title={'Text pieces'} value={result} />
</Grid>
</Grid>
<ToolInputAndResult
input={<ToolTextInput value={input} onChange={setInput} />}
result={<ToolTextResult title={'Text pieces'} value={result} />}
/>
<ToolOptions>
<Formik
initialValues={initialValues}
@@ -143,34 +140,44 @@ export default function SplitText() {
{({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}>
<FormikListenerComponent />
<Box>
<Typography fontSize={22}>Split separator options</Typography>
{splitOperators.map(({ title, description, type }) => (
<RadioWithTextField
key={type}
type={type}
title={title}
fieldName={'splitSeparatorType'}
description={description}
value={values[`${type}Value`]}
onTypeChange={(type) =>
setFieldValue('splitSeparatorType', type)
}
onTextChange={(val) => setFieldValue(`${type}Value`, val)}
/>
))}
</Box>
<Box>
<Typography fontSize={22}>Output separator options</Typography>
{outputOptions.map((option) => (
<TextFieldWithDesc
key={option.accessor}
value={values[option.accessor]}
onChange={(value) => setFieldValue(option.accessor, value)}
description={option.description}
/>
))}
</Box>
<ToolOptionGroups
groups={[
{
title: 'Split separator options',
component: splitOperators.map(
({ title, description, type }) => (
<RadioWithTextField
key={type}
radioValue={type}
title={title}
fieldName={'splitSeparatorType'}
description={description}
value={values[`${type}Value`]}
onRadioChange={(type) =>
setFieldValue('splitSeparatorType', type)
}
onTextChange={(val) =>
setFieldValue(`${type}Value`, val)
}
/>
)
)
},
{
title: 'Output separator options',
component: outputOptions.map((option) => (
<TextFieldWithDesc
key={option.accessor}
value={values[option.accessor]}
onChange={(value) =>
setFieldValue(option.accessor, value)
}
description={option.description}
/>
))
}
]}
/>
</Stack>
)}
</Formik>

View File

@@ -0,0 +1,95 @@
import { Box, Stack } from '@mui/material';
import Grid from '@mui/material/Grid';
import React, { useContext, useEffect, 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 = {
dotSymbol: '.',
dashSymbol: '-'
};
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;
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')
});
return (
<Box>
<ToolInputAndResult
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={[
{
title: 'Short Signal',
component: (
<TextFieldWithDesc
description={
'Symbol that will correspond to the dot in Morse code.'
}
value={values.dotSymbol}
onChange={(val) => setFieldValue('dotSymbol', val)}
/>
)
},
{
title: 'Long Signal',
component: (
<TextFieldWithDesc
description={
'Symbol that will correspond to the dash in Morse code.'
}
value={values.dashSymbol}
onChange={(val) => setFieldValue('dashSymbol', val)}
/>
)
}
]}
/>
</Stack>
)}
</Formik>
</ToolOptions>
</Box>
);
}

View File

@@ -0,0 +1,13 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
// import image from '@assets/text.png';
export const tool = defineTool('string', {
name: 'String To morse',
path: 'to-morse',
// image,
description:
"World's simplest browser-based utility for converting text to Morse code. Load your text in the input form on the left and you'll instantly get Morse code in the output area. Powerful, free, and fast. Load text get Morse code.",
keywords: ['to', 'morse'],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,9 @@
import { encode } from 'morsee';
export const compute = (
input: string,
dotSymbol: string,
dashSymbol: string
): string => {
return encode(input).replaceAll('.', dotSymbol).replaceAll('-', dashSymbol);
};

View File

@@ -0,0 +1,50 @@
import { describe, it, expect } from 'vitest';
import { compute } from './service';
describe('compute function', () => {
it('should replace dots and dashes with specified symbols', () => {
const input = 'test';
const dotSymbol = '*';
const dashSymbol = '#';
const result = compute(input, dotSymbol, dashSymbol);
const expected = '# * *** #';
expect(result).toBe(expected);
});
it('should return an empty string for empty input', () => {
const input = '';
const dotSymbol = '*';
const dashSymbol = '#';
const result = compute(input, dotSymbol, dashSymbol);
expect(result).toBe('');
});
// Test case 3: Special characters handling
it('should handle input with special characters', () => {
const input = 'hello, world!';
const dotSymbol = '*';
const dashSymbol = '#';
const result = compute(input, dotSymbol, dashSymbol);
const expected =
'**** * *#** *#** ### ##**## / *## ### *#* *#** #** #*#*##';
expect(result).toBe(expected);
});
it('should work with different symbols for dots and dashes', () => {
const input = 'morse';
const dotSymbol = '!';
const dashSymbol = '@';
const result = compute(input, dotSymbol, dashSymbol);
const expected = '@@ @@@ !@! !!! !';
expect(result).toBe(expected);
});
it('should handle numeric input correctly', () => {
const input = '12345';
const dotSymbol = '*';
const dashSymbol = '#';
const result = compute(input, dotSymbol, dashSymbol);
const expected = '*#### **### ***## ****# *****'; // This depends on how "12345" is encoded in morse code
expect(result).toBe(expected);
});
});