mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-29 16:16:02 +00:00
refactor: tools folder inside pages
This commit is contained in:
99
src/pages/tools/list/group/group.service.test.ts
Normal file
99
src/pages/tools/list/group/group.service.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { groupList, SplitOperatorType } from './service';
|
||||
|
||||
describe('groupList', () => {
|
||||
it('splits by symbol, groups, pads, and formats correctly', () => {
|
||||
const input = 'a,b,c,d,e,f,g,h,i,j';
|
||||
const splitOperatorType: SplitOperatorType = 'symbol';
|
||||
const splitSeparator = ',';
|
||||
const groupNumber = 3;
|
||||
const itemSeparator = '-';
|
||||
const leftWrap = '[';
|
||||
const rightWrap = ']';
|
||||
const groupSeparator = ' | ';
|
||||
const deleteEmptyItems = false;
|
||||
const padNonFullGroup = true;
|
||||
const paddingChar = 'x';
|
||||
|
||||
const expectedOutput = '[a-b-c] | [d-e-f] | [g-h-i] | [j-x-x]';
|
||||
|
||||
const result = groupList(
|
||||
splitOperatorType,
|
||||
splitSeparator,
|
||||
input,
|
||||
groupNumber,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap,
|
||||
groupSeparator,
|
||||
deleteEmptyItems,
|
||||
padNonFullGroup,
|
||||
paddingChar
|
||||
);
|
||||
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('handles regex split, no padding, and formats correctly', () => {
|
||||
const input = 'a1b2c3d4e5f6g7h8i9j';
|
||||
const splitOperatorType: SplitOperatorType = 'regex';
|
||||
const splitSeparator = '\\d';
|
||||
const groupNumber = 4;
|
||||
const itemSeparator = ',';
|
||||
const leftWrap = '(';
|
||||
const rightWrap = ')';
|
||||
const groupSeparator = ' / ';
|
||||
const deleteEmptyItems = true;
|
||||
const padNonFullGroup = false;
|
||||
|
||||
const expectedOutput = '(a,b,c,d) / (e,f,g,h) / (i,j)';
|
||||
|
||||
const result = groupList(
|
||||
splitOperatorType,
|
||||
splitSeparator,
|
||||
input,
|
||||
groupNumber,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap,
|
||||
groupSeparator,
|
||||
deleteEmptyItems,
|
||||
padNonFullGroup
|
||||
);
|
||||
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
it('handles empty items removal and padd the last group with a z', () => {
|
||||
const input = 'a,,b,,c,,d,,e,,';
|
||||
const splitOperatorType: SplitOperatorType = 'symbol';
|
||||
const splitSeparator = ',';
|
||||
const groupNumber = 2;
|
||||
const itemSeparator = ':';
|
||||
const leftWrap = '<';
|
||||
const rightWrap = '>';
|
||||
const groupSeparator = ' & ';
|
||||
const deleteEmptyItems = true;
|
||||
const padNonFullGroup = true;
|
||||
const paddingChar = 'z';
|
||||
|
||||
const expectedOutput = '<a:b> & <c:d> & <e:z>';
|
||||
|
||||
const result = groupList(
|
||||
splitOperatorType,
|
||||
splitSeparator,
|
||||
input,
|
||||
groupNumber,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap,
|
||||
groupSeparator,
|
||||
deleteEmptyItems,
|
||||
padNonFullGroup,
|
||||
paddingChar
|
||||
);
|
||||
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
});
|
||||
183
src/pages/tools/list/group/index.tsx
Normal file
183
src/pages/tools/list/group/index.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
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 { groupList, SplitOperatorType } from './service';
|
||||
import ToolInputAndResult from '@components/ToolInputAndResult';
|
||||
import SimpleRadio from '@components/options/SimpleRadio';
|
||||
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
||||
import { formatNumber } from '../../../../utils/number';
|
||||
|
||||
const initialValues = {
|
||||
splitOperatorType: 'symbol' as SplitOperatorType,
|
||||
splitSeparator: ',',
|
||||
groupNumber: 2,
|
||||
itemSeparator: ',',
|
||||
leftWrap: '[',
|
||||
rightWrap: ']',
|
||||
groupSeparator: '\\n',
|
||||
deleteEmptyItems: true,
|
||||
padNonFullGroup: false,
|
||||
paddingChar: '...'
|
||||
};
|
||||
const splitOperators: {
|
||||
title: string;
|
||||
description: string;
|
||||
type: SplitOperatorType;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Use a Symbol for Splitting',
|
||||
description: 'Delimit input list items with a character.',
|
||||
type: 'symbol'
|
||||
},
|
||||
{
|
||||
title: 'Use a Regex for Splitting',
|
||||
type: 'regex',
|
||||
description: 'Delimit input list items with a regular expression.'
|
||||
}
|
||||
];
|
||||
|
||||
export default function FindUnique() {
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
const compute = (optionsValues: typeof initialValues, input: any) => {
|
||||
const {
|
||||
splitOperatorType,
|
||||
splitSeparator,
|
||||
groupNumber,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap,
|
||||
groupSeparator,
|
||||
deleteEmptyItems,
|
||||
padNonFullGroup,
|
||||
paddingChar
|
||||
} = optionsValues;
|
||||
|
||||
setResult(
|
||||
groupList(
|
||||
splitOperatorType,
|
||||
splitSeparator,
|
||||
input,
|
||||
groupNumber,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap,
|
||||
groupSeparator,
|
||||
deleteEmptyItems,
|
||||
padNonFullGroup,
|
||||
paddingChar
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<ToolInputAndResult
|
||||
input={
|
||||
<ToolTextInput
|
||||
title={'Input list'}
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
/>
|
||||
}
|
||||
result={<ToolTextResult title={'Grouped items'} value={result} />}
|
||||
/>
|
||||
<ToolOptions
|
||||
compute={compute}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Input Item Separator',
|
||||
component: (
|
||||
<Box>
|
||||
{splitOperators.map(({ title, description, type }) => (
|
||||
<SimpleRadio
|
||||
key={type}
|
||||
onClick={() => updateField('splitOperatorType', type)}
|
||||
title={title}
|
||||
description={description}
|
||||
checked={values.splitOperatorType === type}
|
||||
/>
|
||||
))}
|
||||
<TextFieldWithDesc
|
||||
description={'Set a delimiting symbol or regular expression.'}
|
||||
value={values.splitSeparator}
|
||||
onOwnChange={(val) => updateField('splitSeparator', val)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Group Size and Separators',
|
||||
component: (
|
||||
<Box>
|
||||
<TextFieldWithDesc
|
||||
value={values.groupNumber}
|
||||
description={'Number of items in a group'}
|
||||
type={'number'}
|
||||
onOwnChange={(value) =>
|
||||
updateField('groupNumber', formatNumber(value, 1))
|
||||
}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={values.itemSeparator}
|
||||
description={'Item separator character'}
|
||||
onOwnChange={(value) => updateField('itemSeparator', value)}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={values.groupSeparator}
|
||||
description={'Group separator character'}
|
||||
onOwnChange={(value) => updateField('groupSeparator', value)}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={values.leftWrap}
|
||||
description={"Group's left wrap symbol."}
|
||||
onOwnChange={(value) => updateField('leftWrap', value)}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={values.rightWrap}
|
||||
description={"Group's right wrap symbol."}
|
||||
onOwnChange={(value) => updateField('rightWrap', value)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Empty Items and Padding',
|
||||
component: (
|
||||
<Box>
|
||||
<CheckboxWithDesc
|
||||
title={'Delete Empty Items'}
|
||||
description={
|
||||
"Ignore empty items and don't include them in the groups."
|
||||
}
|
||||
checked={values.deleteEmptyItems}
|
||||
onChange={(value) => updateField('deleteEmptyItems', value)}
|
||||
/>
|
||||
<CheckboxWithDesc
|
||||
title={'Pad Non-full Groups'}
|
||||
description={
|
||||
'Fill non-full groups with a custom item (enter below).'
|
||||
}
|
||||
checked={values.padNonFullGroup}
|
||||
onChange={(value) => updateField('padNonFullGroup', value)}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={values.paddingChar}
|
||||
description={
|
||||
'Use this character or item to pad non-full groups.'
|
||||
}
|
||||
onOwnChange={(value) => updateField('paddingChar', value)}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
]}
|
||||
initialValues={initialValues}
|
||||
input={input}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
13
src/pages/tools/list/group/meta.ts
Normal file
13
src/pages/tools/list/group/meta.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
// import image from '@assets/text.png';
|
||||
|
||||
export const tool = defineTool('list', {
|
||||
name: 'Group',
|
||||
path: 'group',
|
||||
// image,
|
||||
description: '',
|
||||
shortDescription: '',
|
||||
keywords: ['group'],
|
||||
component: lazy(() => import('./index'))
|
||||
});
|
||||
94
src/pages/tools/list/group/service.ts
Normal file
94
src/pages/tools/list/group/service.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
export type SplitOperatorType = 'symbol' | 'regex';
|
||||
|
||||
// function that split the array into an array of subarray of desired length
|
||||
function groupMaker(array: string[], groupNumber: number): string[][] {
|
||||
const result: string[][] = [];
|
||||
for (let i = 0; i < array.length; i += groupNumber) {
|
||||
result.push(array.slice(i, i + groupNumber));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// function use to handle the case paddingNonFullGroup is enable
|
||||
function groupFiller(
|
||||
array: string[][],
|
||||
groupNumber: number,
|
||||
padNonFullGroup: boolean,
|
||||
paddingChar: string = ''
|
||||
): string[][] {
|
||||
if (padNonFullGroup) {
|
||||
const lastSubArray: string[] = array[array.length - 1];
|
||||
if (lastSubArray.length < groupNumber) {
|
||||
for (let i = lastSubArray.length; i < groupNumber; i++) {
|
||||
lastSubArray.push(paddingChar);
|
||||
}
|
||||
}
|
||||
array[array.length - 1] = lastSubArray;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// function that join with the item separator and wrap with left and right each subArray of the Array
|
||||
function groupJoinerAndWrapper(
|
||||
array: string[][],
|
||||
itemSeparator: string = '',
|
||||
leftWrap: string = '',
|
||||
rightWrap: string = ''
|
||||
): string[] {
|
||||
return array.map((subArray) => {
|
||||
return leftWrap + subArray.join(itemSeparator) + rightWrap;
|
||||
});
|
||||
}
|
||||
|
||||
export function groupList(
|
||||
splitOperatorType: SplitOperatorType,
|
||||
splitSeparator: string,
|
||||
input: string,
|
||||
groupNumber: number,
|
||||
itemSeparator: string = '',
|
||||
leftWrap: string = '',
|
||||
rightWrap: string = '',
|
||||
groupSeparator: string,
|
||||
deleteEmptyItems: boolean,
|
||||
padNonFullGroup: boolean,
|
||||
paddingChar: string = ''
|
||||
): string {
|
||||
let array: string[];
|
||||
let splitedArray: string[][];
|
||||
let fullSplitedArray: string[][];
|
||||
let result: string[];
|
||||
switch (splitOperatorType) {
|
||||
case 'symbol':
|
||||
array = input.split(splitSeparator);
|
||||
break;
|
||||
case 'regex':
|
||||
array = input.split(new RegExp(splitSeparator));
|
||||
break;
|
||||
}
|
||||
// delete empty items after intial split
|
||||
if (deleteEmptyItems) {
|
||||
array = array.filter((item) => item !== '');
|
||||
}
|
||||
|
||||
// split the input into an array of subArray with the desired length
|
||||
splitedArray = groupMaker(array, groupNumber);
|
||||
|
||||
// fill the last subArray is PadNonFullGroup is enabled
|
||||
fullSplitedArray = groupFiller(
|
||||
splitedArray,
|
||||
groupNumber,
|
||||
padNonFullGroup,
|
||||
paddingChar
|
||||
);
|
||||
|
||||
// get the list of formated subArray with the item separator and left and right wrapper
|
||||
result = groupJoinerAndWrapper(
|
||||
fullSplitedArray,
|
||||
itemSeparator,
|
||||
leftWrap,
|
||||
rightWrap
|
||||
);
|
||||
|
||||
// finnaly join the group separator before returning
|
||||
return result.join(groupSeparator);
|
||||
}
|
||||
Reference in New Issue
Block a user