feat: rotate ui

This commit is contained in:
Ibrahima G. Coulibaly
2024-07-14 00:23:30 +01:00
parent 350061d79e
commit 46859592cf
30 changed files with 723 additions and 510 deletions

View File

@@ -2,61 +2,65 @@ import { expect, describe, it } from 'vitest';
import { duplicateList } from './service';
describe('duplicateList function', () => {
it('should duplicate elements correctly with symbol split', () => {
const input = "Hello World";
const result = duplicateList('symbol', ' ', ' ', input, true, false, 2);
expect(result).toBe("Hello World Hello World");
});
it('should duplicate elements correctly with symbol split', () => {
const input = 'Hello World';
const result = duplicateList('symbol', ' ', ' ', input, true, false, 2);
expect(result).toBe('Hello World Hello World');
});
it('should duplicate elements correctly with regex split', () => {
const input = "Hello||World";
const result = duplicateList('regex', '\\|\\|', ' ', input, true, false, 2);
expect(result).toBe("Hello World Hello World");
});
it('should duplicate elements correctly with regex split', () => {
const input = 'Hello||World';
const result = duplicateList('regex', '\\|\\|', ' ', input, true, false, 2);
expect(result).toBe('Hello World Hello World');
});
it('should handle fractional duplication', () => {
const input = "Hello World";
const result = duplicateList('symbol', ' ', ' ', input, true, false, 1.5);
expect(result).toBe("Hello World Hello");
});
it('should handle fractional duplication', () => {
const input = 'Hello World';
const result = duplicateList('symbol', ' ', ' ', input, true, false, 1.5);
expect(result).toBe('Hello World Hello');
});
it('should handle reverse option correctly', () => {
const input = "Hello World";
const result = duplicateList('symbol', ' ', ' ', input, true, true, 2);
expect(result).toBe("Hello World World Hello");
});
it('should handle reverse option correctly', () => {
const input = 'Hello World';
const result = duplicateList('symbol', ' ', ' ', input, true, true, 2);
expect(result).toBe('Hello World World Hello');
});
it('should handle concatenate option correctly', () => {
const input = "Hello World";
const result = duplicateList('symbol', ' ', ' ', input, false, false, 2);
expect(result).toBe("Hello Hello World World");
});
it('should handle concatenate option correctly', () => {
const input = 'Hello World';
const result = duplicateList('symbol', ' ', ' ', input, false, false, 2);
expect(result).toBe('Hello Hello World World');
});
it('should handle interweaving option correctly', () => {
const input = "Hello World";
const result = duplicateList('symbol', ' ', ' ', input, false, false, 2);
expect(result).toBe("Hello Hello World World");
});
it('should handle interweaving option correctly', () => {
const input = 'Hello World';
const result = duplicateList('symbol', ' ', ' ', input, false, false, 2);
expect(result).toBe('Hello Hello World World');
});
it('should throw an error for negative copies', () => {
expect(() => duplicateList('symbol', ' ', ' ', "Hello World", true, false, -1)).toThrow("Number of copies cannot be negative");
});
it('should throw an error for negative copies', () => {
expect(() =>
duplicateList('symbol', ' ', ' ', 'Hello World', true, false, -1)
).toThrow('Number of copies cannot be negative');
});
it('should handle interweaving option correctly 2', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, false, true, 2.1);
expect(result).toBe("je, king, m'appelle, m'appelle, king, je");
});
it('should handle interweaving option correctly 2', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, false, true, 2.1);
expect(result).toBe("je, king, m'appelle, m'appelle, king, je");
});
it('should handle interweaving option correctly 3', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, false, true, 1);
expect(result).toBe("je, m'appelle, king");
});
it('should handle interweaving option correctly 3', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, false, true, 1);
expect(result).toBe("je, m'appelle, king");
});
it('should handle interweaving option correctly 3', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, true, true, 2.7);
expect(result).toBe("je, m'appelle, king, king, m'appelle, je, king, m'appelle");
});
});
it('should handle interweaving option correctly 3', () => {
const input = "je m'appelle king";
const result = duplicateList('symbol', ' ', ', ', input, true, true, 2.7);
expect(result).toBe(
"je, m'appelle, king, king, m'appelle, je, king, m'appelle"
);
});
});

View File

@@ -8,4 +8,4 @@ const validationSchema = Yup.object({
});
export default function Duplicate() {
return <Box>Lorem ipsum</Box>;
}
}

View File

@@ -10,4 +10,4 @@ export const tool = defineTool('list', {
shortDescription: '',
keywords: ['duplicate'],
component: lazy(() => import('./index'))
});
});

View File

