single block editor
This commit is contained in:
parent
be59f5832a
commit
91b9c8018b
|
|
@ -10,6 +10,10 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@uiw/codemirror-extensions-color": "^4.23.0",
|
||||||
|
"@uiw/codemirror-extensions-langs": "^4.23.0",
|
||||||
|
"@uiw/react-codemirror": "^4.23.0",
|
||||||
|
"@uiw/react-json-view": "1.12.0",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"match-sorter": "^6.3.4",
|
"match-sorter": "^6.3.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|
@ -18,9 +22,12 @@
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"sort-by": "^1.2.0",
|
"sort-by": "^1.2.0",
|
||||||
"sucrase": "^3.31.0",
|
"sucrase": "^3.31.0",
|
||||||
|
"terser": "^5.31.3",
|
||||||
"use-editable": "^2.3.3"
|
"use-editable": "^2.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.17.0",
|
||||||
|
"@codemirror/state": "^6.4.1",
|
||||||
"@types/react": "^18.2.0",
|
"@types/react": "^18.2.0",
|
||||||
"@types/react-dom": "^18.2.0",
|
"@types/react-dom": "^18.2.0",
|
||||||
"@types/react-transition-group": "^4.4.10",
|
"@types/react-transition-group": "^4.4.10",
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,5 @@
|
||||||
|
|
||||||
.preview-box > div *:not(div),
|
.preview-box > div *:not(div),
|
||||||
.preview-box > div div *:not(div) {
|
.preview-box > div div *:not(div) {
|
||||||
cursor: default;
|
/* cursor: initial; */
|
||||||
}
|
}
|
||||||
|
|
@ -5,32 +5,42 @@ import evalCode from "../../utils/transpile/evalCode";
|
||||||
import transform from "../../utils/transpile/transform";
|
import transform from "../../utils/transpile/transform";
|
||||||
import { ThemeProvider } from "../../components/Theme/ThemePrivider";
|
import { ThemeProvider } from "../../components/Theme/ThemePrivider";
|
||||||
import errorBoundary from "../../utils/transpile/errorBoundary";
|
import errorBoundary from "../../utils/transpile/errorBoundary";
|
||||||
import "./style.css"
|
import "./style.css";
|
||||||
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||||
|
import minifyCode from "../../utils/minify";
|
||||||
|
// import ReactDOM from "react-dom/client";
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
// 定义成一个数组便于下面操作,实际上只有一个元素
|
// 定义成一个数组便于下面操作,实际上只有一个元素
|
||||||
const [results, setResults] = useState<ComponentType[]>([]);
|
const [results, setResults] = useState<ComponentType[]>([]);
|
||||||
|
|
||||||
const errorCallBack = (error: Error) => {
|
const errorCallBack = (error: Error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
const transforms: Transform[] = ["jsx", "typescript", "imports"];
|
const transforms: Transform[] = ["jsx", "typescript", "imports"];
|
||||||
|
|
||||||
const render = (element: ComponentType) => {
|
const render = (element: ComponentType) => {
|
||||||
if (typeof element === "undefined") {
|
const Component = errorBoundary(element, errorCallBack);
|
||||||
errorCallBack(new SyntaxError("`render` must be called with valid JSX."));
|
if (typeof element === "undefined") {
|
||||||
} else {
|
errorCallBack(new SyntaxError("`render` must be called with valid JSX."));
|
||||||
setResults([errorBoundary(element, errorCallBack)]);
|
} else {
|
||||||
}
|
setResults([Component]);
|
||||||
};
|
}
|
||||||
|
// ReactDOM.createRoot(document.querySelector(".test-app") as HTMLElement).render(
|
||||||
// 开发环境下,useEffect会执行两次,模拟装载和卸载组件,生产环境没事。
|
// <React.StrictMode>
|
||||||
useEffect(() => {
|
// < Component />
|
||||||
const allCpns = functionBlockList.map(item => item.code).join("\n");
|
// </React.StrictMode>
|
||||||
const allPage = functionBlockList.map((item,index) => `<${item.cpnName} { ...props[${index}] }/>`).join("\n");
|
// );
|
||||||
const allCode = `
|
};
|
||||||
|
|
||||||
|
// 开发环境下,useEffect会执行两次,模拟装载和卸载组件,生产环境没事。
|
||||||
|
useEffect(() => {
|
||||||
|
const allCpns = functionBlockList.map((item) => item.code).join("\n");
|
||||||
|
const allPage = functionBlockList
|
||||||
|
.map((item, index) => `<${item.cpnName} { ...props[${index}] }/>`)
|
||||||
|
.join("\n");
|
||||||
|
const allCode = `
|
||||||
${allCpns}
|
${allCpns}
|
||||||
|
|
||||||
const ApplicationContext = () => {
|
const ApplicationContext = () => {
|
||||||
|
|
@ -43,29 +53,30 @@ export const App = () => {
|
||||||
|
|
||||||
render(ApplicationContext)
|
render(ApplicationContext)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const propsList = functionBlockList.map(item => item.props);
|
|
||||||
|
|
||||||
const allTransformed = transform({ transforms })(allCode);
|
const propsList = functionBlockList.map((item) => item.props);
|
||||||
|
|
||||||
setTimeout(() => {
|
const allTransformed = transform({ transforms })(allCode);
|
||||||
evalCode(allTransformed, { React, render, props: propsList });
|
|
||||||
},1000)
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
setTimeout(async () => {
|
||||||
<ThemeProvider>
|
const minifiedCode = await minifyCode(allTransformed);
|
||||||
<div className="application">
|
|
||||||
<TransitionGroup>
|
evalCode(minifiedCode, { React, render, props: propsList });
|
||||||
{results.map((Cpn, index) => (
|
}, 1000);
|
||||||
<CSSTransition key={index} timeout={500} classNames="fade"
|
}, []);
|
||||||
>
|
|
||||||
<Cpn />
|
return (
|
||||||
</CSSTransition>
|
<ThemeProvider>
|
||||||
))}
|
<div className="application">
|
||||||
</TransitionGroup>
|
<TransitionGroup>
|
||||||
{results.length === 0 && <div className="loading">loading...</div>}
|
{results.map((Cpn, index) => (
|
||||||
</div>
|
<CSSTransition key={index} timeout={500} classNames="fade">
|
||||||
</ThemeProvider>
|
<Cpn />
|
||||||
);
|
</CSSTransition>
|
||||||
|
))}
|
||||||
|
</TransitionGroup>
|
||||||
|
{results.length === 0 && <div className="loading">loading...</div>}
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { Extension } from '@codemirror/state';
|
||||||
|
import { CompletionContext, autocompletion, Completion } from '@codemirror/autocomplete';
|
||||||
|
|
||||||
|
const colorMap = [
|
||||||
|
{ label: "--primary-7", type: "variable", info: "(点击(click))" },
|
||||||
|
{ label: "--primary-6", type: "variable", info: "(常规)" },
|
||||||
|
{ label: "--primary-5", type: "variable", info: "(悬浮(hover))" },
|
||||||
|
{ label: "--primary-4", type: "variable", info: "(特殊场景)" },
|
||||||
|
{ label: "--primary-3", type: "variable", info: "(一般禁用)" },
|
||||||
|
{ label: "--primary-2", type: "variable", info: "(文字禁用)" },
|
||||||
|
{ label: "--primary-1", type: "variable", info: "(浅色/白底悬浮)" },
|
||||||
|
{ label: "--success-7", type: "variable", info: "(点击(click))" },
|
||||||
|
{ label: "--success-6", type: "variable", info: "(常规)" },
|
||||||
|
{ label: "--success-5", type: "variable", info: "(悬浮(hover))" },
|
||||||
|
{ label: "--success-4", type: "variable", info: "(特殊场景)" },
|
||||||
|
{ label: "--success-3", type: "variable", info: "(一般禁用)" },
|
||||||
|
{ label: "--success-2", type: "variable", info: "(文字禁用)" },
|
||||||
|
{ label: "--success-1", type: "variable", info: "(浅色/白底悬浮)" },
|
||||||
|
{ label: "--warning-7", type: "variable", info: "(点击(click))" },
|
||||||
|
{ label: "--warning-6", type: "variable", info: "(常规)" },
|
||||||
|
{ label: "--warning-5", type: "variable", info: "(悬浮(hover))" },
|
||||||
|
{ label: "--warning-4", type: "variable", info: "(特殊场景)" },
|
||||||
|
{ label: "--warning-3", type: "variable", info: "(一般禁用)" },
|
||||||
|
{ label: "--warning-2", type: "variable", info: "(文字禁用)" },
|
||||||
|
{ label: "--warning-1", type: "variable", info: "(浅色/白底悬浮)" },
|
||||||
|
{ label: "--danger-7", type: "variable", info: "(点击(click))" },
|
||||||
|
{ label: "--danger-6", type: "variable", info: "(常规)" },
|
||||||
|
{ label: "--danger-5", type: "variable", info: "(悬浮(hover))" },
|
||||||
|
{ label: "--danger-4", type: "variable", info: "(特殊场景)" },
|
||||||
|
{ label: "--danger-3", type: "variable", info: "(一般禁用)" },
|
||||||
|
{ label: "--danger-2", type: "variable", info: "(文字禁用)" },
|
||||||
|
{ label: "--danger-1", type: "variable", info: "(浅色/白底悬浮)" },
|
||||||
|
{ label: "--link-7", type: "variable", info: "(点击(click))" },
|
||||||
|
{ label: "--link-6", type: "variable", info: "(常规)" },
|
||||||
|
{ label: "--link-5", type: "variable", info: "(悬浮(hover))" },
|
||||||
|
{ label: "--link-4", type: "variable", info: "(特殊场景)" },
|
||||||
|
{ label: "--link-3", type: "variable", info: "(一般禁用)" },
|
||||||
|
{ label: "--link-2", type: "variable", info: "(文字禁用)" },
|
||||||
|
{ label: "--link-1", type: "variable", info: "(浅色/白底悬浮)" },
|
||||||
|
{ label: "--data-1", type: "variable", info: "" },
|
||||||
|
{ label: "--data-2", type: "variable", info: "" },
|
||||||
|
{ label: "--data-3", type: "variable", info: "" },
|
||||||
|
{ label: "--data-4", type: "variable", info: "" },
|
||||||
|
{ label: "--data-5", type: "variable", info: "" },
|
||||||
|
{ label: "--data-6", type: "variable", info: "" },
|
||||||
|
{ label: "--data-7", type: "variable", info: "" },
|
||||||
|
{ label: "--data-8", type: "variable", info: "" },
|
||||||
|
{ label: "--data-9", type: "variable", info: "" },
|
||||||
|
{ label: "--data-10", type: "variable", info: "" },
|
||||||
|
{ label: "--data-11", type: "variable", info: "" },
|
||||||
|
{ label: "--data-12", type: "variable", info: "" },
|
||||||
|
{ label: "--data-13", type: "variable", info: "" },
|
||||||
|
{ label: "--data-14", type: "variable", info: "" },
|
||||||
|
{ label: "--data-15", type: "variable", info: "" },
|
||||||
|
{ label: "--data-16", type: "variable", info: "" },
|
||||||
|
{ label: "--data-17", type: "variable", info: "" },
|
||||||
|
{ label: "--data-18", type: "variable", info: "" },
|
||||||
|
{ label: "--data-19", type: "variable", info: "" },
|
||||||
|
{ label: "--data-20", type: "variable", info: "" },
|
||||||
|
{ label: "--color-border-1", type: "variable", info: "(浅色)" },
|
||||||
|
{ label: "--color-border-2", type: "variable", info: "(一般)" },
|
||||||
|
{ label: "--color-border-3", type: "variable", info: "(深/悬浮)" },
|
||||||
|
{ label: "--color-border-4", type: "variable", info: "(重/按钮描边)" },
|
||||||
|
{ label: "--color-fill-1", type: "variable", info: "(浅/禁用)" },
|
||||||
|
{ label: "--color-fill-2", type: "variable", info: "(常规/白底悬浮)" },
|
||||||
|
{ label: "--color-fill-3", type: "variable", info: "(深/灰底悬浮)" },
|
||||||
|
{ label: "--color-fill-4", type: "variable", info: "(重/特殊场景)" },
|
||||||
|
{ label: "--color-text-1", type: "variable", info: "(强调/正文标题)" },
|
||||||
|
{ label: "--color-text-2", type: "variable", info: "(次强调/正文标题)" },
|
||||||
|
{ label: "--color-text-3", type: "variable", info: "(次要信息)" },
|
||||||
|
{ label: "--color-text-4", type: "variable", info: "(置灰信息)" },
|
||||||
|
{ label: "--color-bg-1", type: "variable", info: "(整体背景色)" },
|
||||||
|
{ label: "--color-bg-2", type: "variable", info: "(一级容器背景)" },
|
||||||
|
{ label: "--color-bg-3", type: "variable", info: "(二级容器背景)" },
|
||||||
|
{ label: "--color-bg-4", type: "variable", info: "(三级容器背景)" },
|
||||||
|
{ label: "--color-bg-5", type: "variable", info: "(下拉弹出框、Tooltip 背景颜色)" },
|
||||||
|
{ label: "--color-bg-white", type: "variable", info: "(白色背景)" }
|
||||||
|
];
|
||||||
|
|
||||||
|
export function colorCompletions(data: Completion[] = []): Extension {
|
||||||
|
return autocompletion({
|
||||||
|
override: [
|
||||||
|
(context: CompletionContext) => {
|
||||||
|
let word = context.matchBefore(/--\w+/g);
|
||||||
|
if (!word) return null;
|
||||||
|
if (word && word.from == word.to && !context.explicit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
from: word?.from!,
|
||||||
|
options: [...data],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const plugin = colorCompletions(colorMap);
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
import React from "react";
|
||||||
|
import CodeMirror, { basicSetup } from "@uiw/react-codemirror";
|
||||||
|
import { loadLanguage, langs } from "@uiw/codemirror-extensions-langs";
|
||||||
|
import LiveProvider from "../../components/Live/LiveProvider";
|
||||||
|
import LivePreview from "../../components/Live/LivePreview";
|
||||||
|
import LiveError from "../../components/Live/LiveError";
|
||||||
|
import { color, colorView, colorTheme } from "@uiw/codemirror-extensions-color";
|
||||||
|
import JsonViewEditor from "@uiw/react-json-view/editor";
|
||||||
|
import { ThemeProvider } from "../../components/Theme/ThemePrivider";
|
||||||
|
import { plugin as colorCom } from "./completions";
|
||||||
|
|
||||||
|
loadLanguage("tsx");
|
||||||
|
const extensions = [color, langs.tsx(), colorView(false), colorTheme, colorCom];
|
||||||
|
|
||||||
|
export const Creation = () => {
|
||||||
|
// useEffect 拦截ctrls等操作,避免浏览器默认行为
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.ctrlKey && event.key === "s") {
|
||||||
|
event.preventDefault();
|
||||||
|
// 弹出已经保存的提示
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", handleKeyDown);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [componentName, setComponentName] = React.useState("Banner");
|
||||||
|
|
||||||
|
const [componentsProps, setComponentsProps] = React.useState({
|
||||||
|
title: "AI智能建站助手",
|
||||||
|
subTitle: "让我们帮助您快速构建和优化您的网站",
|
||||||
|
btnText: "立即开始",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [newPropKey, setNewPropKey] = React.useState("");
|
||||||
|
|
||||||
|
const handleAddProp = () => {
|
||||||
|
if (newPropKey.trim() !== "") {
|
||||||
|
setComponentsProps((prevProps) => ({
|
||||||
|
...prevProps,
|
||||||
|
[newPropKey]: "",
|
||||||
|
}));
|
||||||
|
setNewPropKey("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const [code, setCode] = React.useState(`
|
||||||
|
const Banner = (props: {
|
||||||
|
title: string
|
||||||
|
subTitle: string
|
||||||
|
btnText: string
|
||||||
|
}) => {
|
||||||
|
function handleClick() {
|
||||||
|
console.log("Hi there!");
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="bg-primary-1 py-16 px-8">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<h1 className="text-primary text-5xl md:text-7xl font-extrabold mb-4">
|
||||||
|
{props.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-primary text-xl md:text-2xl mb-8">
|
||||||
|
{props.subTitle}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={handleClick}
|
||||||
|
className="font-bold py-3 px-6 rounded-full shadow-lg transition duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
{props.btnText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
`);
|
||||||
|
|
||||||
|
const renderCode = (code: string) => {
|
||||||
|
return `
|
||||||
|
${code}
|
||||||
|
render(${componentName});
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = React.useCallback((value: string) => {
|
||||||
|
setCode(value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleComponentNameChange = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
setComponentName(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block mb-2 font-bold text-lg border-primary-1">
|
||||||
|
组件名称
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={componentName}
|
||||||
|
onChange={handleComponentNameChange}
|
||||||
|
className="border border-text-1 rounded p-2 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<LiveProvider code={renderCode(code)} noInline>
|
||||||
|
<div className="h-full grid lg:grid-cols-2 gap-4 border p-4 rounded shadow">
|
||||||
|
<div className="border rounded p-2">
|
||||||
|
<CodeMirror
|
||||||
|
value={code}
|
||||||
|
height="100%"
|
||||||
|
extensions={[
|
||||||
|
...extensions,
|
||||||
|
basicSetup({
|
||||||
|
autocompletion: true,
|
||||||
|
lintKeymap: true,
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
onChange={onChange}
|
||||||
|
className="font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="border rounded p-2">
|
||||||
|
<LivePreview {...componentsProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<LiveError className="text-danger-6 mt-2 p-2 rounded" />
|
||||||
|
</LiveProvider>
|
||||||
|
<div className="mt-4 p-4 border rounded shadow w-2/4">
|
||||||
|
<h2 className="font-bold text-lg mb-2">Props 编辑器</h2>
|
||||||
|
<div className="props-edit">
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block mb-2 font-bold">新属性名</label>
|
||||||
|
<div className="flex">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={newPropKey}
|
||||||
|
onChange={(e) => setNewPropKey(e.target.value)}
|
||||||
|
className="border border-text-1 rounded p-2 flex-grow"
|
||||||
|
/>
|
||||||
|
<button onClick={handleAddProp} className="p-2 rounded ml-2">
|
||||||
|
添加
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<JsonViewEditor
|
||||||
|
value={componentsProps}
|
||||||
|
keyName="props"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--w-rjv-background-color": "#ffffff",
|
||||||
|
"--w-rjv-border-left": "1px dashed #ebebeb",
|
||||||
|
"--w-rjv-update-color": "#ff6ffd",
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
onEdit={(opts) => {
|
||||||
|
const updateNestedProp = (
|
||||||
|
obj: Record<string, any>,
|
||||||
|
path: string[],
|
||||||
|
value: unknown
|
||||||
|
) => {
|
||||||
|
if (path.length === 1) {
|
||||||
|
obj[path[0]] = value;
|
||||||
|
} else {
|
||||||
|
if (!obj[path[0]]) {
|
||||||
|
obj[path[0]] = {};
|
||||||
|
}
|
||||||
|
updateNestedProp(obj[path[0]], path.slice(1), value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const updatedProps = { ...componentsProps };
|
||||||
|
updateNestedProp(
|
||||||
|
updatedProps,
|
||||||
|
(opts as any).namespace,
|
||||||
|
opts.value
|
||||||
|
);
|
||||||
|
setComponentsProps(updatedProps);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -7,7 +7,7 @@ import { useState } from "react";
|
||||||
import { functionBlockList } from "../../data/functionBlocks";
|
import { functionBlockList } from "../../data/functionBlocks";
|
||||||
import ClickContextProvider from "../../components/Selected/ClickContext";
|
import ClickContextProvider from "../../components/Selected/ClickContext";
|
||||||
|
|
||||||
const code = (func: string,cpnName:string) => `
|
const code = (func: string, cpnName: string) => `
|
||||||
${func}
|
${func}
|
||||||
render(${cpnName})
|
render(${cpnName})
|
||||||
`;
|
`;
|
||||||
|
|
@ -112,7 +112,11 @@ export const Editor = () => {
|
||||||
)}
|
)}
|
||||||
{functionBlockList.map((func, index) => {
|
{functionBlockList.map((func, index) => {
|
||||||
return (
|
return (
|
||||||
<LiveProvider code={code(func.code, func.cpnName)} noInline key={index}>
|
<LiveProvider
|
||||||
|
code={code(func.code, func.cpnName)}
|
||||||
|
noInline
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
<ClickContextProvider
|
<ClickContextProvider
|
||||||
meun={[
|
meun={[
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
import {
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
createBrowserRouter,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import { Editor } from "../pages/editor";
|
import { Editor } from "../pages/editor";
|
||||||
import { App } from "../pages/app";
|
import { App } from "../pages/app";
|
||||||
|
import { Creation } from "../pages/creation";
|
||||||
|
|
||||||
export const router = createBrowserRouter([
|
export const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <div>Hello world!</div>,
|
element: <div>Hello world!</div>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/editor",
|
path: "/editor",
|
||||||
element: <Editor/>,
|
element: <Editor />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/app",
|
path: "/app",
|
||||||
element: <App/>,
|
element: <App />,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: "/create",
|
||||||
|
element: <Creation />,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { minify } from "terser";
|
||||||
|
|
||||||
|
export default async function minifyCode(code: string) {
|
||||||
|
const replaced = code
|
||||||
|
.replace(RegExp(`React.createElement`, "g"), `rc`)
|
||||||
|
.replace(RegExp(`React.Fragment`, "g"), `rf`)
|
||||||
|
.replace(RegExp(`React.useState`, "g"), `rs`)
|
||||||
|
.replace(RegExp(`React.useEffect`, "g"), `re`)
|
||||||
|
.replace(RegExp(`React.Component`, "g"), `rcp`)
|
||||||
|
.replace(RegExp(`React.createContext`, "g"), `rcc`)
|
||||||
|
.replace(RegExp(`React.useContext`, "g"), `rcu`)
|
||||||
|
.replace(RegExp(`React.useReducer`, "g"), `rd`)
|
||||||
|
.replace(RegExp(`React.memo`, "g"), `rm`)
|
||||||
|
.replace(
|
||||||
|
`"use strict";`,
|
||||||
|
`"use strict"; const rc=React.createElement,rf=React.Fragment,rs=React.useState,re=React.useEffect,rcp=React.Component,rcc=React.createContext,rcu=React.useContext,rd=React.useReducer,rm=React.memo;`
|
||||||
|
);
|
||||||
|
|
||||||
|
const { code: minifiedCode } = await minify(replaced, {
|
||||||
|
compress: {
|
||||||
|
passes: 2,
|
||||||
|
drop_console: true,
|
||||||
|
drop_debugger: true,
|
||||||
|
arguments: true,
|
||||||
|
},
|
||||||
|
mangle: {
|
||||||
|
toplevel: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return minifiedCode || "";
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue