refactor: tools folder inside pages

This commit is contained in:
Ibrahima G. Coulibaly
2025-02-23 01:38:42 +01:00
parent 62f084eb45
commit 64936ab11f
117 changed files with 447 additions and 194 deletions

View File

@@ -0,0 +1,148 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import ToolOptions from '@components/options/ToolOptions';
import { compute, SplitOperatorType } from './service';
import RadioWithTextField from '@components/options/RadioWithTextField';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import ToolInputAndResult from '@components/ToolInputAndResult';
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
symbolValue: ' ',
regexValue: '/\\s+/',
lengthValue: '16',
chunksValue: '4',
outputSeparator: '\\n',
charBeforeChunk: '',
charAfterChunk: ''
};
const splitOperators: {
title: string;
description: string;
type: SplitOperatorType;
}[] = [
{
title: 'Use a Symbol for Splitting',
description:
'Character that will be used to\n' +
'break text into parts.\n' +
'(Space by default.)',
type: 'symbol'
},
{
title: 'Use a Regex for Splitting',
type: 'regex',
description:
'Regular expression that will be\n' +
'used to break text into parts.\n' +
'(Multiple spaces by default.)'
},
{
title: 'Use Length for Splitting',
description:
'Number of symbols that will be\n' + 'put in each output chunk.',
type: 'length'
},
{
title: 'Use a Number of Chunks',
description: 'Number of chunks of equal\n' + 'length in the output.',
type: 'chunks'
}
];
const outputOptions: {
description: string;
accessor: keyof typeof initialValues;
}[] = [
{
description:
'Character that will be put\n' +
'between the split chunks.\n' +
'(It\'s newline "\\n" by default.)',
accessor: 'outputSeparator'
},
{
description: 'Character before each chunk',
accessor: 'charBeforeChunk'
},
{
description: 'Character after each chunk',
accessor: 'charAfterChunk'
}
];
export default function SplitText() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
const computeExternal = (optionsValues: typeof initialValues, input: any) => {
const {
splitSeparatorType,
outputSeparator,
charBeforeChunk,
charAfterChunk,
chunksValue,
symbolValue,
regexValue,
lengthValue
} = optionsValues;
setResult(
compute(
splitSeparatorType,
input,
symbolValue,
regexValue,
Number(lengthValue),
Number(chunksValue),
charBeforeChunk,
charAfterChunk,
outputSeparator
)
);
};
return (
<Box>
<ToolInputAndResult
input={<ToolTextInput value={input} onChange={setInput} />}
result={<ToolTextResult title={'Text pieces'} value={result} />}
/>
<ToolOptions
compute={computeExternal}
getGroups={({ values, updateField }) => [
{
title: 'Split separator options',
component: splitOperators.map(({ title, description, type }) => (
<RadioWithTextField
key={type}
checked={type === values.splitSeparatorType}
title={title}
fieldName={'splitSeparatorType'}
description={description}
value={values[`${type}Value`]}
onRadioClick={() => updateField('splitSeparatorType', type)}
onTextChange={(val) => updateField(`${type}Value`, val)}
/>
))
},
{
title: 'Output separator options',
component: outputOptions.map((option) => (
<TextFieldWithDesc
key={option.accessor}
value={values[option.accessor]}
onOwnChange={(value) => updateField(option.accessor, value)}
description={option.description}
/>
))
}
]}
initialValues={initialValues}
input={input}
/>
</Box>
);
}

View File

@@ -0,0 +1,14 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
import image from '@assets/text.png';
export const tool = defineTool('string', {
path: 'split',
name: 'Text splitter',
image,
description:
"World's simplest browser-based utility for splitting text. Load your text in the input form on the left and you'll automatically get pieces of this text on the right. Powerful, free, and fast. Load text get chunks.",
shortDescription: 'Quickly split a text',
keywords: ['text', 'split'],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,65 @@
export type SplitOperatorType = 'symbol' | 'regex' | 'length' | 'chunks';
function splitTextByLength(text: string, length: number) {
if (length <= 0) throw new Error('Length must be a positive number');
const result: string[] = [];
for (let i = 0; i < text.length; i += length) {
result.push(text.slice(i, i + length));
}
return result;
}
function splitIntoChunks(text: string, numChunks: number) {
if (numChunks <= 0)
throw new Error('Number of chunks must be a positive number');
const totalLength = text.length;
if (totalLength < numChunks)
throw new Error(
'Text length must be at least as long as the number of chunks'
);
const chunkSize = Math.ceil(totalLength / numChunks); // Calculate the chunk size, rounding up to handle remainders
let result = [];
for (let i = 0; i < totalLength; i += chunkSize) {
result.push(text.slice(i, i + chunkSize));
}
// Ensure the result contains exactly numChunks, adjusting the last chunk if necessary
if (result.length > numChunks) {
result[numChunks - 1] = result.slice(numChunks - 1).join(''); // Merge any extra chunks into the last chunk
result = result.slice(0, numChunks); // Take only the first numChunks chunks
}
return result;
}
export function compute(
splitSeparatorType: SplitOperatorType,
input: string,
symbolValue: string,
regexValue: string,
lengthValue: number,
chunksValue: number,
charBeforeChunk: string,
charAfterChunk: string,
outputSeparator: string
) {
let splitText;
switch (splitSeparatorType) {
case 'symbol':
splitText = input.split(symbolValue);
break;
case 'regex':
splitText = input.split(new RegExp(regexValue));
break;
case 'length':
splitText = splitTextByLength(input, lengthValue);
break;
case 'chunks':
splitText = splitIntoChunks(input, chunksValue).map(
(chunk) => `${charBeforeChunk}${chunk}${charAfterChunk}`
);
}
return splitText.join(outputSeparator);
}

View File

@@ -0,0 +1,72 @@
import { describe, expect, it } from 'vitest';
import { compute } from './service';
describe('compute function', () => {
it('should split by symbol', () => {
const result = compute('symbol', 'hello world', ' ', '', 0, 0, '', '', ',');
expect(result).toBe('hello,world');
});
it('should split by regex', () => {
const result = compute(
'regex',
'hello1world2again',
'',
'\\d',
0,
0,
'',
'',
','
);
expect(result).toBe('hello,world,again');
});
it('should split by length', () => {
const result = compute('length', 'helloworld', '', '', 3, 0, '', '', ',');
expect(result).toBe('hel,low,orl,d');
});
it('should split into chunks', () => {
const result = compute(
'chunks',
'helloworldagain',
'',
'',
0,
3,
'[',
']',
','
);
expect(result).toBe('[hello],[world],[again]');
});
it('should handle empty input', () => {
const result = compute('symbol', '', ' ', '', 0, 0, '', '', ',');
expect(result).toBe('');
});
it('should handle length greater than text length', () => {
const result = compute('length', 'hi', '', '', 5, 0, '', '', ',');
expect(result).toBe('hi');
});
it('should handle chunks greater than text length', () => {
expect(() => {
compute('chunks', 'hi', '', '', 0, 5, '', '', ',');
}).toThrow('Text length must be at least as long as the number of chunks');
});
it('should handle invalid length', () => {
expect(() => {
compute('length', 'hello', '', '', -1, 0, '', '', ',');
}).toThrow('Length must be a positive number');
});
it('should handle invalid chunks', () => {
expect(() => {
compute('chunks', 'hello', '', '', 0, 0, '', '', ',');
}).toThrow('Number of chunks must be a positive number');
});
});