diff --git a/package.json b/package.json
index 9ec6148..b25e9e7 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"dependencies": {
"@fontsource-variable/jetbrains-mono": "^5.1.1",
"@monaco-editor/react": "^4.6.0",
+ "clsx": "^2.1.1",
"framer-motion": "^11.11.10",
"normalize.css": "^8.0.1",
"react": "^18.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f67c85f..88a04db 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@monaco-editor/react':
specifier: ^4.6.0
version: 4.6.0(monaco-editor@0.52.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
framer-motion:
specifier: ^11.11.10
version: 11.11.10(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -702,6 +705,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -1859,6 +1866,8 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ clsx@2.1.1: {}
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
diff --git a/src/Components/Button.tsx b/src/Components/Button.tsx
index 09efb0f..e3c4834 100644
--- a/src/Components/Button.tsx
+++ b/src/Components/Button.tsx
@@ -8,11 +8,14 @@ export const Button = styled.button`
background-color: #ff1696;
color: white;
+ transition: background-color 0.25s ease-out;
+
&:hover {
background-color: #da1c9b;
}
- &:placeholder {
- color: gray;
+ &:disabled {
+ background-color: gray;
+ cursor: inherit;
}
`
diff --git a/src/TransformGrid/index.tsx b/src/TransformGrid/index.tsx
index d82ae65..b4c413b 100644
--- a/src/TransformGrid/index.tsx
+++ b/src/TransformGrid/index.tsx
@@ -3,11 +3,11 @@ import { motion } from "framer-motion";
import style from './style.module.scss'
import { TransformGridItem } from "../TransformGridItem";
-import { transforms } from "../Transforms/Transform";
+import { transforms, wrapTransform } from "../Transforms/Transform";
export const TransformGrid: FC = () =>
- {transforms.map((v, i) =>
+ {transforms.map((transform, i) =>
transition={{ delay: i * 0.1 + 1.5 }} >
+ transform={wrapTransform(transform)}/>
)}
diff --git a/src/TransformGrid/style.module.scss b/src/TransformGrid/style.module.scss
index e4706ca..958b3ab 100644
--- a/src/TransformGrid/style.module.scss
+++ b/src/TransformGrid/style.module.scss
@@ -1,7 +1,9 @@
.grid {
padding: 10px;
+
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ align-content: flex-start;
gap: 10px;
height: 100%;
diff --git a/src/TransformGridItem/index.tsx b/src/TransformGridItem/index.tsx
index 8d1795c..8e25b47 100644
--- a/src/TransformGridItem/index.tsx
+++ b/src/TransformGridItem/index.tsx
@@ -1,30 +1,94 @@
-import { FC } from "react";
+import { ChangeEvent, FC, useState } from "react";
import style from './style.module.scss'
import { Button } from "../Components/Button";
import { Input } from "../Components/Input";
import { TextArea } from "../Components/TextArea";
-import { Transform } from "../Transforms/Transform";
+import { TransformCheckboxOption, TransformOption, TransformTextboxOption, WrappedTransform } from "../Transforms/Transform";
+import { useRecoilState } from "recoil";
+import { EditorValueState } from "../GlobalStates/EditorValueState";
+import clsx from "clsx";
interface TransformGridItemProp {
- transform: Transform
+ transform: WrappedTransform
}
export const TransformGridItem: FC = ({ transform }) => {
+ const [value, setValue] = useRecoilState(EditorValueState)
+ const [options, setOptions] = useState(new Map())
+
+ const result = transform.fn(value, options)
+
+ const onCheckboxOptionChanged =
+ (option: TransformCheckboxOption) =>
+ (event: ChangeEvent) => {
+
+ options.set(option.key, {
+ ...option,
+ value: event.target.checked
+ })
+
+ setOptions(new Map(options))
+ }
+
+
+ const onTextboxOptionChanged =
+ (option: TransformTextboxOption) =>
+ (event: ChangeEvent) => {
+
+ options.set(option.key, {
+ ...option,
+ value: event.target.value
+ })
+
+ setOptions(new Map(options))
+ }
+
+ const onForwardButtonPressed = () => {
+ if (result.error)
+ return
+
+ setValue(result.value)
+ }
+
return (
-
+
+
{transform.name}
-
+
)
}
diff --git a/src/TransformGridItem/style.module.scss b/src/TransformGridItem/style.module.scss
index 4208148..433ce2d 100644
--- a/src/TransformGridItem/style.module.scss
+++ b/src/TransformGridItem/style.module.scss
@@ -4,7 +4,7 @@
display: flex;
padding: 10px;
flex-direction: column;
- min-height: 300px;
+ min-height: 250px;
.toolbar {
display: flex;
@@ -16,7 +16,7 @@
.name {
font-size: inherit;
- padding: 14px 0px;
+ padding: 14px 6px;
}
.options {
@@ -32,7 +32,12 @@
.optionItem {
display: flex;
+ gap: 6px;
}
}
}
+
+ .error {
+ color: #ffcaca;
+ }
}
diff --git a/src/Transforms/Base64DecodeTransform.ts b/src/Transforms/Base64DecodeTransform.ts
deleted file mode 100644
index dc89f6a..0000000
--- a/src/Transforms/Base64DecodeTransform.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { Transform } from "./Transform";
-
-export const Base64DecodeTransform: Transform = {
- name: 'base64d',
- fn: () => 'Hello, world!'
-}
diff --git a/src/Transforms/Base64Transforms.ts b/src/Transforms/Base64Transforms.ts
new file mode 100644
index 0000000..4b88fb9
--- /dev/null
+++ b/src/Transforms/Base64Transforms.ts
@@ -0,0 +1,11 @@
+import { Transform } from "./Transform";
+
+export const Base64DecodeTransform: Transform = {
+ name: 'base64d',
+ fn: (v) => btoa(v)
+}
+
+export const Base64EncodeTransform: Transform = {
+ name: 'base64e',
+ fn: (v) => atob(v)
+}
diff --git a/src/Transforms/Transform.ts b/src/Transforms/Transform.ts
index 30c15ed..85f4f29 100644
--- a/src/Transforms/Transform.ts
+++ b/src/Transforms/Transform.ts
@@ -1,12 +1,59 @@
-import { Base64DecodeTransform } from "./Base64DecodeTransform"
+import { Base64DecodeTransform, Base64EncodeTransform } from "./Base64Transforms"
+import { URIDecodeTransform, URIEncodeTransform } from "./URITransforms"
+
+export interface TransformCheckboxOption {
+ type: 'CHECKBOX',
+ key: string,
+ label?: string,
+ value?: boolean
+}
+
+
+export interface TransformTextboxOption {
+ type: 'TEXTBOX',
+ key: string,
+ label?: string,
+ value?: string
+}
+
+export type TransformOption =
+ TransformCheckboxOption | TransformTextboxOption
export interface Transform {
name: string
- fn: () => string
+ fn: (value: string, options: Map) => string
+ options?: TransformOption[]
}
+export interface WrappedTransform extends Omit {
+ wrapped: true,
+ fn: (value: string, options: Map) => {
+ error: boolean,
+ value: string
+ }
+}
+
+export const wrapTransform = (transform: Transform): WrappedTransform => ({
+ ...transform,
+ fn: (...args) => {
+ try {
+ return {
+ error: false,
+ value: transform.fn(...args)
+ }
+ } catch (err: any) {
+ return {
+ error: true,
+ value: err.toString()
+ }
+ }
+ },
+ wrapped: true
+})
+
export const transforms: Transform[] = [
Base64DecodeTransform,
- Base64DecodeTransform,
- Base64DecodeTransform,
+ Base64EncodeTransform,
+ URIDecodeTransform,
+ URIEncodeTransform
]
diff --git a/src/Transforms/URITransforms.ts b/src/Transforms/URITransforms.ts
new file mode 100644
index 0000000..a09340a
--- /dev/null
+++ b/src/Transforms/URITransforms.ts
@@ -0,0 +1,29 @@
+import { Transform } from "./Transform";
+
+export const URIDecodeTransform: Transform = {
+ name: 'urid',
+
+ fn: (v, o) =>
+ o.get('cmp')?.value === true
+ ? decodeURIComponent(v)
+ : decodeURI(v),
+
+ options: [{
+ type: 'CHECKBOX',
+ key: 'cmp'
+ }]
+}
+
+export const URIEncodeTransform: Transform = {
+ name: 'urie',
+
+ fn: (v, o) =>
+ o.get('cmp')?.value === true
+ ? encodeURIComponent(v)
+ : encodeURI(v),
+
+ options: [{
+ type: 'CHECKBOX',
+ key: 'cmp'
+ }]
+}