feat: code block language selection (#198)

* code block language selection

* cleanup

* Add copy button
This commit is contained in:
Philip Okugbe
2024-08-24 18:12:19 +01:00
committed by GitHub
parent 4433d5174d
commit 17475bf123
4 changed files with 85 additions and 3 deletions

View File

@ -0,0 +1,62 @@
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from "@tiptap/react";
import { ActionIcon, CopyButton, Group, Select, Tooltip } from "@mantine/core";
import { useState } from "react";
import classes from "./code-block.module.css";
import { IconCheck, IconCopy } from "@tabler/icons-react";
import { useHover } from "@mantine/hooks";
export default function CodeBlockView(props: NodeViewProps) {
const { node, updateAttributes, extension, editor, selected } = props;
const { language } = node.attrs;
const [languageValue, setLanguageValue] = useState<string | null>(
language || null,
);
const { hovered, ref } = useHover();
function changeLanguage(language: string) {
setLanguageValue(language);
updateAttributes({
language: language,
});
}
return (
<NodeViewWrapper className="codeBlock" ref={ref}>
<Group justify="flex-end">
<Select
placeholder="Auto"
checkIconPosition="right"
data={extension.options.lowlight.listLanguages()}
value={languageValue}
onChange={changeLanguage}
searchable
style={{ maxWidth: "130px" }}
classNames={{ input: classes.selectInput }}
disabled={!editor.isEditable}
/>
<CopyButton value={node?.textContent} timeout={2000}>
{({ copied, copy }) => (
<Tooltip
label={copied ? "Copied" : "Copy"}
withArrow
position="right"
>
<ActionIcon
color={copied ? "teal" : "gray"}
variant="subtle"
onClick={copy}
>
{copied ? <IconCheck size={16} /> : <IconCopy size={16} />}
</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>
<pre spellCheck="false">
<NodeViewContent as="code" className={`language-${language}`} />
</pre>
</NodeViewWrapper>
);
}

View File

@ -0,0 +1,4 @@
.selectInput {
height: 25px;
min-height: 25px;
}

View File

@ -46,7 +46,12 @@ import ImageView from "@/features/editor/components/image/image-view.tsx";
import CalloutView from "@/features/editor/components/callout/callout-view.tsx"; import CalloutView from "@/features/editor/components/callout/callout-view.tsx";
import { common, createLowlight } from "lowlight"; import { common, createLowlight } from "lowlight";
import VideoView from "@/features/editor/components/video/video-view.tsx"; import VideoView from "@/features/editor/components/video/video-view.tsx";
import { ReactNodeViewRenderer } from "@tiptap/react";
import CodeBlockView from "@/features/editor/components/code-block/code-block-view.tsx";
import plaintext from "highlight.js/lib/languages/plaintext";
const lowlight = createLowlight(common); const lowlight = createLowlight(common);
lowlight.register("mermaid", plaintext);
export const mainExtensions = [ export const mainExtensions = [
StarterKit.configure({ StarterKit.configure({
@ -134,7 +139,11 @@ export const mainExtensions = [
Callout.configure({ Callout.configure({
view: CalloutView, view: CalloutView,
}), }),
CodeBlockLowlight.configure({ CodeBlockLowlight.extend({
addNodeView() {
return ReactNodeViewRenderer(CodeBlockView);
},
}).configure({
lowlight, lowlight,
HTMLAttributes: { HTMLAttributes: {
spellcheck: false, spellcheck: false,

View File

@ -1,6 +1,13 @@
.ProseMirror { .ProseMirror {
.codeBlock {
padding: 4px;
border-radius: var(--mantine-radius-default);
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
}
pre { pre {
padding: var(--mantine-spacing-sm) var(--mantine-spacing-md); padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
margin: 4px;
font-family: "JetBrainsMono", var(--mantine-font-family-monospace); font-family: "JetBrainsMono", var(--mantine-font-family-monospace);
border-radius: var(--mantine-radius-default); border-radius: var(--mantine-radius-default);
@ -87,7 +94,7 @@
} }
} }
:not(pre) > code { :not(pre) > code {
font-family: "JetBrainsMono", var(--mantine-font-family-monospace); font-family: "JetBrainsMono", var(--mantine-font-family-monospace);
line-height: var(--mantine-line-height); line-height: var(--mantine-line-height);
padding: 2px calc(var(--mantine-spacing-xs) / 2); padding: 2px calc(var(--mantine-spacing-xs) / 2);