This commit is contained in:
parent
beba067305
commit
87a35aeffc
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
`
|
||||
|
@ -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 = () =>
|
||||
<ul className={style.grid}>
|
||||
{transforms.map((v, i) =>
|
||||
{transforms.map((transform, i) =>
|
||||
<motion.li
|
||||
key={i}
|
||||
initial={{ opacity: 0 }}
|
||||
@ -15,7 +15,7 @@ export const TransformGrid: FC = () =>
|
||||
transition={{ delay: i * 0.1 + 1.5 }} >
|
||||
|
||||
<TransformGridItem
|
||||
transform={v}/>
|
||||
transform={wrapTransform(transform)}/>
|
||||
|
||||
</motion.li>
|
||||
)}
|
||||
|
@ -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%;
|
||||
|
@ -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<TransformGridItemProp> = ({ transform }) => {
|
||||
const [value, setValue] = useRecoilState(EditorValueState)
|
||||
const [options, setOptions] = useState(new Map<string, TransformOption>())
|
||||
|
||||
const result = transform.fn(value, options)
|
||||
|
||||
const onCheckboxOptionChanged =
|
||||
(option: TransformCheckboxOption) =>
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
|
||||
options.set(option.key, {
|
||||
...option,
|
||||
value: event.target.checked
|
||||
})
|
||||
|
||||
setOptions(new Map(options))
|
||||
}
|
||||
|
||||
|
||||
const onTextboxOptionChanged =
|
||||
(option: TransformTextboxOption) =>
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
|
||||
options.set(option.key, {
|
||||
...option,
|
||||
value: event.target.value
|
||||
})
|
||||
|
||||
setOptions(new Map(options))
|
||||
}
|
||||
|
||||
const onForwardButtonPressed = () => {
|
||||
if (result.error)
|
||||
return
|
||||
|
||||
setValue(result.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={style.item}>
|
||||
<div className={style.toolbar}>
|
||||
<Button><<<</Button>
|
||||
<Button disabled={result.error} onClick={onForwardButtonPressed}>
|
||||
<<<
|
||||
</Button>
|
||||
|
||||
<h2 className={style.name}>{transform.name}</h2>
|
||||
|
||||
<div className={style.options}>
|
||||
<label className={style.optionItem}>hello: <Input type="checkbox" /></label>
|
||||
<label className={style.optionItem}>hello: <Input type="text" /></label>
|
||||
<label className={style.optionItem}>hello: <Input type="checkbox" /></label>
|
||||
<label className={style.optionItem}>hello: <Input type="checkbox" /></label>
|
||||
{transform.options
|
||||
?.filter((option) => option.type === 'CHECKBOX')
|
||||
.map((option, i) => (
|
||||
<label key={i} className={style.optionItem}>
|
||||
<p>{option.label ?? option.key}:</p>
|
||||
|
||||
<Input
|
||||
onChange={onCheckboxOptionChanged(option)}
|
||||
type="checkbox" />
|
||||
</label>))}
|
||||
|
||||
{transform.options
|
||||
?.filter((option) => option.type === 'TEXTBOX')
|
||||
.map((option, i) => (
|
||||
<label key={i} className={style.optionItem}>
|
||||
<p>{option.label ?? option.key}:</p>
|
||||
|
||||
<Input
|
||||
onChange={onTextboxOptionChanged(option)}
|
||||
type="checkbox" />
|
||||
</label>))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TextArea placeholder="(empty)" className={style.input} />
|
||||
<TextArea
|
||||
value={result.value}
|
||||
readOnly
|
||||
placeholder="(empty)"
|
||||
className={clsx(result.error && style.error)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { Transform } from "./Transform";
|
||||
|
||||
export const Base64DecodeTransform: Transform = {
|
||||
name: 'base64d',
|
||||
fn: () => 'Hello, world!'
|
||||
}
|
11
src/Transforms/Base64Transforms.ts
Normal file
11
src/Transforms/Base64Transforms.ts
Normal file
@ -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)
|
||||
}
|
@ -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, TransformOption>) => string
|
||||
options?: TransformOption[]
|
||||
}
|
||||
|
||||
export interface WrappedTransform extends Omit<Transform, 'fn'> {
|
||||
wrapped: true,
|
||||
fn: (value: string, options: Map<string, TransformOption>) => {
|
||||
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
|
||||
]
|
||||
|
29
src/Transforms/URITransforms.ts
Normal file
29
src/Transforms/URITransforms.ts
Normal file
@ -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'
|
||||
}]
|
||||
}
|
Loading…
Reference in New Issue
Block a user