mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-18 22:02:08 +05:30
commit
f855d33928
12 changed files with 364 additions and 330 deletions
151
.idea/workspace.xml
generated
151
.idea/workspace.xml
generated
|
|
@ -4,10 +4,13 @@
|
|||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="docs: readme">
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="refact: examples">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/Hero.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Hero.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/examples/Examples.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ToolExamples.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/string/split/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/split/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/tools/defineTool.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/defineTool.tsx" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
|
@ -23,7 +26,7 @@
|
|||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="4-convert-jpg-to-png" />
|
||||
<entry key="$PROJECT_DIR$" value="main" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
|
|
@ -59,47 +62,47 @@
|
|||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"Docker.Dockerfile build.executor": "Run",
|
||||
"Docker.Dockerfile.executor": "Run",
|
||||
"Playwright.JoinText Component.executor": "Run",
|
||||
"Playwright.JoinText Component.should merge text pieces with specified join character.executor": "Run",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"Vitest.compute function (1).executor": "Run",
|
||||
"Vitest.compute function.executor": "Run",
|
||||
"Vitest.mergeText.executor": "Run",
|
||||
"Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run",
|
||||
"Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor": "Run",
|
||||
"git-widget-placeholder": "main",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src/assets",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.dev.executor": "Run",
|
||||
"npm.lint.executor": "Run",
|
||||
"npm.prebuild.executor": "Run",
|
||||
"npm.script:create:tool.executor": "Run",
|
||||
"npm.test.executor": "Run",
|
||||
"npm.test:e2e.executor": "Run",
|
||||
"npm.test:e2e:run.executor": "Run",
|
||||
"prettierjs.PrettierConfiguration.Package": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier",
|
||||
"project.structure.last.edited": "Problems",
|
||||
"project.structure.proportion": "0.0",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"settings.editor.selected.configurable": "settings.typescriptcompiler",
|
||||
"ts.external.directory.path": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"Docker.Dockerfile build.executor": "Run",
|
||||
"Docker.Dockerfile.executor": "Run",
|
||||
"Playwright.JoinText Component.executor": "Run",
|
||||
"Playwright.JoinText Component.should merge text pieces with specified join character.executor": "Run",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"Vitest.compute function (1).executor": "Run",
|
||||
"Vitest.compute function.executor": "Run",
|
||||
"Vitest.mergeText.executor": "Run",
|
||||
"Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run",
|
||||
"Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor": "Run",
|
||||
"git-widget-placeholder": "examples",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src/assets",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.dev.executor": "Run",
|
||||
"npm.lint.executor": "Run",
|
||||
"npm.prebuild.executor": "Run",
|
||||
"npm.script:create:tool.executor": "Run",
|
||||
"npm.test.executor": "Run",
|
||||
"npm.test:e2e.executor": "Run",
|
||||
"npm.test:e2e:run.executor": "Run",
|
||||
"prettierjs.PrettierConfiguration.Package": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier",
|
||||
"project.structure.last.edited": "Problems",
|
||||
"project.structure.proportion": "0.0",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"settings.editor.selected.configurable": "settings.typescriptcompiler",
|
||||
"ts.external.directory.path": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
}]]></component>
|
||||
<component name="ReactDesignerToolWindowState">
|
||||
<option name="myId2Visible">
|
||||
<map>
|
||||
|
|
@ -262,31 +265,7 @@
|
|||
<workItem from="1740490890760" duration="1889000" />
|
||||
<workItem from="1740503199053" duration="4853000" />
|
||||
<workItem from="1740584243965" duration="17000" />
|
||||
<workItem from="1740613094492" duration="1804000" />
|
||||
</task>
|
||||
<task id="LOCAL-00077" summary="ci: run e2e tests">
|
||||
<option name="closed" value="true" />
|
||||
<created>1719587132558</created>
|
||||
<option name="number" value="00077" />
|
||||
<option name="presentableId" value="LOCAL-00077" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1719587132558</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00078" summary="ci: run e2e tests">
|
||||
<option name="closed" value="true" />
|
||||
<created>1719587281298</created>
|
||||
<option name="number" value="00078" />
|
||||
<option name="presentableId" value="LOCAL-00078" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1719587281298</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00079" summary="fix: ci">
|
||||
<option name="closed" value="true" />
|
||||
<created>1719588326608</created>
|
||||
<option name="number" value="00079" />
|
||||
<option name="presentableId" value="LOCAL-00079" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1719588326608</updated>
|
||||
<workItem from="1740613094492" duration="9615000" />
|
||||
</task>
|
||||
<task id="LOCAL-00080" summary="fix: ci">
|
||||
<option name="closed" value="true" />
|
||||
|
|
@ -656,7 +635,31 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1740614185980</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="126" />
|
||||
<task id="LOCAL-00126" summary="chore: handle enter press on search">
|
||||
<option name="closed" value="true" />
|
||||
<created>1740614957672</created>
|
||||
<option name="number" value="00126" />
|
||||
<option name="presentableId" value="LOCAL-00126" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1740614957672</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00127" summary="chore: show tooloptions in example">
|
||||
<option name="closed" value="true" />
|
||||
<created>1740619610168</created>
|
||||
<option name="number" value="00127" />
|
||||
<option name="presentableId" value="LOCAL-00127" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1740619610169</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00128" summary="refact: examples">
|
||||
<option name="closed" value="true" />
|
||||
<created>1740620866551</created>
|
||||
<option name="number" value="00128" />
|
||||
<option name="presentableId" value="LOCAL-00128" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1740620866551</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="129" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
|
@ -688,9 +691,6 @@
|
|||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||
<option name="CHECK_NEW_TODO" value="false" />
|
||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||
<MESSAGE value="feat: group list ui" />
|
||||
<MESSAGE value="feat: reverse list ui" />
|
||||
<MESSAGE value="feat: self host" />
|
||||
<MESSAGE value="chore: format number" />
|
||||
<MESSAGE value="feat: rotate ui" />
|
||||
<MESSAGE value="feat: shuffle ui" />
|
||||
|
|
@ -713,7 +713,10 @@
|
|||
<MESSAGE value="docs: img" />
|
||||
<MESSAGE value="fix: bg" />
|
||||
<MESSAGE value="docs: readme" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="docs: readme" />
|
||||
<MESSAGE value="chore: handle enter press on search" />
|
||||
<MESSAGE value="chore: show tooloptions in example" />
|
||||
<MESSAGE value="refact: examples" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="refact: examples" />
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ function ToolLinks() {
|
|||
|
||||
return (
|
||||
<Grid container spacing={2} mt={1}>
|
||||
<Grid item md={12} lg={4}>
|
||||
<Grid item md={12} lg={6}>
|
||||
<StyledButton
|
||||
sx={{ backgroundColor: 'white' }}
|
||||
fullWidth
|
||||
|
|
@ -36,16 +36,16 @@ function ToolLinks() {
|
|||
Use This Tool
|
||||
</StyledButton>
|
||||
</Grid>
|
||||
<Grid item md={12} lg={4}>
|
||||
<Grid item md={12} lg={6}>
|
||||
<StyledButton fullWidth variant="outlined" href="#examples">
|
||||
See Examples
|
||||
</StyledButton>
|
||||
</Grid>
|
||||
<Grid item md={12} lg={4}>
|
||||
<StyledButton fullWidth variant="outlined" href="#tour">
|
||||
Learn How to Use
|
||||
</StyledButton>
|
||||
</Grid>
|
||||
{/*<Grid item md={12} lg={4}>*/}
|
||||
{/* <StyledButton fullWidth variant="outlined" href="#tour">*/}
|
||||
{/* Learn How to Use*/}
|
||||
{/* </StyledButton>*/}
|
||||
{/*</Grid>*/}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { ExampleCardProps } from './Examples';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
|
|
@ -9,26 +8,42 @@ import {
|
|||
useTheme
|
||||
} from '@mui/material';
|
||||
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||
import RequiredOptions from './RequiredOptions';
|
||||
import ExampleOptions from './ExampleOptions';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
|
||||
export default function ExampleCard({
|
||||
export interface ExampleCardProps<T> {
|
||||
title: string;
|
||||
description: string;
|
||||
sampleText: string;
|
||||
sampleResult: string;
|
||||
sampleOptions: T;
|
||||
changeInputResult: (newOptions: T) => void;
|
||||
getGroups: GetGroupsType<T>;
|
||||
}
|
||||
|
||||
export default function ExampleCard<T>({
|
||||
title,
|
||||
description,
|
||||
sampleText,
|
||||
sampleResult,
|
||||
requiredOptions,
|
||||
changeInputResult
|
||||
}: ExampleCardProps) {
|
||||
sampleOptions,
|
||||
changeInputResult,
|
||||
getGroups
|
||||
}: ExampleCardProps<T>) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Card
|
||||
raised
|
||||
onClick={() => {
|
||||
changeInputResult(sampleOptions);
|
||||
}}
|
||||
sx={{
|
||||
bgcolor: theme.palette.background.default,
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 2,
|
||||
transition: 'background-color 0.3s ease',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
boxShadow: '12px 9px 11px 2px #b8b9be, -6px -6px 12px #fff'
|
||||
}
|
||||
|
|
@ -46,7 +61,6 @@ export default function ExampleCard({
|
|||
</Typography>
|
||||
|
||||
<Box
|
||||
onClick={() => changeInputResult(sampleText, sampleResult)}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
zIndex: '2',
|
||||
|
|
@ -55,7 +69,6 @@ export default function ExampleCard({
|
|||
bgcolor: 'transparent',
|
||||
padding: '5px 10px',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
boxShadow: 'inset 2px 2px 5px #b8b9be, inset -3px -3px 7px #fff;'
|
||||
}}
|
||||
>
|
||||
|
|
@ -77,7 +90,6 @@ export default function ExampleCard({
|
|||
|
||||
<ArrowDownwardIcon />
|
||||
<Box
|
||||
onClick={() => changeInputResult(sampleText, sampleResult)}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
zIndex: '2',
|
||||
|
|
@ -106,7 +118,7 @@ export default function ExampleCard({
|
|||
/>
|
||||
</Box>
|
||||
|
||||
<RequiredOptions options={requiredOptions} />
|
||||
<ExampleOptions options={sampleOptions} getGroups={getGroups} />
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
19
src/components/examples/ExampleOptions.tsx
Normal file
19
src/components/examples/ExampleOptions.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import ToolOptionGroups from '@components/options/ToolOptionGroups';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
import React from 'react';
|
||||
|
||||
export default function ExampleOptions<T>({
|
||||
options,
|
||||
getGroups
|
||||
}: {
|
||||
options: T;
|
||||
getGroups: GetGroupsType<T>;
|
||||
}) {
|
||||
return (
|
||||
<ToolOptionGroups
|
||||
// @ts-ignore
|
||||
groups={getGroups({ values: options })}
|
||||
vertical
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import { Box, Grid, Stack, Typography } from '@mui/material';
|
||||
import ExampleCard from './ExampleCard';
|
||||
|
||||
export interface ExampleCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
sampleText: string;
|
||||
sampleResult: string;
|
||||
requiredOptions: RequiredOptionsProps;
|
||||
changeInputResult: (input: string, result: string) => void;
|
||||
}
|
||||
|
||||
export interface RequiredOptionsProps {
|
||||
joinCharacter: string;
|
||||
deleteBlankLines: boolean;
|
||||
deleteTrailingSpaces: boolean;
|
||||
}
|
||||
|
||||
interface ExampleProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
exampleCards: ExampleCardProps[];
|
||||
}
|
||||
|
||||
export default function Examples({
|
||||
title,
|
||||
subtitle,
|
||||
exampleCards
|
||||
}: ExampleProps) {
|
||||
return (
|
||||
<Box id={'examples'} mt={4}>
|
||||
<Box mt={4} display="flex" gap={1} alignItems="center">
|
||||
<Typography mb={2} fontSize={30} color={'primary'}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography mb={2} fontSize={30} color={'secondary'}>
|
||||
{subtitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||
<Grid container spacing={2}>
|
||||
{exampleCards.map((card, index) => (
|
||||
<Grid item xs={12} md={6} lg={4} key={index}>
|
||||
<ExampleCard
|
||||
title={card.title}
|
||||
description={card.description}
|
||||
sampleText={card.sampleText}
|
||||
sampleResult={card.sampleResult}
|
||||
requiredOptions={card.requiredOptions}
|
||||
changeInputResult={card.changeInputResult}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
import { Box, Stack, TextField, Typography } from '@mui/material';
|
||||
import { RequiredOptionsProps } from './Examples';
|
||||
import CheckboxWithDesc from 'components/options/CheckboxWithDesc';
|
||||
|
||||
export default function RequiredOptions({
|
||||
options
|
||||
}: {
|
||||
options: RequiredOptionsProps;
|
||||
}) {
|
||||
const { joinCharacter, deleteBlankLines, deleteTrailingSpaces } = options;
|
||||
|
||||
const handleBoxClick = () => {
|
||||
const toolsElement = document.getElementById('tool');
|
||||
if (toolsElement) {
|
||||
toolsElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction={'column'} alignItems={'left'} spacing={2}>
|
||||
<Typography variant="h5" component="h3" sx={{ marginTop: '5px' }}>
|
||||
Required options
|
||||
</Typography>
|
||||
<Typography variant="body2" component="p">
|
||||
These options will be used automatically if you select this example.
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
onClick={handleBoxClick}
|
||||
sx={{
|
||||
zIndex: '2',
|
||||
cursor: 'pointer',
|
||||
bgcolor: 'transparent',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex'
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
disabled
|
||||
value={joinCharacter}
|
||||
fullWidth
|
||||
rows={1}
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
zIndex: '-1'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{deleteBlankLines ? (
|
||||
<Box onClick={handleBoxClick}>
|
||||
<CheckboxWithDesc
|
||||
title="Delete Blank Lines"
|
||||
checked={deleteBlankLines}
|
||||
onChange={() => {}}
|
||||
description="Delete lines that don't have text symbols."
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{deleteTrailingSpaces ? (
|
||||
<Box onClick={handleBoxClick}>
|
||||
<CheckboxWithDesc
|
||||
title="Delete Training Spaces"
|
||||
checked={deleteTrailingSpaces}
|
||||
onChange={() => {}}
|
||||
description="Remove spaces and tabs at the end of the lines."
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
65
src/components/examples/ToolExamples.tsx
Normal file
65
src/components/examples/ToolExamples.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Box, Grid, Stack, Typography } from '@mui/material';
|
||||
import ExampleCard, { ExampleCardProps } from './ExampleCard';
|
||||
import React from 'react';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
import { FormikProps } from 'formik';
|
||||
|
||||
export type CardExampleType<T> = Omit<
|
||||
ExampleCardProps<T>,
|
||||
'getGroups' | 'changeInputResult'
|
||||
>;
|
||||
|
||||
export interface ExampleProps<T> {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
exampleCards: CardExampleType<T>[];
|
||||
getGroups: GetGroupsType<T>;
|
||||
formRef: React.RefObject<FormikProps<T>>;
|
||||
}
|
||||
|
||||
export default function ToolExamples<T>({
|
||||
title,
|
||||
subtitle,
|
||||
exampleCards,
|
||||
getGroups,
|
||||
formRef
|
||||
}: ExampleProps<T>) {
|
||||
function changeInputResult(newOptions: T) {
|
||||
formRef.current?.setValues(newOptions);
|
||||
const toolsElement = document.getElementById('tool');
|
||||
if (toolsElement) {
|
||||
toolsElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box id={'examples'} mt={4}>
|
||||
<Box mt={4} display="flex" gap={1} alignItems="center">
|
||||
<Typography mb={2} fontSize={30} color={'primary'}>
|
||||
{`${title} Examples`}
|
||||
</Typography>
|
||||
<Typography mb={2} fontSize={30} color={'secondary'}>
|
||||
{subtitle ?? 'Click to try!'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||
<Grid container spacing={2}>
|
||||
{exampleCards.map((card, index) => (
|
||||
<Grid item xs={12} md={6} lg={4} key={index}>
|
||||
<ExampleCard
|
||||
title={card.title}
|
||||
description={card.description}
|
||||
sampleText={card.sampleText}
|
||||
sampleResult={card.sampleResult}
|
||||
sampleOptions={card.sampleOptions}
|
||||
getGroups={getGroups}
|
||||
changeInputResult={changeInputResult}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -8,14 +8,16 @@ export interface ToolOptionGroup {
|
|||
}
|
||||
|
||||
export default function ToolOptionGroups({
|
||||
groups
|
||||
groups,
|
||||
vertical
|
||||
}: {
|
||||
groups: ToolOptionGroup[];
|
||||
vertical?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
{groups.map((group) => (
|
||||
<Grid item xs={12} md={4} key={group.title}>
|
||||
<Grid item xs={12} md={vertical ? 12 : 4} key={group.title}>
|
||||
<Typography mb={1} fontSize={22}>
|
||||
{group.title}
|
||||
</Typography>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import ToolOptionGroups, { ToolOptionGroup } from './ToolOptionGroups';
|
|||
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||
import * as Yup from 'yup';
|
||||
|
||||
type UpdateField<T> = <Y extends keyof T>(field: Y, value: T[Y]) => void;
|
||||
export type UpdateField<T> = <Y extends keyof T>(field: Y, value: T[Y]) => void;
|
||||
|
||||
const FormikListenerComponent = <T,>({
|
||||
initialValues,
|
||||
|
|
@ -68,6 +68,10 @@ const ToolBody = <T,>({
|
|||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export type GetGroupsType<T> = (
|
||||
formikProps: FormikProps<T> & { updateField: UpdateField<T> }
|
||||
) => ToolOptionGroup[];
|
||||
export default function ToolOptions<T extends FormikValues>({
|
||||
children,
|
||||
initialValues,
|
||||
|
|
@ -82,9 +86,7 @@ export default function ToolOptions<T extends FormikValues>({
|
|||
validationSchema?: any | (() => any);
|
||||
compute: (optionsValues: T, input: any) => void;
|
||||
input?: any;
|
||||
getGroups: (
|
||||
formikProps: FormikProps<T> & { updateField: UpdateField<T> }
|
||||
) => ToolOptionGroup[];
|
||||
getGroups: GetGroupsType<T>;
|
||||
formRef?: RefObject<FormikProps<T>>;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Box } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import ToolTextInput from '@components/input/ToolTextInput';
|
||||
import ToolTextResult from '@components/result/ToolTextResult';
|
||||
import ToolOptions from '@components/options/ToolOptions';
|
||||
import ToolOptions, { GetGroupsType } from '@components/options/ToolOptions';
|
||||
import { mergeText } from './service';
|
||||
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
||||
|
|
@ -11,14 +11,18 @@ import ToolInputAndResult from '@components/ToolInputAndResult';
|
|||
|
||||
import ToolInfo from '@components/ToolInfo';
|
||||
import Separator from '@components/Separator';
|
||||
import Examples from '@components/examples/Examples';
|
||||
import ToolExamples, {
|
||||
CardExampleType
|
||||
} from '@components/examples/ToolExamples';
|
||||
import { FormikProps } from 'formik';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
|
||||
const initialValues = {
|
||||
joinCharacter: '',
|
||||
deleteBlank: true,
|
||||
deleteTrailing: true
|
||||
};
|
||||
|
||||
type InitialValuesType = typeof initialValues;
|
||||
const validationSchema = Yup.object().shape({
|
||||
joinCharacter: Yup.string().required('Join character is required'),
|
||||
deleteBlank: Yup.boolean().required('Delete blank is required'),
|
||||
|
|
@ -29,13 +33,13 @@ const mergeOptions = {
|
|||
placeholder: 'Join Character',
|
||||
description:
|
||||
'Symbol that connects broken\n' + 'pieces of text. (Space by default.)\n',
|
||||
accessor: 'joinCharacter' as keyof typeof initialValues
|
||||
accessor: 'joinCharacter' as keyof InitialValuesType
|
||||
};
|
||||
|
||||
const blankTrailingOptions: {
|
||||
title: string;
|
||||
description: string;
|
||||
accessor: keyof typeof initialValues;
|
||||
accessor: keyof InitialValuesType;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Delete Blank Lines',
|
||||
|
|
@ -49,7 +53,7 @@ const blankTrailingOptions: {
|
|||
}
|
||||
];
|
||||
|
||||
const exampleCards = [
|
||||
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
{
|
||||
title: 'Merge a To-Do List',
|
||||
description:
|
||||
|
|
@ -62,10 +66,10 @@ feed the cat
|
|||
make dinner
|
||||
build a rocket ship and fly away`,
|
||||
sampleResult: `clean the house and go shopping and feed the cat and make dinner and build a rocket ship and fly away`,
|
||||
requiredOptions: {
|
||||
sampleOptions: {
|
||||
joinCharacter: 'and',
|
||||
deleteBlankLines: true,
|
||||
deleteTrailingSpaces: true
|
||||
deleteBlank: true,
|
||||
deleteTrailing: true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -78,10 +82,10 @@ processor
|
|||
mouse
|
||||
keyboard`,
|
||||
sampleResult: `computer, memory, processor, mouse, keyboard`,
|
||||
requiredOptions: {
|
||||
sampleOptions: {
|
||||
joinCharacter: ',',
|
||||
deleteBlankLines: false,
|
||||
deleteTrailingSpaces: false
|
||||
deleteBlank: false,
|
||||
deleteTrailing: false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -101,33 +105,51 @@ u
|
|||
s
|
||||
!`,
|
||||
sampleResult: `Textabulous!`,
|
||||
requiredOptions: {
|
||||
sampleOptions: {
|
||||
joinCharacter: '',
|
||||
deleteBlankLines: false,
|
||||
deleteTrailingSpaces: false
|
||||
deleteBlank: false,
|
||||
deleteTrailing: false
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default function JoinText() {
|
||||
export default function JoinText({ title }: ToolComponentProps) {
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
const compute = (optionsValues: typeof initialValues, input: any) => {
|
||||
const formRef = useRef<FormikProps<InitialValuesType>>(null);
|
||||
const compute = (optionsValues: InitialValuesType, input: any) => {
|
||||
const { joinCharacter, deleteBlank, deleteTrailing } = optionsValues;
|
||||
setResult(mergeText(input, deleteBlank, deleteTrailing, joinCharacter));
|
||||
};
|
||||
|
||||
function changeInputResult(input: string, result: string) {
|
||||
setInput(input);
|
||||
setResult(result);
|
||||
|
||||
const toolsElement = document.getElementById('tool');
|
||||
if (toolsElement) {
|
||||
toolsElement.scrollIntoView({ behavior: 'smooth' });
|
||||
const getGroups: GetGroupsType<InitialValuesType> = ({
|
||||
values,
|
||||
updateField
|
||||
}) => [
|
||||
{
|
||||
title: 'Text Merged Options',
|
||||
component: (
|
||||
<TextFieldWithDesc
|
||||
placeholder={mergeOptions.placeholder}
|
||||
value={values['joinCharacter']}
|
||||
onOwnChange={(value) => updateField(mergeOptions.accessor, value)}
|
||||
description={mergeOptions.description}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Blank Lines and Trailing Spaces',
|
||||
component: blankTrailingOptions.map((option) => (
|
||||
<CheckboxWithDesc
|
||||
key={option.accessor}
|
||||
title={option.title}
|
||||
checked={!!values[option.accessor]}
|
||||
onChange={(value) => updateField(option.accessor, value)}
|
||||
description={option.description}
|
||||
/>
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
return (
|
||||
<Box>
|
||||
<ToolInputAndResult
|
||||
|
|
@ -141,34 +163,9 @@ export default function JoinText() {
|
|||
result={<ToolTextResult title={'Joined Text'} value={result} />}
|
||||
/>
|
||||
<ToolOptions
|
||||
formRef={formRef}
|
||||
compute={compute}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Text Merged Options',
|
||||
component: (
|
||||
<TextFieldWithDesc
|
||||
placeholder={mergeOptions.placeholder}
|
||||
value={values['joinCharacter']}
|
||||
onOwnChange={(value) =>
|
||||
updateField(mergeOptions.accessor, value)
|
||||
}
|
||||
description={mergeOptions.description}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Blank Lines and Trailing Spaces',
|
||||
component: blankTrailingOptions.map((option) => (
|
||||
<CheckboxWithDesc
|
||||
key={option.accessor}
|
||||
title={option.title}
|
||||
checked={!!values[option.accessor]}
|
||||
onChange={(value) => updateField(option.accessor, value)}
|
||||
description={option.description}
|
||||
/>
|
||||
))
|
||||
}
|
||||
]}
|
||||
getGroups={getGroups}
|
||||
initialValues={initialValues}
|
||||
input={input}
|
||||
/>
|
||||
|
|
@ -177,13 +174,11 @@ export default function JoinText() {
|
|||
description="With this tool you can join parts of the text together. It takes a list of text values, separated by newlines, and merges them together. You can set the character that will be placed between the parts of the combined text. Also, you can ignore all empty lines and remove spaces and tabs at the end of all lines. Textabulous!"
|
||||
/>
|
||||
<Separator backgroundColor="#5581b5" margin="50px" />
|
||||
<Examples
|
||||
title="Text Joiner Examples"
|
||||
subtitle="Click to try!"
|
||||
exampleCards={exampleCards.map((card) => ({
|
||||
...card,
|
||||
changeInputResult
|
||||
}))}
|
||||
<ToolExamples
|
||||
title={title}
|
||||
exampleCards={exampleCards}
|
||||
getGroups={getGroups}
|
||||
formRef={formRef}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
import { Box } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import ToolTextInput from '@components/input/ToolTextInput';
|
||||
import ToolTextResult from '@components/result/ToolTextResult';
|
||||
import ToolOptions from '@components/options/ToolOptions';
|
||||
import ToolOptions, { GetGroupsType } 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';
|
||||
import ToolExamples, {
|
||||
CardExampleType
|
||||
} from '@components/examples/ToolExamples';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { FormikProps } from 'formik';
|
||||
|
||||
const initialValues = {
|
||||
splitSeparatorType: 'symbol' as SplitOperatorType,
|
||||
|
|
@ -73,10 +78,64 @@ const outputOptions: {
|
|||
}
|
||||
];
|
||||
|
||||
export default function SplitText() {
|
||||
const exampleCards: CardExampleType<typeof initialValues>[] = [
|
||||
{
|
||||
title: 'Split German Numbers',
|
||||
description:
|
||||
'In this example, we break the text into pieces by two characters – a comma and space. As a result, we get a column of numbers from 1 to 10 in German.',
|
||||
sampleText: `1 - eins, 2 - zwei, 3 - drei, 4 - vier, 5 - fünf, 6 - sechs, 7 - sieben, 8 - acht, 9 - neun, 10 - zehn`,
|
||||
sampleResult: `1 - eins
|
||||
2 - zwei
|
||||
3 - drei
|
||||
4 - vier
|
||||
5 - fünf
|
||||
6 - sechs
|
||||
7 - sieben
|
||||
8 - acht
|
||||
9 - neun
|
||||
10 - zehn`,
|
||||
sampleOptions: {
|
||||
...initialValues,
|
||||
symbolValue: ',',
|
||||
splitSeparatorType: 'symbol',
|
||||
outputSeparator: '\n'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Text Cleanup via a Regular Expression',
|
||||
description:
|
||||
'In this example, we use a super smart regular expression trick to clean-up the text. This regexp finds all non-alphabetic characters and splits the text into pieces by these non-alphabetic chars. As a result, we extract only those parts of the text that contain Latin letters and words.',
|
||||
sampleText: `Finding%№1.65*;?words()is'12#easy_`,
|
||||
sampleResult: `Finding
|
||||
words
|
||||
is
|
||||
easy`,
|
||||
sampleOptions: {
|
||||
...initialValues,
|
||||
regexValue: '[^a-zA-Z]+',
|
||||
splitSeparatorType: 'regex',
|
||||
outputSeparator: '\n'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Three-dot Output Separator',
|
||||
description:
|
||||
'This example splits the text by spaces and then places three dots between the words.',
|
||||
sampleText: `If you started with $0.01 and doubled your money every day, it would take 27 days to become a millionaire.`,
|
||||
sampleResult: `If...you...started...with...$0.01...and...doubled...your...money...every...day,...it...would...take...27...days...to...become...a...millionaire.!`,
|
||||
sampleOptions: {
|
||||
...initialValues,
|
||||
symbolValue: '',
|
||||
splitSeparatorType: 'symbol',
|
||||
outputSeparator: '...'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default function SplitText({ title }: ToolComponentProps) {
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
|
||||
const formRef = useRef<FormikProps<typeof initialValues>>(null);
|
||||
const computeExternal = (optionsValues: typeof initialValues, input: any) => {
|
||||
const {
|
||||
splitSeparatorType,
|
||||
|
|
@ -104,6 +163,37 @@ export default function SplitText() {
|
|||
);
|
||||
};
|
||||
|
||||
const getGroups: GetGroupsType<typeof initialValues> = ({
|
||||
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}
|
||||
/>
|
||||
))
|
||||
}
|
||||
];
|
||||
return (
|
||||
<Box>
|
||||
<ToolInputAndResult
|
||||
|
|
@ -112,37 +202,16 @@ export default function SplitText() {
|
|||
/>
|
||||
<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}
|
||||
/>
|
||||
))
|
||||
}
|
||||
]}
|
||||
getGroups={getGroups}
|
||||
initialValues={initialValues}
|
||||
input={input}
|
||||
/>
|
||||
<ToolExamples
|
||||
title={title}
|
||||
exampleCards={exampleCards}
|
||||
getGroups={getGroups}
|
||||
formRef={formRef}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { IconifyIcon } from '@iconify/react';
|
|||
|
||||
interface ToolOptions {
|
||||
path: string;
|
||||
component: LazyExoticComponent<JSXElementConstructor<NonNullable<unknown>>>;
|
||||
component: LazyExoticComponent<JSXElementConstructor<ToolComponentProps>>;
|
||||
keywords: string[];
|
||||
icon?: IconifyIcon | string;
|
||||
name: string;
|
||||
|
|
@ -25,6 +25,10 @@ export interface DefinedTool {
|
|||
component: () => JSX.Element;
|
||||
}
|
||||
|
||||
export interface ToolComponentProps {
|
||||
title?: any;
|
||||
}
|
||||
|
||||
export const defineTool = (
|
||||
basePath: ToolCategory,
|
||||
options: ToolOptions
|
||||
|
|
@ -55,7 +59,7 @@ export const defineTool = (
|
|||
icon={icon}
|
||||
type={basePath}
|
||||
>
|
||||
<Component />
|
||||
<Component title={name} />
|
||||
</ToolLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue