Skip to content

Commit 835307d

Browse files
committed
feat: add random string generator
1 parent 3432c19 commit 835307d

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import {
2+
Flex,
3+
Box,
4+
VStack,
5+
HStack,
6+
Text,
7+
NumberInput,
8+
NumberInputField,
9+
NumberInputStepper,
10+
NumberIncrementStepper,
11+
NumberDecrementStepper,
12+
FormControl,
13+
FormLabel,
14+
useClipboard,
15+
Checkbox,
16+
CheckboxGroup,
17+
} from "@chakra-ui/react";
18+
import { Controlled } from "react-codemirror2";
19+
import { FiCopy } from "react-icons/fi";
20+
21+
import { useState, useEffect } from "react";
22+
import "codemirror/lib/codemirror.css";
23+
import "codemirror/theme/material.css";
24+
25+
import CopyIconButton from "@/components/Common/CopyIconButton";
26+
import range from "@/utils/range";
27+
28+
const defaultLength = 13;
29+
const defaultRow = 5;
30+
const maxLength = 120;
31+
32+
const misreadRegex = /[iloqILOQ019!]/g;
33+
const poolNumber = "0123456789";
34+
const poolLower = "abcdefghijklmnopqrstuvwxyz";
35+
const poolUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
36+
const poolSpecial = "!@#$%^&*";
37+
38+
const RandomString = () => {
39+
const [output, setOutput] = useState<string>("");
40+
const [optLength, setOptLength] = useState<number>(defaultLength);
41+
const [optRow, setOptRow] = useState<number>(defaultRow);
42+
const [flags, setFlags] = useState<string[]>([
43+
"number",
44+
"upper",
45+
"lower",
46+
"special",
47+
]);
48+
49+
const { hasCopied, onCopy } = useClipboard(output);
50+
51+
const handleOptLengthChange = (valueAsString: string, value: number) =>
52+
setOptLength(value);
53+
const handleOptRowChange = (valueAsString: string, value: number) =>
54+
setOptRow(value);
55+
56+
useEffect(() => {
57+
const f = async () => {
58+
if (flags.length === 0) {
59+
return "";
60+
}
61+
// create charactor pool
62+
let pool = flags.reduce((p: string, flag: string) => {
63+
switch (flag) {
64+
case "number":
65+
return p + poolNumber;
66+
case "upper":
67+
return p + poolUpper;
68+
case "lower":
69+
return p + poolLower;
70+
case "special":
71+
return p + poolSpecial;
72+
default:
73+
return p;
74+
}
75+
}, "");
76+
if (flags.includes("avoid")) {
77+
pool = pool.replace(misreadRegex, "");
78+
}
79+
const len = pool.length;
80+
const out = range(1, optRow)
81+
.map(() => {
82+
return range(1, optLength)
83+
.map(() => {
84+
// pick random char from pool
85+
return pool.charAt(Math.floor(Math.random() * len));
86+
})
87+
.join("");
88+
})
89+
.join("\n");
90+
setOutput(out);
91+
};
92+
f();
93+
}, [optLength, optRow, flags]);
94+
95+
return (
96+
<VStack spacing="5">
97+
{/* Output Box */}
98+
<Box flexGrow="1" w="100%">
99+
<Flex>
100+
<Text fontSize="lg" ml="45%" marginLeft={"10px"}>
101+
Output
102+
</Text>
103+
<HStack spacing="2" marginLeft={"auto"}>
104+
<CopyIconButton
105+
ariaLabel="Copy"
106+
icon={<FiCopy />}
107+
onCopy={onCopy}
108+
hasCopied={hasCopied}
109+
/>
110+
</HStack>
111+
</Flex>
112+
<Controlled
113+
onBeforeChange={() => {}}
114+
value={output}
115+
options={{
116+
lineNumbers: true,
117+
readOnly: true,
118+
mode: "text",
119+
theme: "material",
120+
}}
121+
/>
122+
</Box>
123+
{/* Configuration part */}
124+
<Flex w="100%">
125+
<VStack spacing="3">
126+
<FormControl as="fieldset">
127+
<FormLabel as="legend">Length</FormLabel>
128+
<NumberInput
129+
value={optLength}
130+
min={1}
131+
max={maxLength}
132+
onChange={handleOptLengthChange}
133+
>
134+
<NumberInputField />
135+
<NumberInputStepper>
136+
<NumberIncrementStepper />
137+
<NumberDecrementStepper />
138+
</NumberInputStepper>
139+
</NumberInput>
140+
<FormLabel as="legend">Row</FormLabel>
141+
<NumberInput
142+
value={optRow}
143+
min={1}
144+
max={maxLength}
145+
onChange={handleOptRowChange}
146+
>
147+
<NumberInputField />
148+
<NumberInputStepper>
149+
<NumberIncrementStepper />
150+
<NumberDecrementStepper />
151+
</NumberInputStepper>
152+
</NumberInput>
153+
{/* charactor settings */}
154+
<FormLabel as="legend">Charactors</FormLabel>
155+
<VStack spacing={2} align={"left"}>
156+
<CheckboxGroup
157+
defaultValue={flags}
158+
onChange={value => setFlags(value as string[])}
159+
>
160+
<Checkbox defaultIsChecked value="number">
161+
0-9
162+
</Checkbox>
163+
<Checkbox defaultIsChecked value="lower">
164+
a-z
165+
</Checkbox>
166+
<Checkbox defaultIsChecked value="upper">
167+
A-Z
168+
</Checkbox>
169+
<Checkbox defaultIsChecked value="special">
170+
!@#$%^&*
171+
</Checkbox>
172+
<Checkbox value="avoid">
173+
Avoid misreading chars (iloqILOQ019!)
174+
</Checkbox>
175+
</CheckboxGroup>
176+
</VStack>
177+
</FormControl>
178+
</VStack>
179+
</Flex>
180+
</VStack>
181+
);
182+
};
183+
184+
export default RandomString;

data/categories.ts

+8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import ColorContrastChecker from "@/data/tools/color/colorContrastChecker";
99
import Base64EncoderDecoder from "@/data/tools/converters/base64EncoderDecoder";
1010
import HtmlToJsx from "@/data/tools/converters/HtmlToJsx";
1111
import JsonYaml from "@/data/tools/converters/JsonYaml";
12+
import RandomString from "@/data/tools/generators/randomString";
1213

1314
const categories: Category[] = [
1415
{
@@ -46,6 +47,13 @@ const categories: Category[] = [
4647
slug: "converters",
4748
directory: "Converters",
4849
},
50+
{
51+
id: 6,
52+
name: "Generators",
53+
children: [RandomString],
54+
slug: "generators",
55+
directory: "Generators",
56+
},
4957
];
5058

5159
export default categories;

data/tools/generators/randomString.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Tool } from "@/data/types";
2+
3+
const RandomString: Tool = {
4+
id: 1,
5+
name: "Random String",
6+
description: "Generate random string",
7+
componentFileName: "RandomString",
8+
slug: "random-string",
9+
categorySlug: "generators",
10+
acceptedDataTypes: ["text"],
11+
};
12+
13+
export default RandomString;

utils/range.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Generate array of sequence.
3+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#sequence_generator_range
4+
* @param {number} start
5+
* @param {number} stop
6+
* @param {number} step default = 1
7+
* @return {number[]} generated numbers
8+
*/
9+
const range = (start: number, stop: number, step: number = 1) =>
10+
Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
11+
12+
export default range;

0 commit comments

Comments
 (0)