From c265e670e66b203004b6dc64a1516bd6358f8f3d Mon Sep 17 00:00:00 2001 From: donotlb Date: Sun, 21 Aug 2022 12:32:02 +0800 Subject: [PATCH] make sure handling of line number will not be overridden by parent component --- .../syntaxhighlighter-types.ts | 4 +- .../syntaxhighlighter/syntaxhighlighter.tsx | 72 +++++++++++-------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/code/lib/components/src/syntaxhighlighter/syntaxhighlighter-types.ts b/code/lib/components/src/syntaxhighlighter/syntaxhighlighter-types.ts index 7548a537a72e..7b90c5ed9094 100644 --- a/code/lib/components/src/syntaxhighlighter/syntaxhighlighter-types.ts +++ b/code/lib/components/src/syntaxhighlighter/syntaxhighlighter-types.ts @@ -7,6 +7,8 @@ export interface SyntaxHighlighterRendererProps { useInlineStyles: boolean; } +export type SyntaxHighlighterRenderer = (props: SyntaxHighlighterRendererProps) => ReactNode; + export interface SyntaxHighlighterCustomProps { language: string; copyable?: boolean; @@ -15,7 +17,7 @@ export interface SyntaxHighlighterCustomProps { format?: SyntaxHighlighterFormatTypes; formatter?: (type: SyntaxHighlighterFormatTypes, source: string) => string; className?: string; - renderer?: (props: SyntaxHighlighterRendererProps) => ReactNode; + renderer?: SyntaxHighlighterRenderer; } export type SyntaxHighlighterFormatTypes = boolean | 'dedent' | BuiltInParserName; diff --git a/code/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/code/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx index f04c6a1977af..ec55a4059a2a 100644 --- a/code/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx +++ b/code/lib/components/src/syntaxhighlighter/syntaxhighlighter.tsx @@ -35,7 +35,11 @@ import { createElement } from 'react-syntax-highlighter/dist/esm/index'; import { ActionBar } from '../ActionBar/ActionBar'; import { ScrollArea } from '../ScrollArea/ScrollArea'; -import type { SyntaxHighlighterProps } from './syntaxhighlighter-types'; +import type { + SyntaxHighlighterProps, + SyntaxHighlighterRenderer, + SyntaxHighlighterRendererProps, +} from './syntaxhighlighter-types'; const { navigator, document, window: globalWindow } = global; @@ -143,38 +147,34 @@ const Code = styled.div(({ theme }) => ({ opacity: 1, })); +const processLineNumber = (row: any) => { + const children = [...row.children]; + const lineNumberNode = children[0]; + const lineNumber = lineNumberNode.children[0].value; + const processedLineNumberNode = { + ...lineNumberNode, + // empty the line-number element + children: [], + properties: { + ...lineNumberNode.properties, + // add a data-line-number attribute to line-number element, so we can access the line number with `content: attr(data-line-number)` + 'data-line-number': lineNumber, + // remove the 'userSelect: none' style, which will produce extra empty lines when copy-pasting in firefox + style: { ...lineNumberNode.properties.style, userSelect: 'auto' }, + }, + }; + children[0] = processedLineNumberNode; + return { ...row, children }; +}; + /** - * A custom renderer used to process `span.linenumber` element in each line of code, - * which should only be enabled if `showLineNumbers = true` + * A custom renderer for handling `span.linenumber` element in each line of code, + * which is enabled by default if no renderer is passed in from the parent component */ -const renderer = ({ - rows, - stylesheet, - useInlineStyles, -}: { - rows: any[]; - stylesheet: any; - useInlineStyles: any; -}) => { +const defaultRenderer: SyntaxHighlighterRenderer = ({ rows, stylesheet, useInlineStyles }) => { return rows.map((node: any, i: number) => { - const children = [...node.children]; - const lineNumberNode = children[0]; - const lineNumber = lineNumberNode.children[0].value; - const processedLineNumberNode = { - ...lineNumberNode, - // empty the line-number element - children: [], - properties: { - ...lineNumberNode.properties, - // add a data-line-number attribute to line-number element, so we can access the line number with `content: attr(data-line-number)` - 'data-line-number': lineNumber, - // remove the 'userSelect: none' style, which will produce extra empty lines when copy-pasting in firefox - style: { ...lineNumberNode.properties.style, userSelect: 'auto' }, - }, - }; - children[0] = processedLineNumberNode; return createElement({ - node: { ...node, children }, + node: processLineNumber(node), stylesheet, useInlineStyles, key: `code-segement${i}`, @@ -182,6 +182,17 @@ const renderer = ({ }); }; +const wrapRenderer = (renderer: SyntaxHighlighterRenderer, showLineNumbers: boolean) => { + if (!showLineNumbers) { + return renderer; + } + if (renderer) { + return ({ rows, ...rest }: SyntaxHighlighterRendererProps) => + renderer({ rows: rows.map((row) => processLineNumber(row)), ...rest }); + } + return defaultRenderer; +}; + export interface SyntaxHighlighterState { copied: boolean; } @@ -216,6 +227,7 @@ export const SyntaxHighlighter: FC = ({ }) .catch(logger.error); }, []); + const renderer = wrapRenderer(rest.renderer, showLineNumbers); return ( = ({ useInlineStyles={false} PreTag={Pre} CodeTag={Code} - renderer={showLineNumbers ? renderer : undefined} lineNumberContainerStyle={{}} {...rest} + renderer={renderer} > {highlightableCode}