feat: string to morse

This commit is contained in:
Ibrahima G. Coulibaly
2024-06-25 02:07:57 +01:00
parent d1450f704f
commit bea0332020
13 changed files with 354 additions and 101 deletions

110
.idea/workspace.xml generated
View File

@@ -4,11 +4,20 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: ResultFooter"> <list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: change color in png finished">
<change afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/meta.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/service.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/to-morse.service.test.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/Navbar/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Navbar/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" 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$/scripts/create-tool.mjs" beforeDir="false" afterPath="$PROJECT_DIR$/scripts/create-tool.mjs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/home/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/index.ts" 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" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -37,37 +46,38 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;, "ASKED_ADD_EXTERNAL_FILES": "true",
&quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;, "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;, "RunOnceActivity.OpenProjectViewOnStart": "true",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "RunOnceActivity.ShowReadmeOnStart": "true",
&quot;Vitest.mergeText.executor&quot;: &quot;Run&quot;, "Vitest.compute function.executor": "Run",
&quot;Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor&quot;: &quot;Run&quot;, "Vitest.mergeText.executor": "Run",
&quot;Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor&quot;: &quot;Run&quot;, "Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run",
&quot;git-widget-placeholder&quot;: &quot;main&quot;, "Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor": "Run",
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;, "git-widget-placeholder": "main",
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;, "ignore.virus.scanning.warn.message": "true",
&quot;last_opened_file_path&quot;: &quot;C:/Users/HP/IdeaProjects/omni-tools/src/assets&quot;, "kotlin-language-version-configured": "true",
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, "last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/assets",
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;, "node.js.detected.package.eslint": "true",
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, "node.js.detected.package.tslint": "true",
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;, "node.js.selected.package.eslint": "(autodetect)",
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, "node.js.selected.package.tslint": "(autodetect)",
&quot;npm.dev.executor&quot;: &quot;Run&quot;, "nodejs_package_manager_path": "npm",
&quot;npm.prebuild.executor&quot;: &quot;Run&quot;, "npm.dev.executor": "Run",
&quot;npm.script:create:tool.executor&quot;: &quot;Run&quot;, "npm.prebuild.executor": "Run",
&quot;npm.test.executor&quot;: &quot;Run&quot;, "npm.script:create:tool.executor": "Run",
&quot;prettierjs.PrettierConfiguration.Package&quot;: &quot;C:\\Users\\HP\\IdeaProjects\\omni-tools\\node_modules\\prettier&quot;, "npm.test.executor": "Run",
&quot;project.structure.last.edited&quot;: &quot;Problems&quot;, "prettierjs.PrettierConfiguration.Package": "C:\\Users\\HP\\IdeaProjects\\omni-tools\\node_modules\\prettier",
&quot;project.structure.proportion&quot;: &quot;0.0&quot;, "project.structure.last.edited": "Problems",
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;, "project.structure.proportion": "0.0",
&quot;settings.editor.selected.configurable&quot;: &quot;settings.typescriptcompiler&quot;, "project.structure.side.proportion": "0.2",
&quot;ts.external.directory.path&quot;: &quot;C:\\Users\\HP\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib&quot;, "settings.editor.selected.configurable": "settings.typescriptcompiler",
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; "ts.external.directory.path": "C:\\Users\\HP\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib",
"vue.rearranger.settings.migration": "true"
} }
}</component> }]]></component>
<component name="ReactDesignerToolWindowState"> <component name="ReactDesignerToolWindowState">
<option name="myId2Visible"> <option name="myId2Visible">
<map> <map>
@@ -91,7 +101,20 @@
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\tools" /> <recent name="C:\Users\HP\IdeaProjects\omni-tools\src\tools" />
</key> </key>
</component> </component>
<component name="RunManager" selected="npm.dev"> <component name="RunManager" selected="Vitest.compute function">
<configuration name="compute function" type="JavaScriptTestRunnerVitest" temporary="true" nameIsGenerated="true">
<node-interpreter value="project" />
<vitest-package value="$PROJECT_DIR$/node_modules/vitest" />
<working-dir value="$PROJECT_DIR$" />
<vitest-options value="--run" />
<envs />
<scope-kind value="SUITE" />
<test-file value="$PROJECT_DIR$/src/pages/string/to-morse/to-morse.service.test.ts" />
<test-names>
<test-name value="compute function" />
</test-names>
<method v="2" />
</configuration>
<configuration name="dev" type="js.build_tools.npm" temporary="true" nameIsGenerated="true"> <configuration name="dev" type="js.build_tools.npm" temporary="true" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" /> <package-json value="$PROJECT_DIR$/package.json" />
<command value="run" /> <command value="run" />
@@ -134,6 +157,7 @@
</configuration> </configuration>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Vitest.compute function" />
<item itemvalue="npm.dev" /> <item itemvalue="npm.dev" />
<item itemvalue="npm.test" /> <item itemvalue="npm.test" />
<item itemvalue="npm.script:create:tool" /> <item itemvalue="npm.script:create:tool" />
@@ -163,7 +187,7 @@
<workItem from="1719166718305" duration="1783000" /> <workItem from="1719166718305" duration="1783000" />
<workItem from="1719168519203" duration="17675000" /> <workItem from="1719168519203" duration="17675000" />
<workItem from="1719197816332" duration="1453000" /> <workItem from="1719197816332" duration="1453000" />
<workItem from="1719273044735" duration="2109000" /> <workItem from="1719273044735" duration="4589000" />
</task> </task>
<task id="LOCAL-00001" summary="feat: use vite and ts"> <task id="LOCAL-00001" summary="feat: use vite and ts">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -549,7 +573,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1719274243788</updated> <updated>1719274243788</updated>
</task> </task>
<option name="localTasksCounter" value="49" /> <task id="LOCAL-00049" summary="feat: change color in png finished">
<option name="closed" value="true" />
<created>1719275214988</created>
<option name="number" value="00049" />
<option name="presentableId" value="LOCAL-00049" />
<option name="project" value="LOCAL" />
<updated>1719275214988</updated>
</task>
<option name="localTasksCounter" value="50" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -570,7 +602,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" /> <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" /> <option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" /> <option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="test: init" />
<MESSAGE value="chore: remove prebuild" /> <MESSAGE value="chore: remove prebuild" />
<MESSAGE value="chore: idea config" /> <MESSAGE value="chore: idea config" />
<MESSAGE value="feat: react helmet" /> <MESSAGE value="feat: react helmet" />
@@ -595,7 +626,8 @@
<MESSAGE value="feat: change colors in png init" /> <MESSAGE value="feat: change colors in png init" />
<MESSAGE value="feat: change colors in png" /> <MESSAGE value="feat: change colors in png" />
<MESSAGE value="chore: ResultFooter" /> <MESSAGE value="chore: ResultFooter" />
<option name="LAST_COMMIT_MESSAGE" value="chore: ResultFooter" /> <MESSAGE value="feat: change color in png finished" />
<option name="LAST_COMMIT_MESSAGE" value="feat: change color in png finished" />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

