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 { getSelectionParent, parseDiffCode } from '../utils';
export function useCodeReview(
props: CodeReviewProps,
ctx: SetupContext,
reviewContentRef: Ref<HTMLElement>,
updateLineNumberMap: (expandLineNumberInfo: IExpandLineNumberInfo, newCode: string, direction: 'up' | 'down') => void,
updateCheckedLine: (expandLineNumberInfo: IExpandLineNumberInfo, direction: 'up' | 'down') => void
) {
const { diff, outputFormat, allowExpand, showBlob } = toRefs(props);
const renderHtml = ref('');
const diffFile: Ref<DiffFile[]> = ref([]);
const ns = useNamespace('code-review');
const selectionSide = ref('');
const { insertExpandButton, onExpandButtonClick } = useCodeReviewExpand(reviewContentRef, props, updateLineNumberMap, updateCheckedLine);
const initDiffContent = () => {
diffFile.value = Diff2Html.parse(diff.value);
nextTick(() => {
if (inBrowser && !showBlob.value) {
parseDiffCode(reviewContentRef.value, diff.value, outputFormat.value, props.options);
allowExpand.value && insertExpandButton();
ctx.emit('contentRefresh', JSON.parse(JSON.stringify(diffFile.value)));
}
});
};
const onContentClick = (e: Event) => {
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.removeRange(selection.getRangeAt(0));
}
} else {
selectionSide.value = '';
}
}
watch(showBlob, initDiffContent);
watch(outputFormat, initDiffContent);
watch(diff, initDiffContent, { immediate: true });
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 };
}