feat: missing tools

This commit is contained in:
Ibrahima G. Coulibaly
2025-03-09 17:45:13 +00:00
parent 2859b1ddc2
commit 04832bd104
11 changed files with 1210 additions and 133 deletions

135
.idea/workspace.xml generated
View File

@@ -4,15 +4,18 @@
<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="refactor: validateJson"> <list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="refactor: use ToolContent">
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" 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/options/CheckboxWithDesc.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/CheckboxWithDesc.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/list/duplicate/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/duplicate/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/pages/tools/list/duplicate/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/duplicate/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/list/find-unique/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/find-unique/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/list/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/index.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/pages/tools/list/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/list/shuffle/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/shuffle/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/pages/tools/list/truncate/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/truncate/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/join/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/join/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/pages/tools/list/unwrap/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/unwrap/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/to-morse/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/to-morse/index.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/pages/tools/list/wrap/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/wrap/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/quote/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/quote/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/rot13/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/rot13/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/rotate/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/rotate/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" />
@@ -111,53 +114,53 @@
<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"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"ASKED_ADD_EXTERNAL_FILES": "true", &quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;,
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true", &quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
"Docker.Dockerfile build.executor": "Run", &quot;Docker.Dockerfile build.executor&quot;: &quot;Run&quot;,
"Docker.Dockerfile.executor": "Run", &quot;Docker.Dockerfile.executor&quot;: &quot;Run&quot;,
"Playwright.JoinText Component.executor": "Run", &quot;Playwright.JoinText Component.executor&quot;: &quot;Run&quot;,
"Playwright.JoinText Component.should merge text pieces with specified join character.executor": "Run", &quot;Playwright.JoinText Component.should merge text pieces with specified join character.executor&quot;: &quot;Run&quot;,
"RunOnceActivity.OpenProjectViewOnStart": "true", &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.git.unshallow": "true", &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
"Vitest.compute function (1).executor": "Run", &quot;Vitest.compute function (1).executor&quot;: &quot;Run&quot;,
"Vitest.compute function.executor": "Run", &quot;Vitest.compute function.executor&quot;: &quot;Run&quot;,
"Vitest.mergeText.executor": "Run", &quot;Vitest.mergeText.executor&quot;: &quot;Run&quot;,
"Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run", &quot;Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor&quot;: &quot;Run&quot;,
"Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.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.removeDuplicateLines function.executor": "Run", &quot;Vitest.removeDuplicateLines function.executor&quot;: &quot;Run&quot;,
"Vitest.removeDuplicateLines function.newlines option.executor": "Run", &quot;Vitest.removeDuplicateLines function.newlines option.executor&quot;: &quot;Run&quot;,
"Vitest.removeDuplicateLines function.newlines option.should filter newlines when newlines is set to filter.executor": "Run", &quot;Vitest.removeDuplicateLines function.newlines option.should filter newlines when newlines is set to filter.executor&quot;: &quot;Run&quot;,
"Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor": "Run", &quot;Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor&quot;: &quot;Run&quot;,
"Vitest.replaceText function.executor": "Run", &quot;Vitest.replaceText function.executor&quot;: &quot;Run&quot;,
"git-widget-placeholder": "main", &quot;git-widget-placeholder&quot;: &quot;main&quot;,
"ignore.virus.scanning.warn.message": "true", &quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
"kotlin-language-version-configured": "true", &quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src/pages/tools/list/duplicate/index.tsx", &quot;last_opened_file_path&quot;: &quot;C:/Users/Ibrahima/IdeaProjects/omni-tools/src/pages/tools/list/duplicate/index.tsx&quot;,
"node.js.detected.package.eslint": "true", &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
"node.js.detected.package.tslint": "true", &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
"node.js.selected.package.eslint": "(autodetect)", &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
"node.js.selected.package.tslint": "(autodetect)", &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
"nodejs_package_manager_path": "npm", &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
"npm.build.executor": "Run", &quot;npm.build.executor&quot;: &quot;Run&quot;,
"npm.dev.executor": "Run", &quot;npm.dev.executor&quot;: &quot;Run&quot;,
"npm.lint.executor": "Run", &quot;npm.lint.executor&quot;: &quot;Run&quot;,
"npm.prebuild.executor": "Run", &quot;npm.prebuild.executor&quot;: &quot;Run&quot;,
"npm.script:create:tool.executor": "Run", &quot;npm.script:create:tool.executor&quot;: &quot;Run&quot;,
"npm.test.executor": "Run", &quot;npm.test.executor&quot;: &quot;Run&quot;,
"npm.test:e2e.executor": "Run", &quot;npm.test:e2e.executor&quot;: &quot;Run&quot;,
"npm.test:e2e:run.executor": "Run", &quot;npm.test:e2e:run.executor&quot;: &quot;Run&quot;,
"prettierjs.PrettierConfiguration.Package": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier", &quot;prettierjs.PrettierConfiguration.Package&quot;: &quot;C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier&quot;,
"project.structure.last.edited": "Problems", &quot;project.structure.last.edited&quot;: &quot;Problems&quot;,
"project.structure.proportion": "0.0", &quot;project.structure.proportion&quot;: &quot;0.0&quot;,
"project.structure.side.proportion": "0.2", &quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
"settings.editor.selected.configurable": "refactai_advanced_settings", &quot;settings.editor.selected.configurable&quot;: &quot;refactai_advanced_settings&quot;,
"ts.external.directory.path": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib", &quot;ts.external.directory.path&quot;: &quot;C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib&quot;,
"vue.rearranger.settings.migration": "true" &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
} }
}]]></component> }</component>
<component name="ReactDesignerToolWindowState"> <component name="ReactDesignerToolWindowState">
<option name="myId2Visible"> <option name="myId2Visible">
<map> <map>
@@ -183,7 +186,7 @@
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" /> <recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" />
</key> </key>
</component> </component>
<component name="RunManager" selected="Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp"> <component name="RunManager" selected="npm.dev">
<configuration name="replaceText function (regexp mode).should return the original text when passed an invalid regexp" type="JavaScriptTestRunnerVitest" temporary="true" nameIsGenerated="true"> <configuration name="replaceText function (regexp mode).should return the original text when passed an invalid regexp" type="JavaScriptTestRunnerVitest" temporary="true" nameIsGenerated="true">
<node-interpreter value="project" /> <node-interpreter value="project" />
<vitest-package value="$PROJECT_DIR$/node_modules/vitest" /> <vitest-package value="$PROJECT_DIR$/node_modules/vitest" />
@@ -256,10 +259,10 @@
</list> </list>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="npm.dev" />
<item itemvalue="Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp" /> <item itemvalue="Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp" />
<item itemvalue="npm.test" /> <item itemvalue="npm.test" />
<item itemvalue="npm.test:e2e" /> <item itemvalue="npm.test:e2e" />
<item itemvalue="npm.dev" />
<item itemvalue="Vitest.replaceText function" /> <item itemvalue="Vitest.replaceText function" />
</list> </list>
</recent_temporary> </recent_temporary>
@@ -340,15 +343,7 @@
<workItem from="1741475969294" duration="4215000" /> <workItem from="1741475969294" duration="4215000" />
<workItem from="1741494053121" duration="178000" /> <workItem from="1741494053121" duration="178000" />
<workItem from="1741537936314" duration="1294000" /> <workItem from="1741537936314" duration="1294000" />
<workItem from="1741539602311" duration="1240000" /> <workItem from="1741539602311" duration="2441000" />
</task>
<task id="LOCAL-00106" summary="fix: compress png">
<option name="closed" value="true" />
<created>1740322444616</created>
<option name="number" value="00106" />
<option name="presentableId" value="LOCAL-00106" />
<option name="project" value="LOCAL" />
<updated>1740322444616</updated>
</task> </task>
<task id="LOCAL-00107" summary="fix: docs"> <task id="LOCAL-00107" summary="fix: docs">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -734,7 +729,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1741535390090</updated> <updated>1741535390090</updated>
</task> </task>
<option name="localTasksCounter" value="155" /> <task id="LOCAL-00155" summary="refactor: use ToolContent">
<option name="closed" value="true" />
<created>1741540939154</created>
<option name="number" value="00155" />
<option name="presentableId" value="LOCAL-00155" />
<option name="project" value="LOCAL" />
<updated>1741540939154</updated>
</task>
<option name="localTasksCounter" value="156" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -781,7 +784,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="chore: show tooloptions in example" />
<MESSAGE value="refact: examples" /> <MESSAGE value="refact: examples" />
<MESSAGE value="fix: examples" /> <MESSAGE value="fix: examples" />
<MESSAGE value="feat: json pretty" /> <MESSAGE value="feat: json pretty" />
@@ -806,7 +808,8 @@
<MESSAGE value="feat: crop png" /> <MESSAGE value="feat: crop png" />
<MESSAGE value="chore: remove unnecessary files" /> <MESSAGE value="chore: remove unnecessary files" />
<MESSAGE value="refactor: validateJson" /> <MESSAGE value="refactor: validateJson" />
<option name="LAST_COMMIT_MESSAGE" value="refactor: validateJson" /> <MESSAGE value="refactor: use ToolContent" />
<option name="LAST_COMMIT_MESSAGE" value="refactor: use ToolContent" />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

