mirror of
https://github.com/Shadowfita/docmost.git
synced 2025-11-13 16:22:32 +10:00
feat: code block language selection (#198)
* code block language selection * cleanup * Add copy button
This commit is contained in:
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
.selectInput {
|
||||||
|
height: 25px;
|
||||||
|
min-height: 25px;
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user