12
package-lock.json generated
View File

@@ -13,9 +13,11 @@
"@mui/icons-material": "^5.15.20", "@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20", "@mui/material": "^5.15.20",
"@types/lodash": "^4.17.5", "@types/lodash": "^4.17.5",
"@types/morsee": "^1.0.2",
"color": "^4.2.3", "color": "^4.2.3",
"formik": "^2.4.6", "formik": "^2.4.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"morsee": "^1.0.9",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@@ -2476,6 +2478,11 @@
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz",
"integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==" "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw=="
}, },
"node_modules/@types/morsee": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/morsee/-/morsee-1.0.2.tgz",
"integrity": "sha512-WANv1kCyQtmGZTiov9FzFdt1X4wRtXYZA6B4YR3CghKgx4ychU7d1gkOx7oD+ddVGI+SWmWOPccco7pAc6wXeA=="
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.14.6", "version": "20.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz",
@@ -6194,6 +6201,11 @@
"ufo": "^1.5.3" "ufo": "^1.5.3"
} }
}, },
"node_modules/morsee": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/morsee/-/morsee-1.0.9.tgz",
"integrity": "sha512-8X8jKVUmZBHKpET9Ap6FPiwlAAASvv60M1K25/YwCU7veuj5MfYgaWX3oEPHtMGgC44IIkIKzyD73fduEKB/9g=="
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",

View File

@@ -29,9 +29,11 @@
"@mui/icons-material": "^5.15.20", "@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20", "@mui/material": "^5.15.20",
"@types/lodash": "^4.17.5", "@types/lodash": "^4.17.5",
"@types/morsee": "^1.0.2",
"color": "^4.2.3", "color": "^4.2.3",
"formik": "^2.4.6", "formik": "^2.4.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"morsee": "^1.0.9",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",

View File