View File

@@ -9,7 +9,7 @@ const CheckboxWithDesc = ({
disabled disabled
}: { }: {
title: string; title: string;
description: string; description?: string;
checked: boolean; checked: boolean;
onChange: (value: boolean) => void; onChange: (value: boolean) => void;
disabled?: boolean; disabled?: boolean;
@@ -30,9 +30,11 @@ const CheckboxWithDesc = ({
} }
label={title} label={title}
/> />
<Typography fontSize={12} mt={1}> {description && (
{description} <Typography fontSize={12} mt={1}>
</Typography> {description}
</Typography>
)}
</Box> </Box>
); );
}; };

View File

@@ -1,12 +1,215 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react';
import * as Yup from 'yup';
import ToolContent from '@components/ToolContent'; import ToolContent from '@components/ToolContent';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { duplicateList, SplitOperatorType } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import * as Yup from 'yup';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ splitOperatorType: SplitOperatorType;
// splitSeparator: Yup.string().required('The separator is required') splitSeparator: string;
}); joinSeparator: string;
export default function Duplicate() { concatenate: boolean;
return <Box>Lorem ipsum</Box>; reverse: boolean;
copy: string;
}
const initialValues: InitialValuesType = {
splitOperatorType: 'symbol',
splitSeparator: ' ',
joinSeparator: ' ',
concatenate: true,
reverse: false,
copy: '2'
};
const validationSchema = Yup.object({
splitSeparator: Yup.string().required('The separator is required'),
joinSeparator: Yup.string().required('The join separator is required'),
copy: Yup.number()
.typeError('Number of copies must be a number')
.min(0.1, 'Number of copies must be positive')
.required('Number of copies is required')
});
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Simple duplication',
description: 'This example shows how to duplicate a list of words.',
sampleText: 'Hello World',
sampleResult: 'Hello World Hello World',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' ',
joinSeparator: ' ',
concatenate: true,
reverse: false,
copy: '2'
}
},
{
title: 'Reverse duplication',
description: 'This example shows how to duplicate a list in reverse order.',
sampleText: 'Hello World',
sampleResult: 'Hello World World Hello',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' ',
joinSeparator: ' ',
concatenate: true,
reverse: true,
copy: '2'
}
},
{
title: 'Interweaving items',
description:
'This example shows how to interweave items instead of concatenating them.',
sampleText: 'Hello World',
sampleResult: 'Hello Hello World World',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' ',
joinSeparator: ' ',
concatenate: false,
reverse: false,
copy: '2'
}
},
{
title: 'Fractional duplication',
description:
'This example shows how to duplicate a list with a fractional number of copies.',
sampleText: 'apple banana cherry',
sampleResult: 'apple banana cherry apple banana',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' ',
joinSeparator: ' ',
concatenate: true,
reverse: false,
copy: '1.7'
}
}
];
export default function Duplicate({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
try {
const copy = parseFloat(optionsValues.copy);
setResult(
duplicateList(
optionsValues.splitOperatorType,
optionsValues.splitSeparator,
optionsValues.joinSeparator,
input,
optionsValues.concatenate,
optionsValues.reverse,
copy
)
);
} catch (error) {
if (error instanceof Error) {
setResult(`Error: ${error.message}`);
} else {
setResult('An unknown error occurred');
}
}
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Split Options',
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the duplicated list'}
/>
</Box>
)
},
{
title: 'Duplication Options',
component: (
<Box>
<TextFieldWithDesc
value={values.copy}
onOwnChange={(val) => updateField('copy', val)}
description={'Number of copies (can be fractional)'}
type="number"
/>
<CheckboxWithDesc
title={'Concatenate'}
checked={values.concatenate}
onChange={(checked) => updateField('concatenate', checked)}
description={
'Concatenate copies (if unchecked, items will be interweaved)'
}
/>
<CheckboxWithDesc
title={'Reverse'}
checked={values.reverse}
onChange={(checked) => updateField('reverse', checked)}
description={'Reverse the duplicated items'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
}
resultComponent={
<ToolTextResult title="Duplicated List" value={result} />
}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Duplication',
description:
"This tool allows you to duplicate items in a list. You can specify the number of copies (including fractional values), control whether items are concatenated or interweaved, and even reverse the duplicated items. It's useful for creating repeated patterns, generating test data, or expanding lists with predictable content."
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -12,14 +12,14 @@ import { tool as listSort } from './sort/meta';
export const listTools = [ export const listTools = [
listSort, listSort,
// listUnwrap, listUnwrap,
listReverse, listReverse,
listFindUnique, listFindUnique,
listFindMostPopular, listFindMostPopular,
listGroup, listGroup,
// listWrap, listWrap,
listRotate, listRotate,
listShuffle listShuffle,
// listTruncate, listTruncate,
// listDuplicate listDuplicate
]; ];

View File

@@ -1,11 +1,189 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import ToolContent from '@components/ToolContent';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { SplitOperatorType, truncateList } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import * as Yup from 'yup'; import * as Yup from 'yup';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ splitOperatorType: SplitOperatorType;
// splitSeparator: Yup.string().required('The separator is required') splitSeparator: string;
}); joinSeparator: string;
export default function Truncate() { end: boolean;
return <Box>Lorem ipsum</Box>; length: string;
}
const initialValues: InitialValuesType = {
splitOperatorType: 'symbol',
splitSeparator: ',',
joinSeparator: ',',
end: true,
length: '3'
};
const validationSchema = Yup.object({
splitSeparator: Yup.string().required('The separator is required'),
joinSeparator: Yup.string().required('The join separator is required'),
length: Yup.number()
.typeError('Length must be a number')
.min(0, 'Length must be a positive number')
.required('Length is required')
});
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Keep first 3 items in a list',
description:
'This example shows how to keep only the first 3 items in a comma-separated list.',
sampleText: 'apple, pineapple, lemon, orange, mango',
sampleResult: 'apple,pineapple,lemon',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ', ',
joinSeparator: ',',
end: true,
length: '3'
}
},
{
title: 'Keep last 2 items in a list',
description:
'This example shows how to keep only the last 2 items in a comma-separated list.',
sampleText: 'apple, pineapple, lemon, orange, mango',
sampleResult: 'orange,mango',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ', ',
joinSeparator: ',',
end: false,
length: '2'
}
},
{
title: 'Truncate a list with custom separators',
description:
'This example shows how to truncate a list with custom separators.',
sampleText: 'apple | pineapple | lemon | orange | mango',
sampleResult: 'apple - pineapple - lemon',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' | ',
joinSeparator: ' - ',
end: true,
length: '3'
}
}
];
export default function Truncate({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
try {
const length = parseInt(optionsValues.length, 10);
setResult(
truncateList(
optionsValues.splitOperatorType,
input,
optionsValues.splitSeparator,
optionsValues.joinSeparator,
optionsValues.end,
length
)
);
} catch (error) {
if (error instanceof Error) {
setResult(`Error: ${error.message}`);
} else {
setResult('An unknown error occurred');
}
}
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Split Options',
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the truncated list'}
/>
</Box>
)
},
{
title: 'Truncation Options',
component: (
<Box>
<TextFieldWithDesc
value={values.length}
onOwnChange={(val) => updateField('length', val)}
description={'Number of items to keep'}
type="number"
/>
<SimpleRadio
onClick={() => updateField('end', true)}
checked={values.end}
title={'Keep items from the beginning'}
/>
<SimpleRadio
onClick={() => updateField('end', false)}
checked={!values.end}
title={'Keep items from the end'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="Truncated List" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Truncation',
description:
"This tool allows you to truncate a list to a specific number of items. You can choose to keep items from the beginning or the end of the list, and specify custom separators for splitting and joining. It's useful for limiting the size of lists, creating previews, or extracting specific portions of data."
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -1,11 +1,212 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import ToolContent from '@components/ToolContent';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { SplitOperatorType, unwrapList } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import * as Yup from 'yup'; import * as Yup from 'yup';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ splitOperatorType: SplitOperatorType;
// splitSeparator: Yup.string().required('The separator is required') splitSeparator: string;
}); joinSeparator: string;
export default function Unwrap() { deleteEmptyItems: boolean;
return <Box>Lorem ipsum</Box>; multiLevel: boolean;
trimItems: boolean;
left: string;
right: string;
}
const initialValues: InitialValuesType = {
splitOperatorType: 'symbol',
splitSeparator: '\n',
joinSeparator: '\n',
deleteEmptyItems: true,
multiLevel: true,
trimItems: true,
left: '',
right: ''
};
const validationSchema = Yup.object({
splitSeparator: Yup.string().required('The separator is required'),
joinSeparator: Yup.string().required('The join separator is required')
});
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Unwrap quotes from list items',
description:
'This example shows how to remove quotes from each item in a list.',
sampleText: '"apple"\n"banana"\n"orange"',
sampleResult: 'apple\nbanana\norange',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: '\n',
joinSeparator: '\n',
deleteEmptyItems: true,
multiLevel: true,
trimItems: true,
left: '"',
right: '"'
}
},
{
title: 'Unwrap multiple levels of characters',
description:
'This example shows how to remove multiple levels of the same character from each item.',
sampleText: '###Hello###\n##World##\n#Test#',
sampleResult: 'Hello\nWorld\nTest',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: '\n',
joinSeparator: '\n',
deleteEmptyItems: true,
multiLevel: true,
trimItems: true,
left: '#',
right: '#'
}
},
{
title: 'Unwrap and join with custom separator',
description:
'This example shows how to unwrap items and join them with a custom separator.',
sampleText: '[item1]\n[item2]\n[item3]',
sampleResult: 'item1, item2, item3',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: '\n',
joinSeparator: ', ',
deleteEmptyItems: true,
multiLevel: false,
trimItems: true,
left: '[',
right: ']'
}
}
];
export default function Unwrap({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
try {
setResult(
unwrapList(
optionsValues.splitOperatorType,
input,
optionsValues.splitSeparator,
optionsValues.joinSeparator,
optionsValues.deleteEmptyItems,
optionsValues.multiLevel,
optionsValues.trimItems,
optionsValues.left,
optionsValues.right
)
);
} catch (error) {
if (error instanceof Error) {
setResult(`Error: ${error.message}`);
} else {
setResult('An unknown error occurred');
}
}
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Split Options',
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the unwrapped list'}
/>
</Box>
)
},
{
title: 'Unwrap Options',
component: (
<Box>
<TextFieldWithDesc
value={values.left}
onOwnChange={(val) => updateField('left', val)}
description={'Characters to remove from the left side'}
/>
<TextFieldWithDesc
value={values.right}
onOwnChange={(val) => updateField('right', val)}
description={'Characters to remove from the right side'}
/>
<CheckboxWithDesc
checked={values.multiLevel}
onChange={(checked) => updateField('multiLevel', checked)}
title={'Remove multiple levels of wrapping'}
/>
<CheckboxWithDesc
checked={values.trimItems}
onChange={(checked) => updateField('trimItems', checked)}
title={'Trim whitespace from items'}
/>
<CheckboxWithDesc
checked={values.deleteEmptyItems}
onChange={(checked) => updateField('deleteEmptyItems', checked)}
title={'Remove empty items'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="Unwrapped List" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Unwrapping',
description:
"This tool allows you to remove wrapping characters from each item in a list. You can specify characters to remove from the left and right sides, handle multiple levels of wrapping, and control how the list is processed. It's useful for cleaning up data, removing quotes or brackets, and formatting lists."
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -1,11 +1,191 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import ToolContent from '@components/ToolContent';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { SplitOperatorType, wrapList } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import * as Yup from 'yup'; import * as Yup from 'yup';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ splitOperatorType: SplitOperatorType;
// splitSeparator: Yup.string().required('The separator is required') splitSeparator: string;
}); joinSeparator: string;
export default function Wrap() { deleteEmptyItems: boolean;
return <Box>Lorem ipsum</Box>; left: string;
right: string;
}
const initialValues: InitialValuesType = {
splitOperatorType: 'symbol',
splitSeparator: ',',
joinSeparator: ',',
deleteEmptyItems: true,
left: '"',
right: '"'
};
const validationSchema = Yup.object({
splitSeparator: Yup.string().required('The separator is required'),
joinSeparator: Yup.string().required('The join separator is required')
});
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Wrap list items with quotes',
description:
'This example shows how to wrap each item in a list with quotes.',
sampleText: 'apple,banana,orange',
sampleResult: '"apple","banana","orange"',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ',',
joinSeparator: ',',
deleteEmptyItems: true,
left: '"',
right: '"'
}
},
{
title: 'Wrap list items with brackets',
description:
'This example shows how to wrap each item in a list with brackets.',
sampleText: 'item1,item2,item3',
sampleResult: '[item1],[item2],[item3]',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ',',
joinSeparator: ',',
deleteEmptyItems: true,
left: '[',
right: ']'
}
},
{
title: 'Wrap list items with custom text',
description:
'This example shows how to wrap each item with different text on each side.',
sampleText: 'apple,banana,orange',
sampleResult:
'prefix-apple-suffix,prefix-banana-suffix,prefix-orange-suffix',
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ',',
joinSeparator: ',',
deleteEmptyItems: true,
left: 'prefix-',
right: '-suffix'
}
}
];
export default function Wrap({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
try {
setResult(
wrapList(
optionsValues.splitOperatorType,
input,
optionsValues.splitSeparator,
optionsValues.joinSeparator,
optionsValues.deleteEmptyItems,
optionsValues.left,
optionsValues.right
)
);
} catch (error) {
if (error instanceof Error) {
setResult(`Error: ${error.message}`);
} else {
setResult('An unknown error occurred');
}
}
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Split Options',
component: (
<Box>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'symbol')}
checked={values.splitOperatorType === 'symbol'}
title={'Split by Symbol'}
/>
<SimpleRadio
onClick={() => updateField('splitOperatorType', 'regex')}
checked={values.splitOperatorType === 'regex'}
title={'Split by Regular Expression'}
/>
<TextFieldWithDesc
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
description={'Separator to split the list'}
/>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(val) => updateField('joinSeparator', val)}
description={'Separator to join the wrapped list'}
/>
<CheckboxWithDesc
checked={values.deleteEmptyItems}
onChange={(checked) => updateField('deleteEmptyItems', checked)}
title={'Remove empty items'}
/>
</Box>
)
},
{
title: 'Wrap Options',
component: (
<Box>
<TextFieldWithDesc
value={values.left}
onOwnChange={(val) => updateField('left', val)}
description={'Text to add before each item'}
/>
<TextFieldWithDesc
value={values.right}
onOwnChange={(val) => updateField('right', val)}
description={'Text to add after each item'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input List" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="Wrapped List" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
validationSchema={validationSchema}
toolInfo={{
title: 'List Wrapping',
description:
"This tool allows you to add text before and after each item in a list. You can specify different text for the left and right sides, and control how the list is processed. It's useful for adding quotes, brackets, or other formatting to list items, preparing data for different formats, or creating structured text."
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -26,5 +26,8 @@ export const stringTools = [
stringUppercase, stringUppercase,
stringExtractSubstring, stringExtractSubstring,
stringCreatePalindrome, stringCreatePalindrome,
stringPalindrome stringPalindrome,
stringQuote,
stringRotate,
stringRot13
]; ];

View File

@@ -1,11 +1,149 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import ToolContent from '@components/ToolContent';
import * as Yup from 'yup'; import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { stringQuoter } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ leftQuote: string;
// splitSeparator: Yup.string().required('The separator is required') rightQuote: string;
}); doubleQuotation: boolean;
export default function Quote() { emptyQuoting: boolean;
return <Box>Lorem ipsum</Box>; multiLine: boolean;
}
const initialValues: InitialValuesType = {
leftQuote: '"',
rightQuote: '"',
doubleQuotation: false,
emptyQuoting: true,
multiLine: true
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Quote text with double quotes',
description: 'This example shows how to quote text with double quotes.',
sampleText: 'Hello World',
sampleResult: '"Hello World"',
sampleOptions: {
leftQuote: '"',
rightQuote: '"',
doubleQuotation: false,
emptyQuoting: true,
multiLine: false
}
},
{
title: 'Quote multi-line text with single quotes',
description:
'This example shows how to quote multi-line text with single quotes.',
sampleText: 'Hello\nWorld',
sampleResult: "'Hello'\n'World'",
sampleOptions: {
leftQuote: "'",
rightQuote: "'",
doubleQuotation: false,
emptyQuoting: true,
multiLine: true
}
},
{
title: 'Quote with custom quotes',
description: 'This example shows how to quote text with custom quotes.',
sampleText: 'Hello World',
sampleResult: '<<Hello World>>',
sampleOptions: {
leftQuote: '<<',
rightQuote: '>>',
doubleQuotation: false,
emptyQuoting: true,
multiLine: false
}
}
];
export default function Quote({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
setResult(
stringQuoter(
input,
optionsValues.leftQuote,
optionsValues.rightQuote,
optionsValues.doubleQuotation,
optionsValues.emptyQuoting,
optionsValues.multiLine
)
);
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Quote Options',
component: (
<Box>
<TextFieldWithDesc
value={values.leftQuote}
onOwnChange={(val) => updateField('leftQuote', val)}
description={'Left quote character(s)'}
/>
<TextFieldWithDesc
value={values.rightQuote}
onOwnChange={(val) => updateField('rightQuote', val)}
description={'Right quote character(s)'}
/>
<CheckboxWithDesc
checked={values.doubleQuotation}
onChange={(checked) => updateField('doubleQuotation', checked)}
title={'Allow double quotation'}
/>
<CheckboxWithDesc
checked={values.emptyQuoting}
onChange={(checked) => updateField('emptyQuoting', checked)}
title={'Quote empty lines'}
/>
<CheckboxWithDesc
checked={values.multiLine}
onChange={(checked) => updateField('multiLine', checked)}
title={'Process as multi-line text'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="Quoted Text" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
toolInfo={{
title: 'Text Quoter',
description:
"This tool allows you to add quotes around text. You can choose different quote characters, handle multi-line text, and control how empty lines are processed. It's useful for preparing text for programming, formatting data, or creating stylized text."
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -1,11 +1,60 @@
import { Box } from '@mui/material'; import React, { useState } from 'react';
import React from 'react'; import ToolContent from '@components/ToolContent';
import * as Yup from 'yup'; import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { rot13 } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
const initialValues = {}; type InitialValuesType = Record<string, never>;
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required') const initialValues: InitialValuesType = {};
});
export default function Rot13() { const exampleCards: CardExampleType<InitialValuesType>[] = [
return <Box>Lorem ipsum</Box>; {
title: 'Encode a message with ROT13',
description:
'This example shows how to encode a simple message using ROT13 cipher.',
sampleText: 'Hello, World!',
sampleResult: 'Uryyb, Jbeyq!',
sampleOptions: {}
},
{
title: 'Decode a ROT13 message',
description:
'This example shows how to decode a message that was encoded with ROT13.',
sampleText: 'Uryyb, Jbeyq!',
sampleResult: 'Hello, World!',
sampleOptions: {}
}
];
export default function Rot13({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (_: InitialValuesType, input: string) => {
if (input) setResult(rot13(input));
};
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="ROT13 Result" value={result} />}
initialValues={initialValues}
getGroups={null}
toolInfo={{
title: 'What Is ROT13?',
description:
'ROT13 (rotate by 13 places) is a simple letter substitution cipher that replaces a letter with the 13th letter after it in the alphabet. ROT13 is a special case of the Caesar cipher which was developed in ancient Rome. Because there are 26 letters in the English alphabet, ROT13 is its own inverse; that is, to undo ROT13, the same algorithm is applied, so the same action can be used for encoding and decoding.'
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }

View File

@@ -1,11 +1,131 @@
import React, { useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import ToolContent from '@components/ToolContent';
import * as Yup from 'yup'; import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { rotateString } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
const initialValues = {}; interface InitialValuesType {
const validationSchema = Yup.object({ step: string;
// splitSeparator: Yup.string().required('The separator is required') direction: 'left' | 'right';
}); multiLine: boolean;
export default function Rotate() { }
return <Box>Lorem ipsum</Box>;
const initialValues: InitialValuesType = {
step: '1',
direction: 'right',
multiLine: true
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Rotate text to the right',
description:
'This example shows how to rotate text to the right by 2 positions.',
sampleText: 'abcdef',
sampleResult: 'efabcd',
sampleOptions: {
step: '2',
direction: 'right',
multiLine: false
}
},
{
title: 'Rotate text to the left',
description:
'This example shows how to rotate text to the left by 2 positions.',
sampleText: 'abcdef',
sampleResult: 'cdefab',
sampleOptions: {
step: '2',
direction: 'left',
multiLine: false
}
},
{
title: 'Rotate multi-line text',
description:
'This example shows how to rotate each line of a multi-line text.',
sampleText: 'abcdef\nghijkl',
sampleResult: 'fabcde\nlghijk',
sampleOptions: {
step: '1',
direction: 'right',
multiLine: true
}
}
];
export default function Rotate({ title }: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: InitialValuesType, input: string) => {
if (input) {
const step = parseInt(optionsValues.step, 10) || 1;
const isRight = optionsValues.direction === 'right';
setResult(rotateString(input, step, isRight, optionsValues.multiLine));
}
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
title: 'Rotation Options',
component: (
<Box>
<TextFieldWithDesc
value={values.step}
onOwnChange={(val) => updateField('step', val)}
description={'Number of positions to rotate'}
type="number"
/>
<SimpleRadio
onClick={() => updateField('direction', 'right')}
checked={values.direction === 'right'}
title={'Rotate Right'}
/>
<SimpleRadio
onClick={() => updateField('direction', 'left')}
checked={values.direction === 'left'}
title={'Rotate Left'}
/>
<CheckboxWithDesc
checked={values.multiLine}
onChange={(checked) => updateField('multiLine', checked)}
title={'Process as multi-line text (rotate each line separately)'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
inputComponent={
<ToolTextInput title="Input Text" value={input} onChange={setInput} />
}
resultComponent={<ToolTextResult title="Rotated Text" value={result} />}
initialValues={initialValues}
getGroups={getGroups}
toolInfo={{
title: 'String Rotation',
description:
'This tool allows you to rotate characters in a string by a specified number of positions. You can rotate to the left or right, and process multi-line text by rotating each line separately. String rotation is useful for simple text transformations, creating patterns, or implementing basic encryption techniques.'
}}
exampleCards={exampleCards}
input={input}
setInput={setInput}
compute={compute}
/>
);
} }