From 3d1e35a694f442c5978400946cba957520b57d3f Mon Sep 17 00:00:00 2001 From: "yuxin.zxy" Date: Wed, 20 Nov 2024 20:40:32 +0800 Subject: [PATCH] feat: support custom line className with generateLineClassName --- README.md | 20 ++++++++++++++++++++ site/components/DiffView/index.tsx | 12 ++++++++++++ src/Diff/index.tsx | 4 ++++ src/Hunk/SplitHunk/SplitChange.tsx | 11 +++++++++-- src/Hunk/UnifiedHunk/UnifiedChange.tsx | 8 +++++++- src/Hunk/interface.ts | 1 + src/context/index.ts | 2 ++ 7 files changed, 55 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a35b47d..3e2e75a 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ Here is the full list of its props: - `{boolean} optimizeSelection`: Whether to optimize selection to a single column, when this prop is set to `true` in split mode, user can only select code from either old or new side, this can help copy and paste lines of code. This feature can cause some performance dropdown when the diff is extremely large, so it is turned off by default. - `{Function} renderToken`: A function to render customized syntax tokens, see [Pick ranges](#pick-ranges) section for detail. - `{Function} renderGutter`: A function to render content in gutter cells, see [Customize gutter](#customize-gutter) section for detail. +- `{Function} generateLineClassName`: A function to provide extra classNames for specific lines, such as rebase diff line, source file content, see [Customize line class name](#customize-lineClassName) section for detail. #### Key of change @@ -539,6 +540,25 @@ const renderGutter = ({change, side, renderDefault, wrapInAnchor, inHoverState}) }; ``` +### customize-lineClassName + +The `generateLineClassName` function prop will receive a single object argument with following properties: + +- `{Change} changes`: an array of changes incurrent line. +- `{Function} defaultGenerate`: A default render function which returns lineClassName. + + +```jsx +const generateLineClassName = ({changes, defaultGenerate}) => { + if (viewType === 'inline') { + const [change] = changes; + return defaultGenerate(); + } + const [oldChange, newChange] = changes; + return 'rebase-line-change'; +} +``` + ## Utilities `react-diff-view` comes with some utility functions to help simplify common issues: diff --git a/site/components/DiffView/index.tsx b/site/components/DiffView/index.tsx index 0223c5d..23d2cbd 100644 --- a/site/components/DiffView/index.tsx +++ b/site/components/DiffView/index.tsx @@ -11,6 +11,7 @@ import { DiffType, GutterOptions, EventMap, + ChangeData, } from 'react-diff-view'; import 'react-diff-view/styles/index.css'; import {useConfiguration} from '../../context/configuration'; @@ -172,6 +173,16 @@ export default function DiffView(props: Props) { }, [addComment, viewType] ); + + const generateLineClassName = useCallback( + ({changes, defaultGenerate}: { changes: ChangeData[], defaultGenerate: () => string }) => { + if (changes.length === 1) { + return `diff-line-${changes[0].type}`; + } + return defaultGenerate(); + }, + [] + ); const events: EventMap = { onClick: toggleSelection, }; @@ -227,6 +238,7 @@ export default function DiffView(props: Props) { codeEvents={events} gutterEvents={events} renderGutter={renderGutter} + generateLineClassName={generateLineClassName} > {hunks => hunks.reduce(renderHunk, [])} diff --git a/src/Diff/index.tsx b/src/Diff/index.tsx index ca6962b..3225c9b 100644 --- a/src/Diff/index.tsx +++ b/src/Diff/index.tsx @@ -28,6 +28,7 @@ export interface DiffProps { className?: string; hunkClassName?: string; lineClassName?: string; + generateLineClassName?: (params: {changes: ChangeData[], defaultGenerate: () => string}) => string | undefined; gutterClassName?: string; codeClassName?: string; tokens?: HunkTokens | null; @@ -70,6 +71,7 @@ function Diff(props: DiffProps) { className, hunkClassName = DEFAULT_CONTEXT_VALUE.hunkClassName, lineClassName = DEFAULT_CONTEXT_VALUE.lineClassName, + generateLineClassName = DEFAULT_CONTEXT_VALUE.generateLineClassName, gutterClassName = DEFAULT_CONTEXT_VALUE.gutterClassName, codeClassName = DEFAULT_CONTEXT_VALUE.codeClassName, gutterType = DEFAULT_CONTEXT_VALUE.gutterType, @@ -158,6 +160,7 @@ function Diff(props: DiffProps) { return { hunkClassName, lineClassName, + generateLineClassName, gutterClassName, codeClassName, monotonous, @@ -184,6 +187,7 @@ function Diff(props: DiffProps) { hideGutter, hunkClassName, lineClassName, + generateLineClassName, monotonous, renderGutter, renderToken, diff --git a/src/Hunk/SplitHunk/SplitChange.tsx b/src/Hunk/SplitHunk/SplitChange.tsx index 748f1fc..ed943e2 100644 --- a/src/Hunk/SplitHunk/SplitChange.tsx +++ b/src/Hunk/SplitHunk/SplitChange.tsx @@ -155,6 +155,7 @@ function SplitChange(props: SplitChangeProps) { codeEvents, hideGutter, generateAnchorID, + generateLineClassName, gutterAnchor, renderToken, renderGutter, @@ -167,6 +168,12 @@ function SplitChange(props: SplitChangeProps) { const newCodeEvents = useCallbackOnSide('new', setHover, newChange, codeEvents); const oldAnchorID = oldChange && generateAnchorID(oldChange); const newAnchorID = newChange && generateAnchorID(newChange); + + const lineClassName = generateLineClassName({ + changes: [oldChange!, newChange!], + defaultGenerate: () => className, + }); + const commons = { monotonous, hideGutter, @@ -206,7 +213,7 @@ function SplitChange(props: SplitChangeProps) { if (monotonous) { return ( - + {renderCells(oldChange ? oldArgs : newArgs)} ); @@ -229,7 +236,7 @@ function SplitChange(props: SplitChangeProps) { })(oldChange, newChange); return ( - + {renderCells(oldArgs)} {renderCells(newArgs)} diff --git a/src/Hunk/UnifiedHunk/UnifiedChange.tsx b/src/Hunk/UnifiedHunk/UnifiedChange.tsx index e6e08d3..f4e6a20 100644 --- a/src/Hunk/UnifiedHunk/UnifiedChange.tsx +++ b/src/Hunk/UnifiedHunk/UnifiedChange.tsx @@ -67,6 +67,7 @@ function UnifiedChange(props: UnifiedChangeProps) { selected, tokens, className, + generateLineClassName, gutterClassName, codeClassName, gutterEvents, @@ -86,6 +87,11 @@ function UnifiedChange(props: UnifiedChangeProps) { const boundCodeEvents = useBoundCallbacks(codeEvents, eventArg, hoverOn, hoverOff); const anchorID = generateAnchorID(change); + const lineClassName = generateLineClassName({ + changes: [change], + defaultGenerate: () => className, + }); + const gutterClassNameValue = classNames( 'diff-gutter', `diff-gutter-${type}`, @@ -100,7 +106,7 @@ function UnifiedChange(props: UnifiedChangeProps) { ); return ( - + { !hideGutter && renderGutterCell( gutterClassNameValue, diff --git a/src/Hunk/interface.ts b/src/Hunk/interface.ts index 90ece27..1672010 100644 --- a/src/Hunk/interface.ts +++ b/src/Hunk/interface.ts @@ -8,6 +8,7 @@ export interface SharedProps { gutterAnchor: boolean; monotonous: boolean; generateAnchorID: (change: ChangeData) => string | undefined; + generateLineClassName: (params: {changes: ChangeData[], defaultGenerate: () => string}) => string | undefined; renderToken?: RenderToken; renderGutter: RenderGutter; } diff --git a/src/context/index.ts b/src/context/index.ts index 8ca3654..b950885 100644 --- a/src/context/index.ts +++ b/src/context/index.ts @@ -54,6 +54,7 @@ export interface ContextProps { selectedChanges: string[]; tokens?: HunkTokens | null; generateAnchorID: (change: ChangeData) => string | undefined; + generateLineClassName: (params: {changes: ChangeData[], defaultGenerate: () => string}) => string | undefined; renderToken?: RenderToken; renderGutter: RenderGutter; gutterEvents: EventMap; @@ -72,6 +73,7 @@ export const DEFAULT_CONTEXT_VALUE: ContextProps = { hideGutter: false, selectedChanges: [], generateAnchorID: () => undefined, + generateLineClassName: () => undefined, renderGutter: ({renderDefault, wrapInAnchor}) => wrapInAnchor(renderDefault()), codeEvents: {}, gutterEvents: {},