diff --git a/packages/diff-view/src/parts/GetContentRightDom/GetContentRightDom.ts b/packages/diff-view/src/parts/GetContentRightDom/GetContentRightDom.ts index a847c4b3..d78eee0c 100644 --- a/packages/diff-view/src/parts/GetContentRightDom/GetContentRightDom.ts +++ b/packages/diff-view/src/parts/GetContentRightDom/GetContentRightDom.ts @@ -1,4 +1,5 @@ import { VirtualDomElements, type VirtualDomNode } from '@lvce-editor/virtual-dom-worker' +import type { DiffMode } from '../DiffViewState/DiffViewState.ts' import type { InlineDiffChange } from '../InlineDiffChange/InlineDiffChange.ts' import type { TokenizedLine } from '../TokenizedLine/TokenizedLine.ts' import type { VisibleLine } from '../VisibleLine/VisibleLine.ts' @@ -7,11 +8,13 @@ import * as ClassNames from '../ClassNames/ClassNames.ts' import * as DomEventListenerFunctions from '../DomEventListenerFunctions/DomEventListenerFunctions.ts' import { getContentDom } from '../GetContentDom/GetContentDom.ts' import { getCursorDom } from '../GetCursorDom/GetCursorDom.ts' +import { getDiffEditorButtonsDom } from '../GetDiffEditorButtonsDom/GetDiffEditorButtonsDom.ts' import * as InputName from '../InputName/InputName.ts' interface GetContentRightDomOptions { readonly allowedLinkSchemes?: readonly string[] readonly contentRight: string + readonly diffMode?: DiffMode readonly editable?: boolean readonly errorCodeFrame?: string readonly errorMessage?: string @@ -22,6 +25,7 @@ interface GetContentRightDomOptions { readonly lineNumbers?: boolean readonly maxLineY?: number readonly minLineY?: number + readonly showWhitespace?: boolean readonly tokenizedLines?: readonly TokenizedLine[] readonly totalLineCount?: number readonly visibleLines?: readonly VisibleLine[] @@ -46,6 +50,7 @@ const getInputWrapperDom = (inputValue: string): readonly VirtualDomNode[] => [ export const getContentRightDom = ({ allowedLinkSchemes = defaultAllowedLinkSchemes, contentRight, + diffMode = 'side-by-side', editable = false, errorCodeFrame = '', errorMessage = '', @@ -56,6 +61,7 @@ export const getContentRightDom = ({ lineNumbers = true, maxLineY, minLineY = 0, + showWhitespace = false, tokenizedLines = [], totalLineCount, visibleLines = [], @@ -80,14 +86,17 @@ export const getContentRightDom = ({ visibleLines, itemHeight, ) + const buttonsDom = getDiffEditorButtonsDom(diffMode, showWhitespace) if (!editable || errorMessage) { const [content, ...rest] = contentDom return [ { ...content, + childCount: content.childCount + 1, onClick: DomEventListenerFunctions.HandleClickRightSide, }, ...rest, + ...buttonsDom, ] } const [content, ...rest] = contentDom @@ -95,11 +104,12 @@ export const getContentRightDom = ({ return [ { ...content, - childCount: content.childCount + 2, + childCount: content.childCount + 3, onClick: DomEventListenerFunctions.HandleClickRightSide, }, ...rest, ...cursorDom, ...getInputWrapperDom(inputValue), + ...buttonsDom, ] } diff --git a/packages/diff-view/src/parts/GetDiffEditorVirtualDom/GetDiffEditorVirtualDom.ts b/packages/diff-view/src/parts/GetDiffEditorVirtualDom/GetDiffEditorVirtualDom.ts index f3acf1c1..3ba70f7a 100644 --- a/packages/diff-view/src/parts/GetDiffEditorVirtualDom/GetDiffEditorVirtualDom.ts +++ b/packages/diff-view/src/parts/GetDiffEditorVirtualDom/GetDiffEditorVirtualDom.ts @@ -5,7 +5,6 @@ import * as ClassNames from '../ClassNames/ClassNames.ts' import * as DomEventListenerFunctions from '../DomEventListenerFunctions/DomEventListenerFunctions.ts' import { getContentLeftDom } from '../GetContentLeftDom/GetContentLeftDom.ts' import { getContentRightDom } from '../GetContentRightDom/GetContentRightDom.ts' -import { getDiffEditorButtonsDom } from '../GetDiffEditorButtonsDom/GetDiffEditorButtonsDom.ts' import { getDiffSearchHeaderDom } from '../GetDiffSearchHeaderDom/GetDiffSearchHeaderDom.ts' import { getImageLeftDom } from '../GetImageLeftDom/GetImageLeftDom.ts' import { getImageRightDom } from '../GetImageRightDom/GetImageRightDom.ts' @@ -36,18 +35,16 @@ const getDiffEditorWithSearchDom = ( sashLayoutClass: string, leftDom: readonly VirtualDomNode[], rightDom: readonly VirtualDomNode[], - buttonsDom: readonly VirtualDomNode[], scrollBarDom: readonly VirtualDomNode[], scrollBarActive: boolean, ): readonly VirtualDomNode[] => { return [ - getRootDom(scrollBarActive ? 4 : 3, `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${diffEditorLayoutClass} ${ClassNames.DiffEditorWithSearch}`), + getRootDom(scrollBarActive ? 3 : 2, `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${diffEditorLayoutClass} ${ClassNames.DiffEditorWithSearch}`), ...getDiffSearchHeaderDom(), getEditorBodyDom(diffEditorLayoutClass), ...leftDom, getSashDom(sashLayoutClass), ...rightDom, - ...buttonsDom, ...scrollBarDom, ] } @@ -57,16 +54,14 @@ const getDiffEditorWithoutSearchDom = ( sashLayoutClass: string, leftDom: readonly VirtualDomNode[], rightDom: readonly VirtualDomNode[], - buttonsDom: readonly VirtualDomNode[], scrollBarDom: readonly VirtualDomNode[], scrollBarActive: boolean, ): readonly VirtualDomNode[] => { return [ - getRootDom(scrollBarActive ? 5 : 4, `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${diffEditorLayoutClass}`), + getRootDom(scrollBarActive ? 4 : 3, `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${diffEditorLayoutClass}`), ...leftDom, getSashDom(sashLayoutClass), ...rightDom, - ...buttonsDom, ...scrollBarDom, ] } @@ -134,10 +129,11 @@ export const getDiffEditorVirtualDom = (state: DiffViewState): readonly VirtualD }) const rightDom = renderModeRight === 'image' && !errorRightMessage - ? getImageRightDom(uriRight, imageSrcRight) + ? getImageRightDom(uriRight, imageSrcRight, diffMode, showWhitespace) : getContentRightDom({ allowedLinkSchemes, contentRight, + diffMode, editable: true, errorCodeFrame: errorRightCodeFrame, errorMessage: errorRightMessage, @@ -148,14 +144,14 @@ export const getDiffEditorVirtualDom = (state: DiffViewState): readonly VirtualD lineNumbers: showLineNumbers, maxLineY, minLineY, + showWhitespace, tokenizedLines: tokenizedLinesRight, totalLineCount: totalLineCountRight, visibleLines: visibleLinesRight, }) const scrollBarDom = scrollBarActive ? getScrollBarDom() : [] - const buttonsDom = getDiffEditorButtonsDom(diffMode, showWhitespace) if (searchVisible) { - return getDiffEditorWithSearchDom(diffEditorLayoutClass, sashLayoutClass, leftDom, rightDom, buttonsDom, scrollBarDom, scrollBarActive) + return getDiffEditorWithSearchDom(diffEditorLayoutClass, sashLayoutClass, leftDom, rightDom, scrollBarDom, scrollBarActive) } - return getDiffEditorWithoutSearchDom(diffEditorLayoutClass, sashLayoutClass, leftDom, rightDom, buttonsDom, scrollBarDom, scrollBarActive) + return getDiffEditorWithoutSearchDom(diffEditorLayoutClass, sashLayoutClass, leftDom, rightDom, scrollBarDom, scrollBarActive) } diff --git a/packages/diff-view/src/parts/GetImageRightDom/GetImageRightDom.ts b/packages/diff-view/src/parts/GetImageRightDom/GetImageRightDom.ts index b7884fd1..f881bdca 100644 --- a/packages/diff-view/src/parts/GetImageRightDom/GetImageRightDom.ts +++ b/packages/diff-view/src/parts/GetImageRightDom/GetImageRightDom.ts @@ -1,12 +1,14 @@ import type { VirtualDomNode } from '@lvce-editor/virtual-dom-worker' import { VirtualDomElements } from '@lvce-editor/virtual-dom-worker' +import type { DiffMode } from '../DiffViewState/DiffViewState.ts' import * as ClassNames from '../ClassNames/ClassNames.ts' import * as DomEventListenerFunctions from '../DomEventListenerFunctions/DomEventListenerFunctions.ts' +import { getDiffEditorButtonsDom } from '../GetDiffEditorButtonsDom/GetDiffEditorButtonsDom.ts' -export const getImageRightDom = (uriRight: string, imageSrc: string): readonly VirtualDomNode[] => { +export const getImageRightDom = (uriRight: string, imageSrc: string, diffMode: DiffMode, showWhitespace: boolean): readonly VirtualDomNode[] => { return [ { - childCount: 1, + childCount: 2, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -23,5 +25,6 @@ export const getImageRightDom = (uriRight: string, imageSrc: string): readonly V src: imageSrc, type: VirtualDomElements.Img, }, + ...getDiffEditorButtonsDom(diffMode, showWhitespace), ] } diff --git a/packages/diff-view/src/parts/RenderEventListeners/RenderEventListeners.ts b/packages/diff-view/src/parts/RenderEventListeners/RenderEventListeners.ts index c83780e9..31f86a19 100644 --- a/packages/diff-view/src/parts/RenderEventListeners/RenderEventListeners.ts +++ b/packages/diff-view/src/parts/RenderEventListeners/RenderEventListeners.ts @@ -20,6 +20,7 @@ export const renderEventListeners = (): readonly DomEventListener[] => { { name: DomEventListenersFunctions.HandleClickRightSide, params: ['handleClickRightSide', EventExpression.ClientX, EventExpression.ClientY], + preventDefault: true, }, { name: DomEventListenersFunctions.HandleMouseOverAt, diff --git a/packages/diff-view/test/GetContentRightDom.test.ts b/packages/diff-view/test/GetContentRightDom.test.ts index e706710b..318902aa 100644 --- a/packages/diff-view/test/GetContentRightDom.test.ts +++ b/packages/diff-view/test/GetContentRightDom.test.ts @@ -4,6 +4,7 @@ import { defaultAllowedLinkSchemes } from '../src/parts/AllowedLinkSchemes/Allow import * as ClassNames from '../src/parts/ClassNames/ClassNames.ts' import * as DomEventListenerFunctions from '../src/parts/DomEventListenerFunctions/DomEventListenerFunctions.ts' import { getContentRightDom } from '../src/parts/GetContentRightDom/GetContentRightDom.ts' +import { getDiffEditorButtonsDom } from '../src/parts/GetDiffEditorButtonsDom/GetDiffEditorButtonsDom.ts' import * as InputName from '../src/parts/InputName/InputName.ts' import { VisibleLineType } from '../src/parts/VisibleLine/VisibleLine.ts' @@ -19,7 +20,7 @@ test.skip('getContentRightDom renders each right line inside an EditorRow', (): expect(result).toEqual([ { - childCount: 2, + childCount: 3, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -58,10 +59,11 @@ test.skip('getContentRightDom renders each right line inside an EditorRow', (): type: VirtualDomElements.Div, }, text('second-line'), + ...getDiffEditorButtonsDom('side-by-side', false), ]) }) -test.skip('getContentRightDom renders cursor for editable right content', (): void => { +test('getContentRightDom renders cursor for editable right content', (): void => { const result = getContentRightDom({ allowedLinkSchemes: defaultAllowedLinkSchemes, contentRight: 'after-content', @@ -81,7 +83,7 @@ test.skip('getContentRightDom renders cursor for editable right content', (): vo expect(result).toEqual([ { - childCount: 4, + childCount: 5, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -128,13 +130,13 @@ test.skip('getContentRightDom renders cursor for editable right content', (): vo className: ClassNames.DiffEditorInput, name: InputName.DiffEditorInput, onInput: DomEventListenerFunctions.HandleInput, - type: VirtualDomElements.Input, + type: VirtualDomElements.TextArea, value: '', }, + ...getDiffEditorButtonsDom('side-by-side', false), ]) }) - -test.skip('getContentRightDom renders load errors when available', (): void => { +test('getContentRightDom renders load errors when available', (): void => { const result = getContentRightDom({ contentRight: '', errorMessage: 'permission denied', @@ -142,7 +144,7 @@ test.skip('getContentRightDom renders load errors when available', (): void => { expect(result).toEqual([ { - childCount: 1, + childCount: 2, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight} ${ClassNames.DiffEditorError}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -153,6 +155,7 @@ test.skip('getContentRightDom renders load errors when available', (): void => { type: VirtualDomElements.Div, }, text('permission denied'), + ...getDiffEditorButtonsDom('side-by-side', false), ]) }) @@ -173,7 +176,7 @@ test.skip('getContentRightDom renders paired deletion and insertion on the same expect(result).toEqual([ { - childCount: 2, + childCount: 3, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -212,6 +215,7 @@ test.skip('getContentRightDom renders paired deletion and insertion on the same type: VirtualDomElements.Div, }, text('added-line'), + ...getDiffEditorButtonsDom('side-by-side', false), ]) }) @@ -229,7 +233,7 @@ test.skip('getContentRightDom renders syntax-highlighted token spans', (): void expect(result).toEqual([ { - childCount: 2, + childCount: 3, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -267,5 +271,6 @@ test.skip('getContentRightDom renders syntax-highlighted token spans', (): void type: VirtualDomElements.Span, }, text(' answer = 1'), + ...getDiffEditorButtonsDom('side-by-side', false), ]) }) diff --git a/packages/diff-view/test/GetDiffEditorVirtualDom.test.ts b/packages/diff-view/test/GetDiffEditorVirtualDom.test.ts index 88e8db1c..77c7e092 100644 --- a/packages/diff-view/test/GetDiffEditorVirtualDom.test.ts +++ b/packages/diff-view/test/GetDiffEditorVirtualDom.test.ts @@ -20,7 +20,7 @@ test('getDiffEditorVirtualDom renders left and right lines inside EditorRow wrap expect(result).toEqual([ { - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -74,7 +74,7 @@ test('getDiffEditorVirtualDom renders left and right lines inside EditorRow wrap type: VirtualDomElements.Div, }, { - childCount: 4, + childCount: 5, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -176,7 +176,7 @@ test('getDiffEditorVirtualDom omits line number gutters when disabled in state', expect(result).toEqual([ { - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -202,7 +202,7 @@ test('getDiffEditorVirtualDom omits line number gutters when disabled in state', type: VirtualDomElements.Div, }, { - childCount: 3, + childCount: 4, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -312,7 +312,7 @@ test('getDiffEditorVirtualDom renders search when visible', (): void => { }) expect(result[0]).toEqual({ - childCount: 3, + childCount: 2, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal} ${ClassNames.DiffEditorWithSearch}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -369,7 +369,7 @@ test('getDiffEditorVirtualDom renders image panes when render mode is image', () expect(result).toEqual([ { - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -401,7 +401,7 @@ test('getDiffEditorVirtualDom renders image panes when render mode is image', () type: VirtualDomElements.Div, }, { - childCount: 3, + childCount: 4, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -461,6 +461,54 @@ test('getDiffEditorVirtualDom renders image panes when render mode is image', () ]) }) +test('getDiffEditorVirtualDom renders buttons inside right image pane', (): void => { + const result = getDiffEditorVirtualDom({ + ...createDefaultState(), + contentLeft: 'before-content', + imageSrcRight: 'blob:after.png', + maxLineY: 1, + renderModeRight: 'image', + showWhitespace: true, + totalLineCount: 1, + uriLeft: '/tmp/before.txt', + uriRight: '/tmp/after.png', + }) + + const rightContentIndex = result.findIndex((node) => node.className === `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`) + + expect(result[rightContentIndex]).toEqual({ + childCount: 2, + className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight}`, + onClick: DomEventListenerFunctions.HandleClickRightSide, + type: VirtualDomElements.Div, + }) + expect(result.slice(rightContentIndex + 3, rightContentIndex + 8)).toEqual([ + { + childCount: 2, + className: ClassNames.DiffEditorButtons, + type: VirtualDomElements.Div, + }, + { + childCount: 1, + className: `${ClassNames.DiffEditorWhitespaceToggle} ${ClassNames.DiffEditorWhitespaceToggleActive}`, + name: ActionName.ToggleWhitespace, + onClick: DomEventListenerFunctions.HandleClickAction, + title: 'Hide whitespace', + type: VirtualDomElements.Button, + }, + text('Hide whitespace'), + { + childCount: 1, + className: ClassNames.DiffEditorModeToggle, + name: ActionName.ToggleDiffMode, + onClick: DomEventListenerFunctions.HandleClickAction, + title: 'Switch to Inline diff', + type: VirtualDomElements.Button, + }, + text('Inline'), + ]) +}) + test('getDiffEditorVirtualDom only renders existing gutter numbers for an empty left pane', (): void => { const result = getDiffEditorVirtualDom({ ...createDefaultState(), @@ -476,7 +524,7 @@ test('getDiffEditorVirtualDom only renders existing gutter numbers for an empty expect(result.slice(0, 9)).toEqual([ { - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -535,7 +583,7 @@ test('getDiffEditorVirtualDom renders pane errors without crashing', (): void => expect(result).toEqual([ { - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorHorizontal}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, @@ -577,7 +625,7 @@ test('getDiffEditorVirtualDom renders pane errors without crashing', (): void => type: VirtualDomElements.Div, }, { - childCount: 2, + childCount: 3, className: `${ClassNames.DiffEditorContent} ${ClassNames.DiffEditorContentRight} ${ClassNames.DiffEditorError}`, onClick: DomEventListenerFunctions.HandleClickRightSide, type: VirtualDomElements.Div, @@ -749,7 +797,7 @@ test('getDiffEditorVirtualDom renders a horizontal sash for vertical layout', () }) expect(result[0]).toEqual({ - childCount: 4, + childCount: 3, className: `${ClassNames.Viewlet} ${ClassNames.DiffEditor} ${ClassNames.DiffEditorVertical}`, onContextMenu: DomEventListenerFunctions.HandleContextMenu, onWheel: DomEventListenerFunctions.HandleWheel, diff --git a/packages/diff-view/test/RenderEventListeners.test.ts b/packages/diff-view/test/RenderEventListeners.test.ts index 332abb44..760551b3 100644 --- a/packages/diff-view/test/RenderEventListeners.test.ts +++ b/packages/diff-view/test/RenderEventListeners.test.ts @@ -48,5 +48,6 @@ test('renderEventListeners registers tracked sash pointer listeners', (): void = expect(result).toContainEqual({ name: DomEventListenerFunctions.HandleClickRightSide, params: ['handleClickRightSide', EventExpression.ClientX, EventExpression.ClientY], + preventDefault: true, }) })