diff --git a/.eslintrc b/.eslintrc index 6a44f8c..ef1ea0f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,7 +15,7 @@ "plugin:react/recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended", + // "plugin:prettier/recommended", "plugin:tailwindcss/recommended" ], "parser": "@typescript-eslint/parser", @@ -26,7 +26,12 @@ "ecmaVersion": 11, "sourceType": "module" }, - "plugins": ["react", "react-hooks", "@typescript-eslint", "tailwindcss"], + "plugins": [ + "react", + "react-hooks", + "@typescript-eslint", + "tailwindcss" + ], "rules": { "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..14ad7b1 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a604522 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/omni-tools.iml b/.idea/omni-tools.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/omni-tools.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 0e5c1db..e9c0f50 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,4 @@ { "trailingComma": "none", - "semi": false, "singleQuote": true } diff --git a/package-lock.json b/package-lock.json index c5761e1..fee9f1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,12 @@ "@mui/icons-material": "^5.15.20", "@mui/material": "^5.15.20", "@types/lodash": "^4.17.5", + "formik": "^2.4.6", "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.23.1" + "react-router-dom": "^6.23.1", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.4.5", @@ -1972,6 +1974,15 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3222,6 +3233,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4113,6 +4132,30 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5158,6 +5201,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6005,6 +6053,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6057,6 +6110,11 @@ "react": "^18.3.1" } }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -6913,6 +6971,16 @@ "node": ">=0.8" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "node_modules/tinybench": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", @@ -6957,6 +7025,11 @@ "node": ">=8.0" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -7007,8 +7080,7 @@ "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/type-check": { "version": "0.4.0", @@ -7634,6 +7706,28 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", + "integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index d5bbb0e..e6a8d16 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,12 @@ "@mui/icons-material": "^5.15.20", "@mui/material": "^5.15.20", "@types/lodash": "^4.17.5", + "formik": "^2.4.6", "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.23.1" + "react-router-dom": "^6.23.1", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.4.5", diff --git a/src/components/App.tsx b/src/components/App.tsx index e8b24c4..11f665a 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,23 +1,22 @@ -import {BrowserRouter, useRoutes} from "react-router-dom"; -import routesConfig from "../config/routesConfig"; -import Navbar from "./Navbar"; -import {Suspense} from "react"; -import Loading from "./Loading"; -import {ThemeProvider} from "@mui/material"; -import theme from "../config/muiConfig"; +import { BrowserRouter, useRoutes } from 'react-router-dom' +import routesConfig from '../config/routesConfig' +import Navbar from './Navbar' +import { Suspense } from 'react' +import Loading from './Loading' +import { ThemeProvider } from '@mui/material' +import theme from '../config/muiConfig' const AppRoutes = () => { - return useRoutes(routesConfig); -}; + return useRoutes(routesConfig) +} function App() { - return ( - - }> - + + }> + diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index f8b36b8..114b105 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -1,7 +1,7 @@ -import Typography from "@mui/material/Typography"; -import {useState} from "react"; -import Box from "@mui/material/Box"; -import {useTimeout} from "../hooks"; +import Typography from '@mui/material/Typography' +import { useState } from 'react' +import Box from '@mui/material/Box' +import { useTimeout } from '../hooks' export type FuseLoadingProps = { delay?: number; @@ -12,12 +12,12 @@ export type FuseLoadingProps = { * FuseLoading displays a loading state with an optional delay */ function FuseLoading(props: FuseLoadingProps) { - const {delay = 0, className} = props; - const [showLoading, setShowLoading] = useState(!delay); + const { delay = 0, className } = props + const [showLoading, setShowLoading] = useState(!delay) useTimeout(() => { - setShowLoading(true); - }, delay); + setShowLoading(true) + }, delay) return (
- Chargement + Loading div": { - backgroundColor: "palette.secondary.main", - }, + '& > div': { + backgroundColor: 'palette.secondary.main' + } }} > -
-
-
+
+
+
- ); + ) } -export default FuseLoading; +export default FuseLoading diff --git a/src/components/input/ToolTextInput.tsx b/src/components/input/ToolTextInput.tsx new file mode 100644 index 0000000..7920e61 --- /dev/null +++ b/src/components/input/ToolTextInput.tsx @@ -0,0 +1,31 @@ +import { Box, Stack, TextField } from '@mui/material' +import Typography from '@mui/material/Typography' +import Button from '@mui/material/Button' +import PublishIcon from '@mui/icons-material/Publish' +import ContentPasteIcon from '@mui/icons-material/ContentPaste' +import React from 'react' + +export default function ToolTextInput({ value, onChange, title = 'Input text' }: { + title?: string; + value: string + onChange: (value: string) => void +}) { + return ( + + + {title} + + onChange(event.target.value)} + fullWidth + multiline + rows={10} + /> + + + + + + ) +} diff --git a/src/components/result/ToolTextResult.tsx b/src/components/result/ToolTextResult.tsx new file mode 100644 index 0000000..916b132 --- /dev/null +++ b/src/components/result/ToolTextResult.tsx @@ -0,0 +1,21 @@ +import Typography from '@mui/material/Typography' +import { Box, Stack, TextField } from '@mui/material' +import Button from '@mui/material/Button' +import DownloadIcon from '@mui/icons-material/Download' +import ContentPasteIcon from '@mui/icons-material/ContentPaste' +import React from 'react' + +export default function ToolTextResult({ title = 'Result', value }: { title?: string; value: string }) { + return ( + + + {title} + + + + + + + + ) +} diff --git a/src/config/muiConfig.ts b/src/config/muiConfig.ts index 53308fc..0aeb41b 100644 --- a/src/config/muiConfig.ts +++ b/src/config/muiConfig.ts @@ -1,11 +1,12 @@ -import {createTheme} from "@mui/material"; +import { createTheme } from '@mui/material' const theme = createTheme({ typography: { button: { textTransform: 'none' } - } -}); + }, + palette: { background: { default: '#ebf5ff' } } +}) -export default theme; +export default theme diff --git a/src/pages/images/png/change-colors-in-png/index.tsx b/src/pages/images/png/change-colors-in-png/index.tsx index a5286a7..1229f9f 100644 --- a/src/pages/images/png/change-colors-in-png/index.tsx +++ b/src/pages/images/png/change-colors-in-png/index.tsx @@ -1,5 +1,5 @@ import { Box } from "@mui/material"; -export default function ChangeColorsInPng(){ - return() +export default function ChangeColorsInPng() { + return ; } diff --git a/src/pages/string/split/index.tsx b/src/pages/string/split/index.tsx index 4b590a2..87818ed 100644 --- a/src/pages/string/split/index.tsx +++ b/src/pages/string/split/index.tsx @@ -1,45 +1,169 @@ -import ToolHeader from "../../../components/ToolHeader"; -import ToolLayout from "../../../components/ToolLayout"; -import {Box, Stack, TextField} from "@mui/material"; -import Grid from "@mui/material/Grid"; -import Typography from "@mui/material/Typography"; -import Button from "@mui/material/Button"; -import PublishIcon from '@mui/icons-material/Publish'; -import ContentPasteIcon from '@mui/icons-material/ContentPaste'; -import DownloadIcon from '@mui/icons-material/Download'; -import React, {useEffect, useState} from "react"; +import ToolHeader from '../../../components/ToolHeader' +import ToolLayout from '../../../components/ToolLayout' +import { Box, Radio, Stack, TextField, useTheme } from '@mui/material' +import Grid from '@mui/material/Grid' +import Typography from '@mui/material/Typography' +import Button from '@mui/material/Button' +import PublishIcon from '@mui/icons-material/Publish' +import ContentPasteIcon from '@mui/icons-material/ContentPaste' +import DownloadIcon from '@mui/icons-material/Download' +import React, { useEffect, useRef, useState } from 'react' +import ToolTextInput from '../../../components/input/ToolTextInput' +import ToolTextResult from '../../../components/result/ToolTextResult' +import SettingsIcon from '@mui/icons-material/Settings' +import { Field, Formik, FormikProps } from 'formik' +import * as Yup from 'yup' +type SplitOperatorType = 'symbol' | 'regex' | 'length' | 'chunks'; +const initialValues = { + splitSeparatorType: 'symbol', + splitSeparator: ' ' +} +const initialSplitOperators: { + title: string; + description: string; + defaultValue: 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.)', + defaultValue: ' ', + type: 'symbol' +}, + { + title: 'Use a Regex for Splitting', + defaultValue: '/\\s+/', + 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.', + defaultValue: '16', + type: 'length' + }, + { + title: 'Use a Number of Chunks', description: 'Number of chunks of equal\n' + + 'length in the output.', + defaultValue: '4', + type: 'chunks' + }] + +const CustomRadioButton = ({ + fieldName, + type, + title, + setFieldValue, + value, + description, + onTextChange + }: { + fieldName: string; + title: string; + type: SplitOperatorType; + setFieldValue: (fieldName: string, val: string) => void; + value: string; + description: string; + onTextChange: (type: SplitOperatorType, value: string) => void; +}) => { + const onChange = () => setFieldValue(fieldName, type) + return ( + + + {title} + + onTextChange(type, event.target.value)} /> + {description} + + ) +} export default function SplitText() { - const [input, setInput] = useState(''); + const [input, setInput] = useState('') const [result, setResult] = useState('') + const formRef = useRef>() + const theme = useTheme() + const [splitOperators, setSplitOperators] = useState<{ + title: string; + description: string; + defaultValue: string; + type: SplitOperatorType; + value: string + }[]>(initialSplitOperators.map(operator => ({ ...operator, value: operator.defaultValue }))) useEffect(() => { setResult(input.split(' ').join('\n')) - }, [input]); + }, [input]) + + const validationSchema = Yup.object({ + splitSeparator: Yup.string().required('The separator is required') + }) + + const onSplitOperatorTextChange = (type: SplitOperatorType, value: string) => { + const splitOperatorsClone = [...splitOperators].map(splitOperator => { + if (splitOperator.type === type) { + splitOperator.value = value + } + return splitOperator + }) + setSplitOperators(splitOperatorsClone) + } return ( - + - Input text - setInput(event.target.value)} fullWidth multiline rows={10}/> - - - - + - Text pieces - - - - - + - - + + + + Tool options + + { + }} + > + {({ setFieldValue }) => ( + + + Split separator options + {splitOperators.map(({ title, description, type, value }) => )} + + + + )} + - ) + + ) }