aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules/code/code.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/molecules/code/code.tsx')
-rw-r--r--src/components/molecules/code/code.tsx183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/components/molecules/code/code.tsx b/src/components/molecules/code/code.tsx
new file mode 100644
index 0000000..0168fd6
--- /dev/null
+++ b/src/components/molecules/code/code.tsx
@@ -0,0 +1,183 @@
+import { forwardRef, type ForwardRefRenderFunction } from 'react';
+import {
+ usePrism,
+ type PrismLanguage,
+ type PrismAvailablePlugin,
+} from '../../../utils/hooks';
+import { Figure, type FigureProps } from '../../atoms';
+import styles from './code.module.scss';
+
+export type CodeProps = Omit<FigureProps, 'children'> & {
+ /**
+ * The code to highlight.
+ */
+ children: string;
+ /**
+ * Define a pattern to automatically present some lines as continuation lines
+ * when using command line.
+ *
+ * @default undefined
+ */
+ cmdContinuationFilter?: string;
+ /**
+ * Define the prompt to be displayed when the command has continued beyond
+ * the first line. Only used with command line.
+ *
+ * @default '>'
+ */
+ cmdContinuationPrompt?: string;
+ /**
+ * Define the line continuation string or character when using command line.
+ */
+ cmdContinuationStr?: string;
+ /**
+ * Define the host when using command line.
+ */
+ cmdHost?: string;
+ /**
+ * Define a custom prompt when using command line. By default, `#` will be
+ * used for the root user and `$` for all other users.
+ */
+ cmdPrompt?: string;
+ /**
+ * Define the line(s) that must be presented as output when using command
+ * line.
+ *
+ * @example '6' // a single line
+ * @example '2-7' // a range
+ * @example '3,9-11' // multiple lines with a range
+ *
+ * @default undefined
+ */
+ cmdOutput?: string;
+ /**
+ * Define a pattern to automatically present some lines as output when using
+ * command line.
+ *
+ * @default undefined
+ */
+ cmdOutputFilter?: string;
+ /**
+ * Specify the user when using command line.
+ */
+ cmdUser?: string;
+ /**
+ * Define the line(s) that must be highlighted.
+ *
+ * DON'T USE: it seems the plugin is not correctly loaded.
+ *
+ * @example '6' // a single line
+ * @example '2-7' // a range
+ * @example '3,9-11' // multiple lines with a range
+ *
+ * @default undefined
+ */
+ highlight?: string;
+ /**
+ * Should the code be treated as command lines?
+ *
+ * @default false
+ */
+ isCmd?: boolean;
+ /**
+ * Should the code be treated as a diff block?
+ *
+ * @default false
+ */
+ isDiff?: boolean;
+ /**
+ * The code language.
+ */
+ language: PrismLanguage;
+ /**
+ * Define the starting line number. It will be ignored with command lines.
+ *
+ * @default undefined // Starts with 1.
+ */
+ start?: string;
+};
+
+const CodeWithRef: ForwardRefRenderFunction<HTMLElement, CodeProps> = (
+ {
+ children,
+ className = '',
+ cmdContinuationFilter,
+ cmdContinuationPrompt,
+ cmdContinuationStr,
+ cmdHost,
+ cmdOutput,
+ cmdOutputFilter,
+ cmdPrompt,
+ cmdUser,
+ highlight,
+ isCmd = false,
+ isDiff = false,
+ language,
+ start,
+ ...props
+ },
+ ref
+) => {
+ const wrapperClass = `${styles.wrapper} ${className}`;
+ const codeClass = isDiff
+ ? `language-diff-${language}`
+ : `language-${language}`;
+ const plugins: PrismAvailablePlugin[] = [
+ 'toolbar',
+ 'autoloader',
+ 'show-language',
+ 'color-scheme',
+ 'copy-to-clipboard',
+ 'inline-color',
+ 'match-braces',
+ 'normalize-whitespace',
+ ];
+
+ if (isDiff || language === 'diff') plugins.push('diff-highlight');
+
+ if (language.endsWith('treeview')) plugins.push('treeview');
+ else plugins.push(isCmd ? 'command-line' : 'line-numbers');
+
+ const { attributes: prismAttributes, className: prismClass } = usePrism({
+ attributes: {
+ 'data-continuation-prompt': cmdContinuationPrompt,
+ 'data-continuation-str': cmdContinuationStr,
+ 'data-filter-continuation': cmdContinuationFilter,
+ 'data-filter-output': cmdOutputFilter,
+ 'data-host': cmdHost,
+ 'data-line': highlight,
+ 'data-output': cmdOutput,
+ 'data-prompt': cmdPrompt,
+ 'data-start': start,
+ 'data-toolbar-order': 'show-language,copy-to-clipboard,color-scheme',
+ 'data-user': cmdUser,
+ },
+ language,
+ plugins,
+ });
+
+ return (
+ <Figure {...props} className={wrapperClass} ref={ref}>
+ <pre
+ {...prismAttributes}
+ className={prismClass}
+ // cSpell:ignore noninteractive
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
+ tabIndex={0}
+ >
+ <code className={codeClass}>{children}</code>
+ </pre>
+ </Figure>
+ );
+};
+
+/**
+ * Code component
+ *
+ * Render a code block with syntax highlighting.
+ *
+ * @todo Find a way to load Prism plugins without Babel (Next uses SWC). It
+ * seems some plugins are not loaded correctly (`line-highlight` or `treeview`
+ * for example).
+ */
+export const Code = forwardRef(CodeWithRef);