From 96c68071aea13d2d05738d12ec30d21d0d1810a1 Mon Sep 17 00:00:00 2001 From: GreatZP Date: Wed, 11 Dec 2024 11:35:03 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/composables/use-editor-md.ts | 4 +- .../devui/editor-md/src/toolbar-config.ts | 76 ++++++++++--------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/packages/devui-vue/devui/editor-md/src/composables/use-editor-md.ts b/packages/devui-vue/devui/editor-md/src/composables/use-editor-md.ts index a6655aaf46..d3ae10bb75 100644 --- a/packages/devui-vue/devui/editor-md/src/composables/use-editor-md.ts +++ b/packages/devui-vue/devui/editor-md/src/composables/use-editor-md.ts @@ -2,7 +2,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { computed, nextTick, onMounted, reactive, Ref, ref, SetupContext, toRefs, watch, onBeforeUnmount } from 'vue'; import { debounce } from '../../../shared/utils'; import { EditorMdProps, Mode } from '../editor-md-types'; -import { ALT_KEY, DEFAULT_TOOLBARS } from '../toolbar-config'; +import { DEFAULT_TOOLBARS, GET_ALT_KEY } from '../toolbar-config'; import { parseHTMLStringToDomList } from '../utils'; import { refreshEditorCursor, _enforceMaxLength } from './helper'; import { throttle } from 'lodash'; @@ -326,7 +326,7 @@ export function useEditorMd(props: EditorMdProps, ctx: SetupContext) { keyCombination += '⌘-'; } if (e.altKey) { - keyCombination += `${ALT_KEY}-`; + keyCombination += `${GET_ALT_KEY()}-`; } if (e.shiftKey) { keyCombination += 'Shift-'; diff --git a/packages/devui-vue/devui/editor-md/src/toolbar-config.ts b/packages/devui-vue/devui/editor-md/src/toolbar-config.ts index bc4b0c665c..db6d152781 100644 --- a/packages/devui-vue/devui/editor-md/src/toolbar-config.ts +++ b/packages/devui-vue/devui/editor-md/src/toolbar-config.ts @@ -278,8 +278,16 @@ class ToolBarHandler { static color = (): void => {}; } -export const CTRL_KEY = navigator?.platform?.indexOf('Mac') !== -1 ? '⌘' : 'Ctrl'; -export const ALT_KEY = navigator?.platform?.indexOf('Mac') !== -1 ? '⌥' : 'Alt'; +export const GET_CTRL_KEY = () => { + if (typeof window !== 'undefined') { + return navigator?.platform?.indexOf('Mac') !== -1 ? '⌘' : 'Ctrl'; + } +} +export const GET_ALT_KEY = () => { + if (typeof window !== 'undefined') { + return navigator?.platform?.indexOf('Mac') !== -1 ? '⌥' : 'Alt'; + } +} export const DEFAULT_TOOLBARS: Record = { undo: { @@ -287,8 +295,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'undo', type: 'button', icon: UNDO_ICON, - shortKey: `${CTRL_KEY}+Z`, - shortKeyWithCode: `${CTRL_KEY}+90`, + shortKey: `${GET_CTRL_KEY()}+Z`, + shortKeyWithCode: `${GET_CTRL_KEY()}+90`, handler: ToolBarHandler.undo, }, redo: { @@ -296,8 +304,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'redo', type: 'button', icon: REDO_ICON, - shortKey: `${CTRL_KEY}+Y`, - shortKeyWithCode: `${CTRL_KEY}+89`, + shortKey: `${GET_CTRL_KEY()}+Y`, + shortKeyWithCode: `${GET_CTRL_KEY()}+89`, handler: ToolBarHandler.redo, }, bold: { @@ -305,8 +313,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'bold', type: 'button', icon: BOLD_ICON, - shortKey: `${CTRL_KEY}+B`, - shortKeyWithCode: `${CTRL_KEY}+66`, + shortKey: `${GET_CTRL_KEY()}+B`, + shortKeyWithCode: `${GET_CTRL_KEY()}+66`, handler: ToolBarHandler.bold, }, italic: { @@ -314,8 +322,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'italic', type: 'button', icon: ITALIC_ICON, - shortKey: `${CTRL_KEY}+I`, - shortKeyWithCode: `${CTRL_KEY}+73`, + shortKey: `${GET_CTRL_KEY()}+I`, + shortKeyWithCode: `${GET_CTRL_KEY()}+73`, handler: ToolBarHandler.italic, }, strike: { @@ -323,8 +331,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'strike', type: 'button', icon: STRIKE_ICON, - shortKey: `${CTRL_KEY}+D`, - shortKeyWithCode: `${CTRL_KEY}+68`, + shortKey: `${GET_CTRL_KEY()}+D`, + shortKeyWithCode: `${GET_CTRL_KEY()}+68`, handler: ToolBarHandler.strike, }, h1: { @@ -332,8 +340,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'h1', type: 'button', icon: H1_ICON, - shortKey: `${CTRL_KEY}+1`, - shortKeyWithCode: `${CTRL_KEY}+49`, + shortKey: `${GET_CTRL_KEY()}+1`, + shortKeyWithCode: `${GET_CTRL_KEY()}+49`, handler: ToolBarHandler.h1, }, h2: { @@ -341,8 +349,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'h2', type: 'button', icon: H2_ICON, - shortKey: `${CTRL_KEY}+2`, - shortKeyWithCode: `${CTRL_KEY}+50`, + shortKey: `${GET_CTRL_KEY()}+2`, + shortKeyWithCode: `${GET_CTRL_KEY()}+50`, handler: ToolBarHandler.h2, }, ul: { @@ -350,8 +358,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'unorderedlist', type: 'button', icon: LIST_UNORDERED_ICON, - shortKey: `${CTRL_KEY}+U`, - shortKeyWithCode: `${CTRL_KEY}+85`, + shortKey: `${GET_CTRL_KEY()}+U`, + shortKeyWithCode: `${GET_CTRL_KEY()}+85`, handler: ToolBarHandler.ul, }, ol: { @@ -359,8 +367,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'orderedlist', type: 'button', icon: LIST_ORDERED_ICON, - shortKey: `${CTRL_KEY}+O`, - shortKeyWithCode: `${CTRL_KEY}+79`, + shortKey: `${GET_CTRL_KEY()}+O`, + shortKeyWithCode: `${GET_CTRL_KEY()}+79`, handler: ToolBarHandler.ol, }, checklist: { @@ -368,8 +376,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'checklist', type: 'button', icon: LIST_CHECK_ICON, - shortKey: `${CTRL_KEY}+${ALT_KEY}+C`, - shortKeyWithCode: `${CTRL_KEY}+${ALT_KEY}+67`, + shortKey: `${GET_CTRL_KEY()}+${GET_ALT_KEY()}+C`, + shortKeyWithCode: `${GET_CTRL_KEY()}+${GET_ALT_KEY()}+67`, handler: ToolBarHandler.checkList, }, underline: { @@ -377,8 +385,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'underline', type: 'button', icon: UNDERLINE_ICON, - shortKey: `${CTRL_KEY}+R`, - shortKeyWithCode: `${CTRL_KEY}+82`, + shortKey: `${GET_CTRL_KEY()}+R`, + shortKeyWithCode: `${GET_CTRL_KEY()}+82`, handler: ToolBarHandler.underline, }, font: { @@ -394,8 +402,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'link', type: 'button', icon: LINK_ICON, - shortKey: `${CTRL_KEY}+L`, - shortKeyWithCode: `${CTRL_KEY}+76`, + shortKey: `${GET_CTRL_KEY()}+L`, + shortKeyWithCode: `${GET_CTRL_KEY()}+76`, handler: ToolBarHandler.link, }, image: { @@ -403,8 +411,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'image', type: 'button', icon: IMAGE_ICON, - shortKey: `${CTRL_KEY}+G`, - shortKeyWithCode: `${CTRL_KEY}+71`, + shortKey: `${GET_CTRL_KEY()}+G`, + shortKeyWithCode: `${GET_CTRL_KEY()}+71`, params: { imageUploadToServer: false }, handler: ToolBarHandler.image, }, @@ -414,8 +422,8 @@ export const DEFAULT_TOOLBARS: Record = { type: 'button', icon: FILE_ICON, params: {}, - shortKey: `${CTRL_KEY}+F`, - shortKeyWithCode: `${CTRL_KEY}+70`, + shortKey: `${GET_CTRL_KEY()}+F`, + shortKeyWithCode: `${GET_CTRL_KEY()}+70`, handler: ToolBarHandler.file, }, code: { @@ -423,8 +431,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'code', type: 'button', icon: CODE_ICON, - shortKey: `${CTRL_KEY}+K`, - shortKeyWithCode: `${CTRL_KEY}+75`, + shortKey: `${GET_CTRL_KEY()}+K`, + shortKeyWithCode: `${GET_CTRL_KEY()}+75`, handler: ToolBarHandler.code, }, table: { @@ -432,8 +440,8 @@ export const DEFAULT_TOOLBARS: Record = { name: 'table', type: 'button', icon: TABLE_ICON, - shortKey: `${CTRL_KEY}+${ALT_KEY}+T`, - shortKeyWithCode: `${CTRL_KEY}+${ALT_KEY}+84`, + shortKey: `${GET_CTRL_KEY()}+${GET_ALT_KEY()}+T`, + shortKeyWithCode: `${GET_CTRL_KEY()}+${GET_ALT_KEY()}+84`, handler: ToolBarHandler.table, }, fullscreen: { From 680410aced8128f977d0404f7896dde4e54dff01 Mon Sep 17 00:00:00 2001 From: GreatZP Date: Fri, 3 Jan 2025 14:56:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(md):=20a=E6=A0=87=E7=AD=BE=E5=8F=AF?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=9B=B4=E5=A4=9A=E5=8D=8F=E8=AE=AE=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor-md/src/composables/md-render-service.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/devui-vue/devui/editor-md/src/composables/md-render-service.ts b/packages/devui-vue/devui/editor-md/src/composables/md-render-service.ts index e3e3a688da..cf1dedbe11 100644 --- a/packages/devui-vue/devui/editor-md/src/composables/md-render-service.ts +++ b/packages/devui-vue/devui/editor-md/src/composables/md-render-service.ts @@ -102,7 +102,14 @@ export class MDRenderService { } private onIgnoreTagAttr(tag: string, name: string, value: string, isWhiteAttr: boolean) { - if (!isWhiteAttr && (name === 'id' || (tag === 'span' && name === 'style'))) { + if (!isWhiteAttr && (name === 'id' || (tag === 'span' && name === 'style') + || (tag === 'a' && name === 'href'))) { + return name + '=' + value; + } + } + + private onTagAttr(tag: string, name: string, value: string, isWhiteAttr: boolean) { + if (isWhiteAttr && (tag === 'a' && name === 'href')) { return name + '=' + value; } } @@ -139,6 +146,7 @@ export class MDRenderService { html = filterXSS(html, { whiteList: this.xssWhiteList, onIgnoreTagAttr: this.onIgnoreTagAttr, + onTagAttr: this.onTagAttr, css: { whiteList: Object.assign({}, this.cssWhiteList, { top: true, From fe6af0ffaebf0fe8c506f4c7932adaf11b6df3f1 Mon Sep 17 00:00:00 2001 From: GreatZP Date: Fri, 3 Jan 2025 14:59:42 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(md):=20a=E6=A0=87=E7=AD=BE=E5=8F=AF?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=9B=B4=E5=A4=9A=E5=8D=8F=E8=AE=AE=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/devui-vue/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devui-vue/package.json b/packages/devui-vue/package.json index 6fbbb04538..7c8f8ed4cb 100644 --- a/packages/devui-vue/package.json +++ b/packages/devui-vue/package.json @@ -1,6 +1,6 @@ { "name": "vue-devui", - "version": "1.6.29", + "version": "1.6.30", "license": "MIT", "description": "DevUI components based on Vite and Vue3", "keywords": [ From 5563114b5752cfc2d795e50f14dad82e5bc64307 Mon Sep 17 00:00:00 2001 From: GreatZP Date: Thu, 23 Jan 2025 15:19:09 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix(code-review):=20=E5=8F=8C=E6=A0=8F?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=94=AF=E6=8C=81=E5=8D=8A=E8=BE=B9?= =?UTF-8?q?=E9=80=89=E4=B8=AD=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devui/code-review/src/code-review.scss | 12 ++++ .../devui/code-review/src/code-review.tsx | 5 +- .../src/composables/use-code-review.ts | 71 ++++++++++++++++++- .../devui-vue/devui/code-review/src/utils.ts | 18 +++++ packages/devui-vue/package.json | 2 +- 5 files changed, 102 insertions(+), 6 deletions(-) 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": [