@@ -1,69 +1,81 @@
export type SplitOperatorType = 'symbol' | 'regex';
function interweave(
array1: string[],
array2: string[]) {
const result: string[] = [];
const maxLength = Math.max(array1.length, array2.length);
function interweave(array1: string[], array2: string[]) {
const result: string[] = [];
const maxLength = Math.max(array1.length, array2.length);
for (let i = 0; i < maxLength; i++) {
if (i < array1.length) result.push(array1[i]);
if (i < array2.length) result.push(array2[i]);
}
return result;
for (let i = 0; i < maxLength; i++) {
if (i < array1.length) result.push(array1[i]);
if (i < array2.length) result.push(array2[i]);
}
return result;
}
function duplicate(
input: string[],
concatenate: boolean,
reverse: boolean,
copy?: number
input: string[],
concatenate: boolean,
reverse: boolean,
copy?: number
) {
if (copy) {
if (copy > 0) {
let result: string[] = [];
let toAdd: string[] = [];
let WholePart: string[] = [];
let fractionalPart: string[] = [];
const whole = Math.floor(copy);
const fractional = copy - whole;
if (!reverse) {
WholePart = concatenate ? Array(whole).fill(input).flat() : Array(whole - 1).fill(input).flat();
fractionalPart = input.slice(0, Math.floor(input.length * fractional));
toAdd = WholePart.concat(fractionalPart);
result = concatenate ? WholePart.concat(fractionalPart) : interweave(input, toAdd);
} else {
WholePart = Array(whole - 1).fill(input).flat().reverse()
fractionalPart = input.slice().reverse().slice(0, Math.floor(input.length * fractional));
toAdd = WholePart.concat(fractionalPart);
result = concatenate ? input.concat(toAdd) : interweave(input, toAdd);
}
if (copy) {
if (copy > 0) {
let result: string[] = [];
let toAdd: string[] = [];
let WholePart: string[] = [];
let fractionalPart: string[] = [];
const whole = Math.floor(copy);
const fractional = copy - whole;
if (!reverse) {
WholePart = concatenate
? Array(whole).fill(input).flat()
: Array(whole - 1)
.fill(input)
.flat();
fractionalPart = input.slice(0, Math.floor(input.length * fractional));
toAdd = WholePart.concat(fractionalPart);
result = concatenate
? WholePart.concat(fractionalPart)
: interweave(input, toAdd);
} else {
WholePart = Array(whole - 1)
.fill(input)
.flat()
.reverse();
fractionalPart = input
.slice()
.reverse()
.slice(0, Math.floor(input.length * fractional));
toAdd = WholePart.concat(fractionalPart);
result = concatenate ? input.concat(toAdd) : interweave(input, toAdd);
}
return result;
}
throw new Error("Number of copies cannot be negative");
return result;
}
throw new Error("Number of copies must be a valid number");
throw new Error('Number of copies cannot be negative');
}
throw new Error('Number of copies must be a valid number');
}
export function duplicateList(
splitOperatorType: SplitOperatorType,
splitSeparator: string,
joinSeparator: string,
input: string,
concatenate: boolean,
reverse: boolean,
copy?: number
splitOperatorType: SplitOperatorType,
splitSeparator: string,
joinSeparator: string,
input: string,
concatenate: boolean,
reverse: boolean,
copy?: number
): string {
let array: string[];
let result: string[];
switch (splitOperatorType) {
case 'symbol':
array = input.split(splitSeparator);
break;
case 'regex':
array = input.split(new RegExp(splitSeparator)).filter(item => item !== '');
break;
}
result = duplicate(array, concatenate, reverse, copy);
return result.join(joinSeparator);
}
let array: string[];
let result: string[];
switch (splitOperatorType) {
case 'symbol':
array = input.split(splitSeparator);
break;
case 'regex':
array = input
.split(new RegExp(splitSeparator))
.filter((item) => item !== '');
break;
}
result = duplicate(array, concatenate, reverse, copy);
return result.join(joinSeparator);
}

View File

@@ -1,11 +1,158 @@
import { Box } from '@mui/material';
import React from 'react';
import React, { useState } from 'react';
import ToolTextInput from '../../../components/input/ToolTextInput';
import ToolTextResult from '../../../components/result/ToolTextResult';
import * as Yup from 'yup';
import ToolOptions from '../../../components/options/ToolOptions';
import { rotateList, SplitOperatorType } from './service';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
import SimpleRadio from '../../../components/options/SimpleRadio';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import { formatNumber } from '../../../utils/number';
const initialValues = {
splitOperatorType: 'symbol' as SplitOperatorType,
input: '',
splitSeparator: ',',
joinSeparator: ',',
right: true,
step: 1
};
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.'
}
];
const rotationDirections: {
title: string;
description: string;
value: boolean;
}[] = [
{
title: 'Rotate forward',
description:
'Rotate list items to the right. (Down if a vertical column list.)',
value: true
},
{
title: 'Rotate backward',
description:
'Rotate list items to the left. (Up if a vertical column list.)',
value: false
}
];
const initialValues = {};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
export default function Rotate() {
return <Box>Lorem ipsum</Box>;
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
const { splitOperatorType, splitSeparator, joinSeparator, right, step } =
optionsValues;
setResult(
rotateList(
splitOperatorType,
input,
splitSeparator,
joinSeparator,
right,
step
)
);
};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
return (
<Box>
<ToolInputAndResult
input={
<ToolTextInput
title={'Input list'}
value={input}
onChange={setInput}
/>
}
result={<ToolTextResult title={'Rotated list'} value={result} />}
/>
<ToolOptions
compute={compute}
getGroups={({ values, updateField }) => [
{
title: 'Item split mode',
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: 'Rotation Direction and Count',
component: (
<Box>
{rotationDirections.map(({ title, description, value }) => (
<SimpleRadio
key={`${value}`}
onClick={() => updateField('right', Boolean(value))}
title={title}
description={description}
checked={values.right === value}
/>
))}
<TextFieldWithDesc
description={'Number of items to rotate'}
value={values.step}
onOwnChange={(val) =>
updateField('step', formatNumber(val, 1))
}
/>
</Box>
)
},
{
title: 'Rotated List Joining Symbol',
component: (
<Box>
<TextFieldWithDesc
value={values.joinSeparator}
onOwnChange={(value) => updateField('joinSeparator', value)}
description={
'Enter the character that goes between items in the rotated list.'
}
/>
</Box>
)
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Box>
);
}