diff --git a/packages/devui-vue/devui/code-review/src/code-review.scss b/packages/devui-vue/devui/code-review/src/code-review.scss index ef00494816..771f383b3a 100644 --- a/packages/devui-vue/devui/code-review/src/code-review.scss +++ b/packages/devui-vue/devui/code-review/src/code-review.scss @@ -304,4 +304,16 @@ box-shadow: 0 0 1px 1px rgba(37, 43, 58, 0.16); cursor: pointer; } + + &--left-selected { + .d-code-right span { + user-select: none; + } + } + + &--right-selected { + .d-code-left span { + user-select: none; + } + } } diff --git a/packages/devui-vue/devui/code-review/src/code-review.tsx b/packages/devui-vue/devui/code-review/src/code-review.tsx index e6beb4678d..73ab7c5795 100644 --- a/packages/devui-vue/devui/code-review/src/code-review.tsx +++ b/packages/devui-vue/devui/code-review/src/code-review.tsx @@ -31,7 +31,8 @@ export default defineComponent({ updateLineNumberMap, updateCheckedLine, } = useCodeReviewComment(reviewContentRef, props, ctx); - const { renderHtml, diffFile, onContentClick } = useCodeReview(props, ctx, reviewContentRef, updateLineNumberMap, updateCheckedLine); + const { renderHtml, diffFile, selectionSide, onContentClick } = + useCodeReview(props, ctx, reviewContentRef, updateLineNumberMap, updateCheckedLine); const { isFold, toggleFold } = useCodeReviewFold(props, ctx); onMounted(() => { @@ -51,7 +52,7 @@ export default defineComponent({ }); return () => ( -
+
(isFold.value = !isFold.value)} />
{props.showBlob ? ( diff --git a/packages/devui-vue/devui/code-review/src/composables/use-code-review.ts b/packages/devui-vue/devui/code-review/src/composables/use-code-review.ts index 5bebeac5ef..dd0b8367f3 100644 --- a/packages/devui-vue/devui/code-review/src/composables/use-code-review.ts +++ b/packages/devui-vue/devui/code-review/src/composables/use-code-review.ts @@ -1,11 +1,12 @@ -import { toRefs, ref, watch, nextTick } from 'vue'; +import { toRefs, ref, watch, nextTick, onUnmounted } from 'vue'; import type { SetupContext, Ref } from 'vue'; import type { DiffFile } from 'diff2html/lib/types'; import * as Diff2Html from 'diff2html'; +import { useNamespace } from '../../../shared/hooks/use-namespace'; import { inBrowser } from '../../../shared/utils/common-var'; import type { CodeReviewProps, IExpandLineNumberInfo } from '../code-review-types'; import { useCodeReviewExpand } from './use-code-review-expand'; -import { parseDiffCode } from '../utils'; +import { getSelectionParent, parseDiffCode } from '../utils'; export function useCodeReview( props: CodeReviewProps, @@ -17,6 +18,8 @@ export function useCodeReview( const { diff, outputFormat, allowExpand, showBlob } = toRefs(props); const renderHtml = ref(''); const diffFile: Ref = ref([]); + const ns = useNamespace('code-review'); + const selectionSide = ref(''); const { insertExpandButton, onExpandButtonClick } = useCodeReviewExpand(reviewContentRef, props, updateLineNumberMap, updateCheckedLine); const initDiffContent = () => { @@ -34,11 +37,73 @@ export function useCodeReview( onExpandButtonClick(e, props.options); }; + function onSelectionChange() { + if (selectionSide.value) { + return; + } + if (typeof window === 'undefined') { + return; + } + const selection = window.getSelection(); + if (selection?.toString() && selection?.anchorNode) { + const side = getSelectionParent(selection.anchorNode as HTMLElement); + if (side) { + selectionSide.value = side; + } + } + } + function onMousedown(e: Event) { + if (typeof window === 'undefined') { + return; + } + const selection = window.getSelection(); + const composedPath = e.composedPath(); + const isLineNumber = composedPath.some((item: HTMLElement) => item.classList?.contains('d2h-code-side-linenumber')); + const isClickInner = composedPath.some((item: HTMLElement) => item.classList?.contains(ns.e('content'))); + const clickSide = getSelectionParent(e.target as HTMLElement); + if (selection && selection.toString()) { + const isInRange = selection?.getRangeAt(0).intersectsNode(e.target); + if ( + !isInRange || + !isClickInner || + (clickSide === 'left' && selectionSide.value === 'right') || + (clickSide === 'right' && selectionSide.value === 'left') || + isLineNumber + ) { + setTimeout(() => { + selectionSide.value = ''; + selection.removeAllRanges(); + }); + } + } else { + selectionSide.value = ''; + } + } + watch(showBlob, initDiffContent); watch(outputFormat, initDiffContent); watch(diff, initDiffContent, { immediate: true }); - return { renderHtml, diffFile, onContentClick }; + watch( + () => props.outputFormat, + (val) => { + if (val === 'side-by-side') { + document.addEventListener('selectionchange', onSelectionChange); + document.addEventListener('mousedown', onMousedown, true); + } else { + document.removeEventListener('selectionchange', onSelectionChange); + document.removeEventListener('mousedown', onMousedown, true); + } + }, + { immediate: true } + ); + + onUnmounted(() => { + document.removeEventListener('selectionchange', onSelectionChange); + document.removeEventListener('mousedown', onMousedown, true); + }); + + return { renderHtml, diffFile, selectionSide, onContentClick }; } diff --git a/packages/devui-vue/devui/code-review/src/utils.ts b/packages/devui-vue/devui/code-review/src/utils.ts index d8408bf387..fa838bee4e 100644 --- a/packages/devui-vue/devui/code-review/src/utils.ts +++ b/packages/devui-vue/devui/code-review/src/utils.ts @@ -721,3 +721,21 @@ export function addCommentCheckedForSingle( return result; } + +/* 双栏模式,选中文本时,根据选择的节点查找其父节点,用于判断左侧选中还是右侧选中 */ +export function getSelectionParent(el: HTMLElement) { + if (el.tagName === 'TR') { + return; + } + if (el.tagName === 'TD' && (el.classList.contains('d-code-left') || el.classList.contains('d-code-right'))) { + if (el.classList.contains('d-code-left')) { + return 'left'; + } + if (el.classList.contains('d-code-right')) { + return 'right'; + } + } + if (el.parentElement) { + return getSelectionParent(el.parentElement); + } +} diff --git a/packages/devui-vue/package.json b/packages/devui-vue/package.json index 7c8f8ed4cb..9bfc2d75a3 100644 --- a/packages/devui-vue/package.json +++ b/packages/devui-vue/package.json @@ -1,6 +1,6 @@ { "name": "vue-devui", - "version": "1.6.30", + "version": "1.6.31", "license": "MIT", "description": "DevUI components based on Vite and Vue3", "keywords": [