Files
docmost/apps/client/src/features/editor/components/code-block/code-block-view.tsx
T
lleohao 670ee64179 Support I18n (#243)
* feat: support i18n

* feat: wip support i18n

* feat: complete space translation

* feat: complete page translation

* feat: update space translation

* feat: update workspace translation

* feat: update group translation

* feat: update workspace translation

* feat: update page translation

* feat: update user translation

* chore: update pnpm-lock

* feat: add query translation

* refactor: merge to single file

* chore: remove necessary code

* feat: save language to BE

* fix: only load current language

* feat: save language to locale column

* fix: cleanups

* add language menu to preferences page

* new translations

* translate editor

* Translate editor placeholders

* translate space selection component

---------

Co-authored-by: Philip Okugbe <phil@docmost.com>
Co-authored-by: Philip Okugbe <16838612+Philipinho@users.noreply.github.com>
2025-01-04 13:17:17 +00:00

100 lines
3.1 KiB
TypeScript

import { NodeViewContent, NodeViewProps, NodeViewWrapper } from '@tiptap/react';
import { ActionIcon, CopyButton, Group, Select, Tooltip } from '@mantine/core';
import { useEffect, useState } from 'react';
import { IconCheck, IconCopy } from '@tabler/icons-react';
import classes from './code-block.module.css';
import React from 'react';
import { Suspense } from 'react';
import { useTranslation } from "react-i18next";
const MermaidView = React.lazy(
() => import('@/features/editor/components/code-block/mermaid-view.tsx')
);
export default function CodeBlockView(props: NodeViewProps) {
const { t } = useTranslation();
const { node, updateAttributes, extension, editor, getPos } = props;
const { language } = node.attrs;
const [languageValue, setLanguageValue] = useState<string | null>(
language || null
);
const [isSelected, setIsSelected] = useState(false);
useEffect(() => {
const updateSelection = () => {
const { state } = editor;
const { from, to } = state.selection;
// Check if the selection intersects with the node's range
const isNodeSelected =
(from >= getPos() && from < getPos() + node.nodeSize) ||
(to > getPos() && to <= getPos() + node.nodeSize);
setIsSelected(isNodeSelected);
};
editor.on('selectionUpdate', updateSelection);
return () => {
editor.off('selectionUpdate', updateSelection);
};
}, [editor, getPos(), node.nodeSize]);
function changeLanguage(language: string) {
setLanguageValue(language);
updateAttributes({
language: language,
});
}
return (
<NodeViewWrapper className="codeBlock">
<Group justify="flex-end" contentEditable={false}>
<Select
placeholder="auto"
checkIconPosition="right"
data={extension.options.lowlight.listLanguages().sort()}
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 ? t('Copied') : t('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"
hidden={
((language === 'mermaid' && !editor.isEditable) ||
(language === 'mermaid' && !isSelected)) &&
node.textContent.length > 0
}
>
<NodeViewContent as="code" className={`language-${language}`} />
</pre>
{language === 'mermaid' && (
<Suspense fallback={null}>
<MermaidView props={props} />
</Suspense>
)}
</NodeViewWrapper>
);
}