pref: improve code block styling in editor (#6089)

#### What type of PR is this?

/kind improvement
/area editor

#### What this PR does / why we need it:

优化编辑器代码块样式。

before:

<img width="907" alt="image" src="https://github.com/halo-dev/halo/assets/31335418/11ad91a9-75ce-42ec-a947-effca7b42f30">

after:

<img width="932" alt="image" src="https://github.com/halo-dev/halo/assets/31335418/d0b3275b-a269-4104-aea8-0d8726ce32e7">

#### How to test it?

测试复制功能是否正常。

#### Does this PR introduce a user-facing change?
```release-note
优化默认编辑器代码块样式
```
pull/6081/head
Takagi 2024-06-18 14:00:52 +08:00 committed by GitHub
parent 10f3157258
commit 1e37768b35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 71 additions and 22 deletions

View File

@ -4,6 +4,10 @@ import type { Editor, Node } from "@/tiptap/vue-3";
import { NodeViewContent, NodeViewWrapper } from "@/tiptap/vue-3";
import lowlight from "./lowlight";
import { computed } from "vue";
import BxBxsCopy from "~icons/bx/bxs-copy";
import IconCheckboxCircle from "~icons/ri/checkbox-circle-line";
import { useTimeout } from "@vueuse/core";
import { i18n } from "@/locales";
const props = defineProps<{
editor: Editor;
@ -28,24 +32,53 @@ const selectedLanguage = computed({
props.updateAttributes({ language: language });
},
});
const { ready, start } = useTimeout(2000, { controls: true, immediate: false });
const handleCopyCode = () => {
if (!ready.value) return;
const code = props.node.textContent;
navigator.clipboard.writeText(code).then(() => {
start();
});
};
</script>
<template>
<node-view-wrapper as="div" class="code-node">
<div class="py-1.5">
<select
v-model="selectedLanguage"
contenteditable="false"
class="block px-2 py-1.5 text-sm text-gray-900 border border-gray-300 rounded-md bg-gray-50 focus:ring-blue-500 focus:border-blue-500"
>
<option :value="null">auto</option>
<option
v-for="(language, index) in languages"
:key="index"
:value="language"
<node-view-wrapper as="div" class="code-node border-[1px] rounded mt-3">
<div
class="bg-neutral-100 border-b-[1px] border-b-gray-100 py-1 flex items-center justify-between rounded-t"
>
<div class="flex-1 flex items-center pl-3">
<select
v-model="selectedLanguage"
contenteditable="false"
class="block !leading-8 text-sm text-gray-900 border select-none border-transparent rounded-md bg-transparent focus:ring-blue-500 focus:border-blue-500 cursor-pointer hover:bg-zinc-200"
>
{{ language }}
</option>
</select>
<option :value="null">auto</option>
<option
v-for="(language, index) in languages"
:key="index"
:value="language"
>
{{ language }}
</option>
</select>
</div>
<div class="pr-3 flex items-center">
<div
v-tooltip="
ready
? i18n.global.t('editor.common.codeblock.copy_code')
: i18n.global.t('editor.common.codeblock.copy_code_success')
"
class="w-8 h-8 cursor-pointer rounded flex items-center justify-center"
:class="{ 'hover:bg-zinc-200': ready }"
@click="handleCopyCode"
>
<IconCheckboxCircle v-if="!ready" class="w-4 h-4 text-green-500" />
<BxBxsCopy v-else class="w-4 h-4 text-gray-500" />
</div>
</div>
</div>
<pre><node-view-content as="code" class="hljs" /></pre>
</node-view-wrapper>

View File

@ -205,7 +205,7 @@ export default CodeBlockLowlight.extend<
editor,
isActive: editor.isActive("codeBlock"),
icon: markRaw(MdiCodeBracesBox),
title: i18n.global.t("editor.common.codeblock"),
title: i18n.global.t("editor.common.codeblock.title"),
action: () => editor.chain().focus().toggleCodeBlock().run(),
},
};
@ -214,7 +214,7 @@ export default CodeBlockLowlight.extend<
return {
priority: 80,
icon: markRaw(MdiCodeBracesBox),
title: "editor.common.codeblock",
title: "editor.common.codeblock.title",
keywords: ["codeblock", "daimakuai"],
command: ({ editor, range }: { editor: Editor; range: Range }) => {
editor.chain().focus().deleteRange(range).setCodeBlock().run();
@ -229,7 +229,7 @@ export default CodeBlockLowlight.extend<
props: {
editor,
icon: markRaw(MdiCodeBracesBox),
title: i18n.global.t("editor.common.codeblock"),
title: i18n.global.t("editor.common.codeblock.title"),
action: () => {
editor.chain().focus().setCodeBlock().run();
},

View File

@ -4,7 +4,7 @@ import "./styles/index.scss";
import "./styles/tailwind.css";
import "floating-vue/dist/style.css";
import "github-markdown-css/github-markdown-light.css";
import "highlight.js/styles/github-dark.css";
import "highlight.js/styles/github.css";
const plugin: Plugin = {
install(app: App) {

View File

@ -104,7 +104,10 @@ editor:
code: Code
superscript: Super Script
subscript: Sub Script
codeblock: Code block
codeblock:
title: Code block
copy_code: Copy code
copy_code_success: Copy success
image: Image
heading:
title: Text type

View File

@ -104,7 +104,10 @@ editor:
code: 行内代码
superscript: 上角标
subscript: 下角标
codeblock: 代码块
codeblock:
title: 代码块
copy_code: 复制代码
copy_code_success: 复制成功
image: 图片
heading:
title: 文本类型

View File

@ -37,7 +37,7 @@
}
pre {
background: #0d0d0d;
background-color: transparent;
padding: 0.75rem 1rem;
margin: 0;

View File

@ -4,3 +4,4 @@
@import "./columns.scss";
@import "./search.scss";
@import "./format-brush.scss";
@import "./node-select.scss";

View File

@ -0,0 +1,9 @@
.halo-rich-text-editor {
$editorNodeCardBorderSelected: rgba(47, 142, 244);
.has-node-selected {
&.code-node {
border-color: $editorNodeCardBorderSelected;
}
}
}