@@ -45,7 +45,7 @@ const toolNameCamelCase = toolName.replace(/-./g, (x) => x[1].toUpperCase())
const toolNameTitleCase = const toolNameTitleCase =
toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' ') toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' ')
const toolDir = join(toolsDir, toolName) const toolDir = join(toolsDir, toolName)
const type = folder.split(sep)[folder.split(sep).length - 1]
await createFolderStructure(toolDir, folder.split(sep).length) await createFolderStructure(toolDir, folder.split(sep).length)
console.log(`Directory created: ${toolDir}`) console.log(`Directory created: ${toolDir}`)
@@ -78,7 +78,7 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react'; import { lazy } from 'react';
// import image from '@assets/text.png'; // import image from '@assets/text.png';
export const tool = defineTool('${folder.split(sep)[folder.split(sep).length - 1]}', { export const tool = defineTool('${type}', {
name: '${toolNameTitleCase}', name: '${toolNameTitleCase}',
path: '${toolName}', path: '${toolName}',
// image, // image,
@@ -132,7 +132,7 @@ const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then(
indexContent.splice( indexContent.splice(
0, 0,
0, 0,
`import { tool as ${toolNameCamelCase} } from './${toolName}/meta';` `import { tool as ${type}${capitalizeFirstLetter(toolNameCamelCase)} } from './${toolName}/meta';`
) )
writeFile(toolsIndex, indexContent.join('\n')) writeFile(toolsIndex, indexContent.join('\n'))
console.log(`Added import in: ${toolsIndex}`) console.log(`Added import in: ${toolsIndex}`)

View File

@@ -0,0 +1,20 @@
import Typography from '@mui/material/Typography';
import React, { ReactNode } from 'react';
import { Box, Stack } from '@mui/material';
export default function ToolOptionGroups({
groups
}: {
groups: { title: string; component: ReactNode }[];
}) {
return (
<Stack direction={'row'} spacing={2}>
{groups.map((group) => (
<Box key={group.title}>
<Typography fontSize={22}>{group.title}</Typography>
{group.component}
</Box>
))}
</Stack>
);
}

View File

@@ -12,7 +12,7 @@ import SearchIcon from '@mui/icons-material/Search';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { filterTools, getToolsByCategory, tools } from '../../tools'; import { filterTools, getToolsByCategory, tools } from '../../tools';
import { useState } from 'react'; import { useState } from 'react';
import { DefinedTool } from '../../tools/defineTool'; import { DefinedTool } from '@tools/defineTool';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
const exampleTools: { label: string; url: string }[] = [ const exampleTools: { label: string; url: string }[] = [
@@ -20,7 +20,7 @@ const exampleTools: { label: string; url: string }[] = [
label: 'Create a transparent image', label: 'Create a transparent image',
url: '' url: ''
}, },
{ label: 'Convert text to morse code', url: '' }, { label: 'Convert text to morse code', url: '/string/to-morse' },
{ label: 'Change GIF speed', url: '' }, { label: 'Change GIF speed', url: '' },
{ label: 'Pick a random item', url: '' }, { label: 'Pick a random item', url: '' },
{ label: 'Find and replace text', url: '' }, { label: 'Find and replace text', url: '' },

View File

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

View File

@@ -9,6 +9,7 @@ import { mergeText } from './service';
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext'; import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc'; import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc'; import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
const initialValues = { const initialValues = {
joinCharacter: '', joinCharacter: '',
@@ -90,31 +91,37 @@ export default function JoinText() {
{({ setFieldValue, values }) => ( {({ setFieldValue, values }) => (
<Stack direction={'row'} spacing={2}> <Stack direction={'row'} spacing={2}>
<FormikListenerComponent input={input} /> <FormikListenerComponent input={input} />
<Box> <ToolOptionGroups
<Typography fontSize={22}>Text Merged Options</Typography> groups={[
<TextFieldWithDesc {
placeholder={mergeOptions.placeholder} title: 'Text Merged Options',
value={values['joinCharacter']} component: (
onChange={(value) => <TextFieldWithDesc
setFieldValue(mergeOptions.accessor, value) 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) => (
<CheckboxWithDesc
key={option.accessor}
title={option.title}
checked={!!values[option.accessor]}
onChange={(value) => setFieldValue(option.accessor, value)}
description={option.description}
/>
))}
</Box>
</Stack> </Stack>
)} )}
</Formik> </Formik>

View File

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

View File

@@ -0,0 +1,98 @@
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';
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>
<Grid container spacing={2}>
<Grid item xs={6}>
<ToolTextInput value={input} onChange={setInput} />
</Grid>
<Grid item xs={6}>
<ToolTextResult title={'Morse code'} value={result} />
</Grid>
</Grid>
<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,12 @@
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: '',
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);
});
});