mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-05 14:04:54 +00:00
feat(ui): CodeInput
This commit is contained in:
97
ui/src/components/CodeInput.tsx
Normal file
97
ui/src/components/CodeInput.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { useMemo, useRef } from "react";
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { yaml } from "@codemirror/lang-yaml";
|
||||
import { StreamLanguage } from "@codemirror/language";
|
||||
import { powerShell } from "@codemirror/legacy-modes/mode/powershell";
|
||||
import { shell } from "@codemirror/legacy-modes/mode/shell";
|
||||
import { basicSetup } from "@uiw/codemirror-extensions-basic-setup";
|
||||
import { vscodeDark, vscodeLight } from "@uiw/codemirror-theme-vscode";
|
||||
import CodeMirror, { type ReactCodeMirrorProps, type ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
||||
import { useFocusWithin } from "ahooks";
|
||||
import { theme } from "antd";
|
||||
|
||||
import { useBrowserTheme } from "@/hooks";
|
||||
import { mergeCls } from "@/utils/css";
|
||||
|
||||
export interface CodeInputProps extends Omit<ReactCodeMirrorProps, "extensions" | "lang" | "theme"> {
|
||||
disabled?: boolean;
|
||||
language?: string | string[];
|
||||
}
|
||||
|
||||
const CodeInput = ({ className, style, disabled, language, ...props }: CodeInputProps) => {
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const { theme: browserTheme } = useBrowserTheme();
|
||||
|
||||
const cmRef = useRef<ReactCodeMirrorRef>(null);
|
||||
const isFocusWithin = useFocusWithin(cmRef.current?.editor);
|
||||
|
||||
const cmTheme = useMemo(() => {
|
||||
if (browserTheme === "dark") {
|
||||
return vscodeDark;
|
||||
}
|
||||
return vscodeLight;
|
||||
}, [browserTheme]);
|
||||
|
||||
const cmExtensions = useMemo(() => {
|
||||
const temp: NonNullable<ReactCodeMirrorProps["extensions"]> = [
|
||||
basicSetup({
|
||||
foldGutter: false,
|
||||
dropCursor: false,
|
||||
allowMultipleSelections: false,
|
||||
indentOnInput: false,
|
||||
}),
|
||||
];
|
||||
|
||||
const langs = Array.isArray(language) ? language : [language];
|
||||
langs.forEach((lang) => {
|
||||
switch (lang) {
|
||||
case "shell":
|
||||
temp.push(StreamLanguage.define(shell));
|
||||
break;
|
||||
case "json":
|
||||
temp.push(json());
|
||||
break;
|
||||
case "powershell":
|
||||
temp.push(StreamLanguage.define(powerShell));
|
||||
break;
|
||||
case "yaml":
|
||||
temp.push(yaml());
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return temp;
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={mergeCls(className, `hover:border-[${themeToken.colorPrimaryBorderHover}]`)}
|
||||
style={{
|
||||
...(style ?? {}),
|
||||
border: `1px solid ${isFocusWithin ? (themeToken.Input?.activeBorderColor ?? themeToken.colorPrimaryBorder) : themeToken.colorBorder}`,
|
||||
borderRadius: `${themeToken.borderRadius}px`,
|
||||
backgroundColor: disabled ? themeToken.colorBgContainerDisabled : themeToken.colorBgContainer,
|
||||
boxShadow: isFocusWithin ? themeToken.Input?.activeShadow : undefined,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<CodeMirror
|
||||
ref={cmRef}
|
||||
height="100%"
|
||||
style={{ height: "100%" }}
|
||||
{...props}
|
||||
basicSetup={{
|
||||
foldGutter: false,
|
||||
dropCursor: false,
|
||||
allowMultipleSelections: false,
|
||||
indentOnInput: false,
|
||||
}}
|
||||
extensions={cmExtensions}
|
||||
theme={cmTheme}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeInput;
|
@@ -4,6 +4,7 @@ import { Alert, Button, Dropdown, Form, type FormInstance, Input, Select, Switch
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
import Show from "@/components/Show";
|
||||
import { type AccessConfigForWebhook } from "@/domain/access";
|
||||
|
||||
@@ -105,8 +106,8 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
formInst.setFieldValue("headers", value);
|
||||
};
|
||||
|
||||
const handleWebhookDataForDeploymentBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const handleWebhookDataForDeploymentBlur = () => {
|
||||
const value = formInst.getFieldValue("defaultDataForDeployment");
|
||||
try {
|
||||
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||
formInst.setFieldValue("defaultDataForDeployment", json);
|
||||
@@ -115,8 +116,8 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
}
|
||||
};
|
||||
|
||||
const handleWebhookDataForNotificationBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const handleWebhookDataForNotificationBlur = () => {
|
||||
const value = formInst.getFieldValue("defaultDataForNotification");
|
||||
try {
|
||||
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||
formInst.setFieldValue("defaultDataForNotification", json);
|
||||
@@ -279,7 +280,7 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.webhook_headers.tooltip") }}></span>}
|
||||
>
|
||||
<Input.TextArea autoSize={{ minRows: 3, maxRows: 5 }} placeholder={t("access.form.webhook_headers.placeholder")} onBlur={handleWebhookHeadersBlur} />
|
||||
<Input.TextArea autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("access.form.webhook_headers.placeholder")} onBlur={handleWebhookHeadersBlur} />
|
||||
</Form.Item>
|
||||
|
||||
<Show when={!usage || usage === "deployment"}>
|
||||
@@ -297,9 +298,11 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="defaultDataForDeployment" rules={[formRule]}>
|
||||
<Input.TextArea
|
||||
allowClear
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language="json"
|
||||
placeholder={t("access.form.webhook_default_data_for_deployment.placeholder")}
|
||||
onBlur={handleWebhookDataForDeploymentBlur}
|
||||
/>
|
||||
@@ -338,9 +341,11 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="defaultDataForNotification" rules={[formRule]}>
|
||||
<Input.TextArea
|
||||
allowClear
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language="json"
|
||||
placeholder={t("access.form.webhook_default_data_for_notification.placeholder")}
|
||||
onBlur={handleWebhookDataForNotificationBlur}
|
||||
/>
|
||||
|
@@ -75,7 +75,7 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
|
||||
</CopyToClipboard>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input.TextArea value={data.certificate} variant="filled" rows={5} autoSize={{ maxRows: 5 }} readOnly />
|
||||
<Input.TextArea value={data.certificate} variant="filled" autoSize={{ minRows: 5, maxRows: 5 }} readOnly />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
@@ -92,7 +92,7 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
|
||||
</CopyToClipboard>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input.TextArea value={data.privateKey} variant="filled" rows={5} autoSize={{ maxRows: 5 }} readOnly />
|
||||
<Input.TextArea value={data.privateKey} variant="filled" autoSize={{ minRows: 5, maxRows: 5 }} readOnly />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
|
@@ -108,7 +108,7 @@ const NotifyTemplateForm = ({ className, style }: NotifyTemplateFormProps) => {
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 3, maxRows: 5 }}
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
placeholder={t("settings.notification.template.form.message.placeholder")}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
|
@@ -4,6 +4,7 @@ import { Alert, Button, Dropdown, Form, type FormInstance, Input, Select } from
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
import Show from "@/components/Show";
|
||||
import { CERTIFICATE_FORMATS } from "@/domain/certificate";
|
||||
|
||||
@@ -407,7 +408,13 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="preCommand" rules={[formRule]}>
|
||||
<Input.TextArea autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("workflow_node.deploy.form.local_pre_command.placeholder")} />
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language={["shell", "powershell"]}
|
||||
placeholder={t("workflow_node.deploy.form.local_pre_command.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
@@ -437,7 +444,13 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="postCommand" rules={[formRule]}>
|
||||
<Input.TextArea autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("workflow_node.deploy.form.local_post_command.placeholder")} />
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language={["shell", "powershell"]}
|
||||
placeholder={t("workflow_node.deploy.form.local_post_command.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
@@ -4,6 +4,7 @@ import { Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
import Show from "@/components/Show";
|
||||
import { CERTIFICATE_FORMATS } from "@/domain/certificate";
|
||||
|
||||
@@ -278,7 +279,13 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="preCommand" rules={[formRule]}>
|
||||
<Input.TextArea autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("workflow_node.deploy.form.ssh_pre_command.placeholder")} />
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language={["shell", "powershell"]}
|
||||
placeholder={t("workflow_node.deploy.form.ssh_pre_command.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
@@ -308,7 +315,13 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
|
||||
</div>
|
||||
</label>
|
||||
<Form.Item name="postCommand" rules={[formRule]}>
|
||||
<Input.TextArea autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("workflow_node.deploy.form.ssh_post_command.placeholder")} />
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language={["shell", "powershell"]}
|
||||
placeholder={t("workflow_node.deploy.form.ssh_post_command.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Alert, Form, type FormInstance, Input } from "antd";
|
||||
import { Alert, Form, type FormInstance } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
|
||||
type DeployNodeConfigFormWebhookConfigFieldValues = Nullish<{
|
||||
webhookData: string;
|
||||
}>;
|
||||
@@ -39,8 +41,8 @@ const DeployNodeConfigFormWebhookConfig = ({ form: formInst, formName, disabled,
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleWebhookDataBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const handleWebhookDataBlur = () => {
|
||||
const value = formInst.getFieldValue("webhookData");
|
||||
try {
|
||||
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||
formInst.setFieldValue("webhookData", json);
|
||||
@@ -68,9 +70,11 @@ const DeployNodeConfigFormWebhookConfig = ({ form: formInst, formName, disabled,
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.webhook_data.tooltip") }}></span>}
|
||||
>
|
||||
<Input.TextArea
|
||||
allowClear
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language="json"
|
||||
placeholder={t("workflow_node.deploy.form.webhook_data.placeholder")}
|
||||
onBlur={handleWebhookDataBlur}
|
||||
/>
|
||||
|
@@ -3,6 +3,8 @@ import { Alert, Form, type FormInstance, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
|
||||
type NotifyNodeConfigFormWebhookConfigFieldValues = Nullish<{
|
||||
webhookData: string;
|
||||
}>;
|
||||
@@ -39,8 +41,8 @@ const NotifyNodeConfigFormWebhookConfig = ({ form: formInst, formName, disabled,
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const handleWebhookDataBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const handleWebhookDataBlur = () => {
|
||||
const value = formInst.getFieldValue("webhookData");
|
||||
try {
|
||||
const json = JSON.stringify(JSON.parse(value), null, 2);
|
||||
formInst.setFieldValue("webhookData", json);
|
||||
@@ -68,9 +70,11 @@ const NotifyNodeConfigFormWebhookConfig = ({ form: formInst, formName, disabled,
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.webhook_data.tooltip") }}></span>}
|
||||
>
|
||||
<Input.TextArea
|
||||
allowClear
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
language="json"
|
||||
placeholder={t("workflow_node.notify.form.webhook_data.placeholder")}
|
||||
onBlur={handleWebhookDataBlur}
|
||||
/>
|
||||
|
@@ -531,9 +531,9 @@
|
||||
"workflow_node.deploy.form.ssh_shell_env.label": "命令执行环境",
|
||||
"workflow_node.deploy.form.ssh_shell_env.value": "POSIX Bash(Linux / macOS)",
|
||||
"workflow_node.deploy.form.ssh_pre_command.label": "前置命令(可选)",
|
||||
"workflow_node.deploy.form.ssh_pre_command.placeholder": "请输入保存文件前执行的命令",
|
||||
"workflow_node.deploy.form.ssh_pre_command.placeholder": "请输入上传文件前执行的命令",
|
||||
"workflow_node.deploy.form.ssh_post_command.label": "后置命令(可选)",
|
||||
"workflow_node.deploy.form.ssh_post_command.placeholder": "请输入保存文件后执行的命令",
|
||||
"workflow_node.deploy.form.ssh_post_command.placeholder": "请输入上传文件后执行的命令",
|
||||
"workflow_node.deploy.form.ssh_preset_scripts.button": "使用预设脚本",
|
||||
"workflow_node.deploy.form.ssh_preset_scripts.option.sh_backup_files.label": "POSIX Bash - 备份原证书文件",
|
||||
"workflow_node.deploy.form.ssh_preset_scripts.option.ps_backup_files.label": "PowerShell - 备份原证书文件",
|
||||
|
Reference in New Issue
Block a user