244 lines
7.6 KiB
JavaScript
244 lines
7.6 KiB
JavaScript
import { nextTick } from 'vue'
|
|
import {
|
|
createPreviewAppendParagraphValue,
|
|
createPreviewDeleteLineValue,
|
|
createPreviewExtractGalleryImageValue,
|
|
createPreviewGalleryReorderValue,
|
|
createPreviewImagesMergeValue,
|
|
createPreviewInsertAfterLineResult,
|
|
createPreviewInsertImageToGalleryValue,
|
|
createPreviewMergeWithPreviousLineValue,
|
|
createPreviewRemoveGalleryImageResult
|
|
} from '../lib/markdown-editor-preview-blocks.js'
|
|
import { shouldIgnorePendingMediaSlashCommit } from '../lib/markdown-slash-commands.js'
|
|
|
|
/**
|
|
* 관리자 Markdown 라이브 미리보기 이벤트 적용 함수를 만든다.
|
|
* @param {Object} options - 미리보기 액션 옵션
|
|
* @param {import('vue').Ref<string>} options.value - Markdown 본문 ref
|
|
* @param {import('vue').Ref<number>} options.activeLogicalLineIndex - 활성 논리 줄 ref
|
|
* @param {import('vue').Ref<number|null>} options.liveSlashInsertLine - 슬래시 미디어 삽입 대기 줄 ref
|
|
* @param {(startLine: number, endLine: number, replacementLines: string[], focusEditor?: boolean) => void} options.replaceLineRange - 줄 범위 교체 함수
|
|
* @param {(startLine: number, fallbackEndLine: number) => number} options.resolveCurrentFencedBlockEndLine - 현재 fenced 블록 끝 줄 계산 함수
|
|
* @param {() => void} options.resetBlockPanelContext - 블록 패널 컨텍스트 초기화 함수
|
|
* @param {(line: number, column?: number, align?: string, offset?: number, block?: string) => void} options.focusPreviewEditableAtLine - 미리보기 줄 포커스 함수
|
|
* @returns {Object} 미리보기 이벤트 적용 함수
|
|
*/
|
|
export const useAdminMarkdownPreviewActions = (options) => {
|
|
/**
|
|
* 계산된 Markdown 값을 현재 본문에 반영한다.
|
|
* @param {string|null} nextValue - 다음 Markdown 값
|
|
* @returns {boolean} 적용 여부
|
|
*/
|
|
const applyNextValue = (nextValue) => {
|
|
if (nextValue === null) {
|
|
return false
|
|
}
|
|
|
|
options.value.value = nextValue
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* 미리보기에서 갤러리 순서 변경을 반영한다.
|
|
* @param {{ startLine: number, endLine: number, images: Array<Object> }} payload - 갤러리 줄 범위·이미지 목록
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewGalleryReorder = ({ startLine, endLine, images }) => {
|
|
applyNextValue(createPreviewGalleryReorderValue({
|
|
value: options.value.value,
|
|
startLine,
|
|
endLine,
|
|
images
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 두 단일 이미지 블록을 갤러리로 병합한다.
|
|
* @param {{ sourceLine: number, targetLine: number }} payload - 원본·대상 줄
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewMergeImagesToGallery = ({ sourceLine, targetLine }) => {
|
|
applyNextValue(createPreviewImagesMergeValue({
|
|
value: options.value.value,
|
|
sourceLine,
|
|
targetLine
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 단일 이미지 블록을 기존 갤러리에 추가한다.
|
|
* @param {{ sourceLine: number, startLine: number, endLine: number, targetIndex: number, image?: Object }} payload - 이미지 줄·갤러리 범위·삽입 위치
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewInsertImageToGallery = ({
|
|
sourceLine,
|
|
startLine,
|
|
endLine,
|
|
targetIndex,
|
|
image
|
|
}) => {
|
|
applyNextValue(createPreviewInsertImageToGalleryValue({
|
|
value: options.value.value,
|
|
sourceLine,
|
|
startLine,
|
|
endLine,
|
|
targetIndex,
|
|
image
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드 갤러리에서 특정 이미지를 삭제한다.
|
|
* @param {{ startLine: number, endLine: number, imageIndex: number }} payload - 갤러리 범위·이미지 인덱스
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewRemoveGalleryImage = ({ startLine, endLine, imageIndex }) => {
|
|
const result = createPreviewRemoveGalleryImageResult({
|
|
value: options.value.value,
|
|
startLine,
|
|
endLine,
|
|
imageIndex
|
|
})
|
|
|
|
if (!result) {
|
|
return
|
|
}
|
|
|
|
options.value.value = result.value
|
|
options.activeLogicalLineIndex.value = result.focusLine
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 갤러리 이미지를 블록 사이에 단일 이미지 줄로 분리한다.
|
|
* @param {{ startLine: number, endLine: number, imageIndex: number, insertBeforeLine: number, image?: Object }} payload - 갤러리 범위·삽입 위치
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewExtractGalleryImage = ({
|
|
startLine,
|
|
endLine,
|
|
imageIndex,
|
|
insertBeforeLine,
|
|
image
|
|
}) => {
|
|
applyNextValue(createPreviewExtractGalleryImageValue({
|
|
value: options.value.value,
|
|
startLine,
|
|
endLine,
|
|
imageIndex,
|
|
insertBeforeLine,
|
|
image
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* 미리보기 인라인 편집 결과를 마크다운 본문에 반영한다.
|
|
* @param {{ startLine: number, endLine: number, replacementLines: string[] }} payload - 줄 범위·대체 줄
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewBlockContentChange = ({ startLine, endLine, replacementLines }) => {
|
|
if (typeof startLine !== 'number' || typeof endLine !== 'number' || !Array.isArray(replacementLines)) {
|
|
return
|
|
}
|
|
|
|
if (shouldIgnorePendingMediaSlashCommit({
|
|
pendingInsertLine: options.liveSlashInsertLine.value,
|
|
startLine,
|
|
replacementLines
|
|
})) {
|
|
return
|
|
}
|
|
|
|
options.replaceLineRange(
|
|
startLine,
|
|
options.resolveCurrentFencedBlockEndLine(startLine, endLine),
|
|
replacementLines,
|
|
false
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드 교차 선택 삭제 결과를 본문에 반영한다.
|
|
* @param {{ value: string }} payload - 갱신된 마크다운
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewContentReplace = ({ value }) => {
|
|
if (typeof value === 'string') {
|
|
options.value.value = value
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드 하단에서 새 문단 줄을 추가한다.
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewAppendParagraph = () => {
|
|
options.value.value = createPreviewAppendParagraphValue(options.value.value)
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 지정 줄 아래에 마크다운 줄을 삽입한다.
|
|
* @param {{ afterLine: number, lines: string[], focusLine?: number }} payload - 삽입 위치·줄
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewInsertAfterLine = ({ afterLine, lines, focusLine }) => {
|
|
const result = createPreviewInsertAfterLineResult({
|
|
value: options.value.value,
|
|
afterLine,
|
|
lines,
|
|
focusLine
|
|
})
|
|
|
|
if (!result) {
|
|
return
|
|
}
|
|
|
|
options.value.value = result.value
|
|
options.activeLogicalLineIndex.value = result.focusLine
|
|
options.resetBlockPanelContext()
|
|
|
|
nextTick(() => {
|
|
options.focusPreviewEditableAtLine(result.focusLine, 0, 'start')
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 현재 줄을 삭제한다.
|
|
* @param {number} lineIndex - 줄 번호
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewDeleteLine = (lineIndex) => {
|
|
applyNextValue(createPreviewDeleteLineValue({
|
|
value: options.value.value,
|
|
lineIndex
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* 라이브 모드에서 현재 줄을 이전 줄 끝에 병합한다.
|
|
* @param {{ lineIndex: number, mergedLine: string }} payload - 병합 위치·결과 줄
|
|
* @returns {void}
|
|
*/
|
|
const onPreviewMergeWithPreviousLine = ({ lineIndex, mergedLine }) => {
|
|
applyNextValue(createPreviewMergeWithPreviousLineValue({
|
|
value: options.value.value,
|
|
lineIndex,
|
|
mergedLine
|
|
}))
|
|
}
|
|
|
|
return {
|
|
onPreviewAppendParagraph,
|
|
onPreviewBlockContentChange,
|
|
onPreviewContentReplace,
|
|
onPreviewDeleteLine,
|
|
onPreviewExtractGalleryImage,
|
|
onPreviewGalleryReorder,
|
|
onPreviewInsertAfterLine,
|
|
onPreviewInsertImageToGallery,
|
|
onPreviewMergeImagesToGallery,
|
|
onPreviewMergeWithPreviousLine,
|
|
onPreviewRemoveGalleryImage
|
|
}
|
|
}
|